For small-ish lists, you can use the DateRange class.
public class DateRange {
DateTime begins, ends;
public DateRange(DateTime b, DateTime e) {
begins = b;
ends = e;
}
public Integer hashCode() {
return 1;
}
public Boolean equals(Object o) {
DateRange r = (DateRange)o;
return begins < r.ends && ends > r.begins;
}
}
To use it:
Set<DateRange> ranges = new Set<DateRange>();
for(Integer index = 0, size = inputs.size(); index < size; index++) {
DateRange key = new DateRange(startDates[index], endDates[index]);
if(ranges.contains(key)) {
// record an error for this index;
} else {
ranges.add(key);
}
}
Note that because of how this class operates, if you add a range when contains returns true, it will screw up the internal state of the Set/Map. However, it will perform decently well on lists of up to a few hundred items.
This algorithm is an exponential-growth algorithm, specifically average is n2/2 executions, and max executions is n2. I've tested this up to about 1,000 items without a problem, though.
It's much faster than the manual:
for(Integer i = 0, s = items.size(); i < s; i++) {
for(Integer j = i + 1; j < s; j++) {
...
}
}
If you can, do this validation in JavaScript, it's much more efficient.