Java 8:如何比较Set的所有元素

时间:2016-05-27 13:57:13

标签: java java-8 java-stream

这可能是一个已经问过的问题,但我找不到我需要的答案。

我有一个像

这样的对象
public class MyObject {
    private LocalDate dateBeginning;
    private LocalDate dateEnd;

    public boolean overlap(MyObject otherDate) { /*code to check overlapping*/ }
}

我需要检查Set是否包含彼此重叠的元素。在" old-java"我会经历两次集合,检查所有存在的组合,然后在找到它时断开或返回。

我们如何在Java 8中使用stream和lambdas做到这一点?

我已经尝试使用reduction()filter(),但似乎没有一个可以使用

.filter((obj1, obj2) -> { if (obj1.overlap(obj2)) return true;}) //doesn't work

3 个答案:

答案 0 :(得分:9)

正如您在问题中所说的那样,一种可能的解决方案是在集合上循环两次并确定是否存在任何重叠。所以我们需要确定的是,对于集合中的任何元素,我们是否可以找到任何其他不同且与之重叠的元素。

使用Stream API,您可以拥有以下内容:

boolean overlap = set.stream().anyMatch(o1 -> set.stream().anyMatch(o2 -> o1 != o2 && o1.overlap(o2)));

anyMatch将确定流的任何元素是否满足给定条件。因此,上面的代码会询问是否有o1o2o1不同{我们可以安全地使用!=,因为这两个对象都来自同一组)并与之重叠。

请注意,这是一个O(n²)实现:遍历集合两次。这可以在单次迭代中实现:在每次迭代时,保留间隔[dateBeginning, dateEnd]的并集;如果在任何时候当前区间和累积联合之间的交集是非空的,那么我们就知道重叠已被击中。

答案 1 :(得分:0)

使用compareTo覆盖的想法的实现。如果您需要准确获取重叠范围或数字,请使用此选项。

public class Range implements Comparable<Range> {
    private LocalDate startDate;
    private LocalDate endDate;

    public Range(LocalDate startDate, LocalDate endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }

    @Override
    public int compareTo(Range range) {
        if (range.endDate.compareTo(endDate) >= 0 && range.startDate.compareTo(endDate) >= 0) return 1;
        if (range.endDate.compareTo(startDate) <= 0 && range.startDate.compareTo(startDate) <= 0) return -1;
        return 0;
    }
}

测试它:

LocalDate May1 = LocalDate.of(2016, 5, 1);
LocalDate May3 = LocalDate.of(2016, 5, 3);
LocalDate May5 = LocalDate.of(2016, 5, 5);
LocalDate May7 = LocalDate.of(2016, 5, 7);
LocalDate May9 = LocalDate.of(2016, 5, 9);

Set<Range> ranges = new HashSet<>();

ranges.add(new Range(May1, May5));
ranges.add(new Range(May3, May7));
ranges.add(new Range(May7, May9));

Set filteredRanges = ranges.stream().collect(Collectors.toCollection(TreeSet::new));
long totalOverlaps = ranges.size() - filteredRanges.size();
System.out.println(totalOverlaps + " overlapping range(s)"); 

请注意,范围{1..3, 3..5}被视为不重叠。要处理此类案例(当一个范围的endDate等于另一个范围的startDate)作为重叠时,将<=>=替换为<>

答案 2 :(得分:0)

我可能还建议在这种情况下使用org.apache.commons.lang3.Range类,并使用parallelStream来获得性能。将其与Tunaki's solution结合起来我们得到:

Set<Range> ranges = new HashSet<>();
ranges.add(Range.between(LocalDate.of(2016, 5, 1), LocalDate.of(2016, 5, 5)));
ranges.add(Range.between(LocalDate.of(2016, 5, 3), LocalDate.of(2016, 5, 7)));

boolean overlap = ranges.parallelStream().anyMatch(
                o1 -> ranges.parallelStream()
                        .anyMatch(o2 -> o1 != o2 && o1.isOverlappedBy(o2))
);

System.out.println("overlap = " + overlap);