public static List<Interval> normalize(List<Interval> intervals) {
    if (intervals.size() <= 1) {
      return intervals;
    }

    List<Interval> valid =
        intervals.stream().filter(Interval::isValid).collect(Collectors.toList());
    if (valid.size() <= 1) {
      return valid;
    }
    // 2 or more intervals
    List<Interval> result = new ArrayList<>(valid.size());
    Collections.sort(valid);

    long start = valid.get(0).getStartMs();
    long end = valid.get(0).getEndMs();
    // scan entire list from the second interval
    for (int i = 1; i < valid.size(); i++) {
      Interval interval = valid.get(i);
      if (interval.getStartMs() <= end) {
        // continue with the same interval
        end = Math.max(end, interval.getEndMs());
      } else {
        // These are disjoint. add the previous interval
        result.add(Interval.between(start, end));
        start = interval.getStartMs();
        end = interval.getEndMs();
      }
    }
    // add the last interval
    result.add(Interval.between(start, end));
    if (result.isEmpty()) {
      return Collections.emptyList();
    }
    return result;
  }
@Test
  public void normalizeDisjoint() {
    List<Interval> l;
    Set<Interval> s;

    l = Arrays.asList(Interval.between(1, 3), Interval.between(11, 13));
    s = Sets.newHashSet(IntervalUtils.normalize(l));
    Assert.assertEquals(l.size(), s.size());
    Assert.assertTrue(s.containsAll(l));

    l = Arrays.asList(Interval.between(11, 13), Interval.between(1, 3));
    s = Sets.newHashSet(IntervalUtils.normalize(l));
    Assert.assertEquals(l.size(), s.size());
    Assert.assertTrue(s.containsAll(l));

    l = Arrays.asList(Interval.between(21, 23), Interval.between(11, 13), Interval.between(1, 3));
    s = Sets.newHashSet(IntervalUtils.normalize(l));
    Assert.assertEquals(l.size(), s.size());
    Assert.assertTrue(s.containsAll(l));
  }