【问题标题】:Grouping a list of objects into a list of lists by a boolean function using Java 8 streams使用 Java 8 流通过布尔函数将对象列表分组为列表列表
【发布时间】:2016-12-09 07:23:09
【问题描述】:

假设我有一个对象列表。我想将它们分组到一个列表列表中,其中每个内部列表都包含布尔比较函数为其返回 true 的元素:

public class VO {
    public VO(int age, int val) {
        this.age = age;
        this.val = val;
    }

    public int age;
    public int val;
}

public void testGrouping() {
    // Equal to vo2
    VO vo1 = new VO(1, 100);
    // Equal to vo1 and vo3
    VO vo2 = new VO(3, 105);
    // Equal to vo2 but not vo1 (age difference > 2),
    // so it belongs into a new bucket
    VO vo3 = new VO(5, 110);
    // Equal to vo3, so it belongs into the same bucket as vo3
    VO vo4 = new VO(7, 116);

    List<VO> values = Arrays.asList(vo1, vo2, vo3, vo4);

    //Group values using isEqual(VO, VO) into buckets somehow
    //Any two values in a bucket must pass the check

    //Expected without any specific order:
    //[[vo1, vo2, [vo3, vo4]]
}

private boolean isEqual(VO a, VO b) {
    return Math.abs(a.age - b.age) <= 2 && Math.abs(a.val - b.val) <= 10;
}

这只是我拥有的数据的一个简化示例,实际上比较方法比这更复杂。重要的是,每个对象必须在其存储桶中与检查相关的其他对象匹配。对象不能按特定值分组/映射。

我已经有了执行此操作的代码,但需要三个级别的 for 循环,并且花了我大约一天的时间来编写。我很好奇这是否可以通过流更轻松地实现。

【问题讨论】:

  • 这是一个无法解决的任务,因为标准不能保证产生不同的组。您可以拥有三个元素,abc,其中ab 之间的距离小到足以属于一个组,bc 相同,而ac 距离不够近。然后呢?
  • 我目前遍历首先按大小排序的列表。我正在寻找一种“足够好”的方法,因此拥有 [a,b] 和 [c] 与拥有 [a] 和 [b,c] 一样有效。
  • Stream API 不是这样工作的。只要保持你的循环......

标签: java list java-8 java-stream


【解决方案1】:

先尝试映射您的项目,然后使用Collectors.groupingBy()

values.stream().map(name -> name.split("\\s+")).collect(groupingBy(a -> a[1]));

这不会返回列表而是数组 - 但概念是相同的。

【讨论】:

  • 不幸的是,正如我所写,比较方法比这更复杂。不可能通过简单的方法映射值。它们具有不同的数值,可以进行比较,并且不能超过一定的差值。
  • 我认为您不能使用 Stream API 编写单行程序。试一试:首先,使用 Comparator 对列表进行排序,仅当满足分组条件时才返回 0。结果应该是一个列表,其中一组中的所有项目都是连续的。其次,使用 for 循环遍历列表并使用相同的 Comparator 检测组边缘(每次比较前一个元素时开始一个新组,当前元素返回 0)。不是你想要的,但可能更具可读性。
【解决方案2】:

您可以稍微修改isEqual 方法,以便将其用作TreeMap 的比较器。然后您可以按如下方式收集TreeMap&lt;VO, List&lt;VO&gt;&gt;。此代码适用于 Java 7

public static int isEqual(VO a, VO b) {
    if (Math.abs(a.age - b.age) > 2 || Math.abs(a.val - b.val) > 10)
        return 1;
    else if (Math.abs(a.age - b.age) <= 2 && Math.abs(a.val - b.val) <= 10)
        return 0;
    else
        return -1;
}
public static void main(String[] args) {
    VO vo1 = new VO(1, 100);
    VO vo2 = new VO(3, 105);
    VO vo3 = new VO(5, 110);
    VO vo4 = new VO(7, 116);

    List<VO> values = Arrays.asList(vo1, vo2, vo3, vo4);

    Map<VO, List<VO>> buckets = new TreeMap<>(new Comparator<VO>() {
        @Override
        public int compare(VO o1, VO o2) {
            return isEqual(o1, o2);
        }
    });

    for (VO vo : values) {
        List<VO> list = buckets.get(vo);
        if (list == null) {
            list = new ArrayList<>();
        }
        list.add(vo);
        buckets.put(vo, list);
    }

    // output
    System.out.println(buckets);
    //{VO{1=100}=[VO{1=100}, VO{3=105}], VO{5=110}=[VO{5=110}, VO{7=116}]}
}
public static class VO {
    public int age, val;

    public VO(int age, int val) {
        this.age = age;
        this.val = val;
    }

    @Override
    public String toString() {
        return "VO{" + age + "=" + val + "}";
    }
}

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 2022-11-17
    • 2011-02-11
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多