简体   繁体   中英

Java 8: How to compare all elements of a Set

This may be an already asked question but I don't find the answer I need.

I have a Set with objects like

public class MyObject {
    private LocalDate dateBeginning;
    private LocalDate dateEnd;

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

I need to check whether the Set contains to elements that overlap each other. In "old-java" I would go through the set twice and check for all combinations that exist and then break or return when I find it.

How can we do this with streams and lambdas in Java 8?

I have already tried with reduction() and filter() but none of them seem to work

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

As you said in your question, a possible solution is to loop over the set twice and determine if there are any overlaps. So what we need to determine is if, for any element in the set, we can find any other element that is different and overlaps with it.

With the Stream API, you could thus have the following:

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

anyMatch will determine if any elements of the stream satisfies the given condition. The code above is therefore asking if there is one o1 such that there is one o2 different than o1 (we can safely use != here since both objects are coming from the same set) and overlapping with it.

Note that this is a O(n²) implementation: the set is traversed twice. This could be possible in a single iteration: at each iteration, an union of the intervals [dateBeginning, dateEnd] is kept; if at any-time the intersection between the current interval and the accumulated union is non-void, then we know an overlap has been hit.

An implementation of the idea with compareTo override. Use this if you need to get exactly the overlapping ranges or their number.

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;
    }
}

Testing it:

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)"); 

Be aware that ranges { 1..3, 3..5 } are considered non-overlapping. To treat such cases (when endDate of one range equals startDate of another) as overlapping replace <= , >= with < , > .

I may also suggest using org.apache.commons.lang3.Range class for this case and a parallelStream to gain perfomance. Combining this with Tunaki's solution we get:

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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