[英]Java 8: How to compare all elements of a Set
这可能是一个已经被问到的问题,但我没有找到我需要的答案。
我有一个像这样的对象的集合
public class MyObject {
private LocalDate dateBeginning;
private LocalDate dateEnd;
public boolean overlap(MyObject otherDate) { /*code to check overlapping*/ }
}
我需要检查 Set 是否包含相互重叠的元素。 在“old-java”中,我会检查集合两次并检查所有存在的组合,然后在找到时中断或返回。
我们如何在 Java 8 中使用流和 lambda 来做到这一点?
我已经尝试过reduction()
和filter()
但它们似乎都不起作用
.filter((obj1, obj2) -> { if (obj1.overlap(obj2)) return true;}) //doesn't work
正如您在问题中所说,一个可能的解决方案是循环两次并确定是否有任何重叠。 所以我们需要确定的是,对于集合中的任何元素,我们是否可以找到与它不同并与之重叠的任何其他元素。
使用 Stream API,您可以拥有以下内容:
boolean overlap = set.stream()
.anyMatch(
o1 -> set.stream().anyMatch(o2 -> o1 != o2 && o1.overlap(o2))
);
anyMatch
将确定流的任何元素是否满足给定条件。 因此,上面的代码询问是否有一个o1
使得有一个o2
与o1
不同(我们可以安全地使用!=
在这里,因为两个对象都来自同一个集合)并与它重叠。
请注意,这是一个 O(n²) 实现:该集合被遍历两次。 这在单次迭代中是可能的:在每次迭代中,保持区间[dateBeginning, dateEnd]
的并集; 如果在任何时候当前间隔和累积联合之间的交集是非空的,那么我们就知道已经命中了重叠。
使用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
时)视为重叠,将<=
, >=
替换为<
, >
。
我也可能建议在这种情况下使用org.apache.commons.lang3.Range类和一个parallelStream
来获得性能。 将此与Tunaki 的解决方案相结合,我们得到:
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.