简体   繁体   English

使用 Java 8 流通过布尔函数将对象列表分组为列表列表

[英]Grouping a list of objects into a list of lists by a boolean function using Java 8 streams

Say I have a list of objects.假设我有一个对象列表。 I want to group them into a list of lists where each inner list contains elements for which a boolean comparison function returns true :我想将它们分组到一个列表列表中,其中每个内部列表都包含布尔比较函数为其返回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;
}

This is just a simplified example of the data that I have, in reality the comparison method is more complicated than that.这只是我拥有的数据的一个简化示例,实际上比较方法比这更复杂。 Important is that each object must match each other object in its bucket regarding the check.重要的是,每个对象必须在其存储桶中与检查相关的其他对象匹配。 The objects cannot be grouped/mapped by a specific value.不能按特定值对对象进行分组/映射。

I already have code which does this but takes three levels of for-loops and which took me about a day to write.我已经有了执行此操作的代码,但需要三个级别的 for 循环,并且花了我大约一天的时间来编写。 I'm curious if this can be achieved easier with streams.我很好奇这是否可以通过流更容易地实现。

Try mapping your items first, then use Collectors.groupingBy() :首先尝试映射您的项目,然后使用Collectors.groupingBy()

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

This does not return lists but arrays - but the concept is the same.这不会返回列表而是数组 - 但概念是相同的。

You can modify a bit the isEqual method so that you can use it as a comparator for the TreeMap .您可以稍微修改isEqual方法,以便将其用作TreeMap的比较器。 Then you can collect the TreeMap<VO, List<VO>> as follows.然后你可以收集TreeMap<VO, List<VO>>如下。 This code works in Java 7 :此代码适用于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 + "}";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM