Negating the no-overlap case
You mention four separate cases where two time intervals overlap. You then reduce it to three by pointing out that one case is really the intersection of two of the other cases. So checking those cases implicitly checks the fourth. But this is more complicated than it needs to be. Instead of thinking about when they overlap, think about when they don't.
There are only two cases where the intervals do not overlap, and they are mirror images of each other. Two intervals do not overlap when one ends before the other begins. Because either one can (a priori) be the one that ends first, this requires two checks, as coded here:
private static boolean hasOverlap(Interval t1, Interval t2) {
return !t1.end.isBefore(t2.begin) && !t1.begin.isAfter(t2.end);
}
I changed the name to hasOverlap
as being simpler.
I distributed the negation over the union to get an intersection of negations. The two are logically equivalent.
This code implicitly assumes that the time intervals are closed. I.e. that they can overlap if one interval starts at the exact time that the other ends. If that is not how intervals are supposed to work, you can change it. E.g. to
return t1.end.isAfter(t2.begin) && t1.begin.isBefore(t2.end);
This will not match if one interval starts when the other ends but will match when there is an overlapping interval (as opposed to a point of intersection).
A variant of this was already suggested in this answer.
One version is not better than the other. It really depends on your requirements. Is it a requirement that a point of intersection be considered overlap? Or should only intervals broader than a point count?
Your original is compatible with the latter definition.
Simplifying the overlap case
Another way of reaching the same conclusion is to realize that your third case is also a special version of your first case. The second interval begins after the first and before the first ends. As such, it doesn't matter whether the second interval ends before or after the first. It overlaps regardless. Same thing with the third case and the second case, only it's the end of the second interval that is within the first interval. So you can get rid of the third case.
You actually have a bug if your intention was to check for the third case and not the fourth.
t1.begin.isBefore(t2.begin) && t1.end.isAfter(t2.end);
That's a check for the fourth case (the first interval entirely encloses the second, starting before it and ending after). You are implicitly assuming that the third case is covered by the other cases. Even though your logic says that you are supposed to be eliminating the fourth case and checking the third. You checked the fourth and eliminated the third. Of course, since both are redundant, the bug did not affect functionality.
The final observation is that you know the beginning of an interval is before the end (logically). So you don't need to compare both the beginning and the end to the same point in the other interval. If the end of one interval is before the beginning of the other, then the beginning is as well. And the symmetric relationship. This reduces from four comparisons to check the first two cases to just two. You won't know which of the overlap cases holds, but you'll know that one does.
Interval
constructor reallyprivate
? How then are you creating new instances of it? Please show your real actual code. \$\endgroup\$Interval
class and theisThereOverlap
method, both reside in the same class. \$\endgroup\$