001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021import static com.google.common.base.Predicates.compose;
022import static com.google.common.base.Predicates.in;
023import static com.google.common.base.Predicates.not;
024import static com.google.common.collect.Iterators.emptyIterator;
025import static com.google.common.collect.Maps.immutableEntry;
026import static java.util.Collections.emptyMap;
027import static java.util.Objects.requireNonNull;
028
029import com.google.common.annotations.GwtIncompatible;
030import com.google.common.base.MoreObjects;
031import com.google.common.base.Predicate;
032import com.google.common.collect.Maps.IteratorBasedAbstractMap;
033import java.util.AbstractMap;
034import java.util.Collection;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Map;
038import java.util.Map.Entry;
039import java.util.NavigableMap;
040import java.util.NoSuchElementException;
041import java.util.Set;
042import javax.annotation.CheckForNull;
043
044/**
045 * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting all optional
046 * operations.
047 *
048 * <p>Like all {@code RangeMap} implementations, this supports neither null keys nor null values.
049 *
050 * @author Louis Wasserman
051 * @since 14.0
052 */
053@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989
054@GwtIncompatible // NavigableMap
055public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {
056
057  private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound;
058
059  /** Returns a new, empty {@link TreeRangeMap}. */
060  public static <K extends Comparable, V> TreeRangeMap<K, V> create() {
061    return new TreeRangeMap<>();
062  }
063
064  /**
065   * Returns a new {@link TreeRangeMap} containing the same ranges as the given {@code RangeMap}.
066   *
067   * @since 33.4.0
068   */
069  @SuppressWarnings("unchecked")
070  public static <K extends Comparable<?>, V> TreeRangeMap<K, V> copyOf(
071      RangeMap<K, ? extends V> rangeMap) {
072    if (rangeMap instanceof TreeRangeMap) {
073      NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound = Maps.newTreeMap();
074      entriesByLowerBound.putAll(((TreeRangeMap<K, V>) rangeMap).entriesByLowerBound);
075      return new TreeRangeMap<>(entriesByLowerBound);
076    } else {
077      NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound = Maps.newTreeMap();
078      for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) {
079        entriesByLowerBound.put(
080            entry.getKey().lowerBound(), new RangeMapEntry<K, V>(entry.getKey(), entry.getValue()));
081      }
082      return new TreeRangeMap<>(entriesByLowerBound);
083    }
084  }
085
086  private TreeRangeMap() {
087    this.entriesByLowerBound = Maps.newTreeMap();
088  }
089
090  private TreeRangeMap(NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound) {
091    this.entriesByLowerBound = entriesByLowerBound;
092  }
093
094  private static final class RangeMapEntry<K extends Comparable, V>
095      extends AbstractMapEntry<Range<K>, V> {
096    private final Range<K> range;
097    private final V value;
098
099    RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
100      this(Range.create(lowerBound, upperBound), value);
101    }
102
103    RangeMapEntry(Range<K> range, V value) {
104      this.range = range;
105      this.value = value;
106    }
107
108    @Override
109    public Range<K> getKey() {
110      return range;
111    }
112
113    @Override
114    public V getValue() {
115      return value;
116    }
117
118    public boolean contains(K value) {
119      return range.contains(value);
120    }
121
122    Cut<K> getLowerBound() {
123      return range.lowerBound;
124    }
125
126    Cut<K> getUpperBound() {
127      return range.upperBound;
128    }
129  }
130
131  @Override
132  @CheckForNull
133  public V get(K key) {
134    Entry<Range<K>, V> entry = getEntry(key);
135    return (entry == null) ? null : entry.getValue();
136  }
137
138  @Override
139  @CheckForNull
140  public Entry<Range<K>, V> getEntry(K key) {
141    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry =
142        entriesByLowerBound.floorEntry(Cut.belowValue(key));
143    if (mapEntry != null && mapEntry.getValue().contains(key)) {
144      return mapEntry.getValue();
145    } else {
146      return null;
147    }
148  }
149
150  @Override
151  public void put(Range<K> range, V value) {
152    if (!range.isEmpty()) {
153      checkNotNull(value);
154      remove(range);
155      entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value));
156    }
157  }
158
159  @Override
160  public void putCoalescing(Range<K> range, V value) {
161    // don't short-circuit if the range is empty - it may be between two ranges we can coalesce.
162    if (entriesByLowerBound.isEmpty()) {
163      put(range, value);
164      return;
165    }
166
167    Range<K> coalescedRange = coalescedRange(range, checkNotNull(value));
168    put(coalescedRange, value);
169  }
170
171  /** Computes the coalesced range for the given range+value - does not mutate the map. */
172  private Range<K> coalescedRange(Range<K> range, V value) {
173    Range<K> coalescedRange = range;
174    Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
175        entriesByLowerBound.lowerEntry(range.lowerBound);
176    coalescedRange = coalesce(coalescedRange, value, lowerEntry);
177
178    Entry<Cut<K>, RangeMapEntry<K, V>> higherEntry =
179        entriesByLowerBound.floorEntry(range.upperBound);
180    coalescedRange = coalesce(coalescedRange, value, higherEntry);
181
182    return coalescedRange;
183  }
184
185  /** Returns the range that spans the given range and entry, if the entry can be coalesced. */
186  private static <K extends Comparable, V> Range<K> coalesce(
187      Range<K> range, V value, @CheckForNull Entry<Cut<K>, RangeMapEntry<K, V>> entry) {
188    if (entry != null
189        && entry.getValue().getKey().isConnected(range)
190        && entry.getValue().getValue().equals(value)) {
191      return range.span(entry.getValue().getKey());
192    }
193    return range;
194  }
195
196  @Override
197  public void putAll(RangeMap<K, ? extends V> rangeMap) {
198    for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) {
199      put(entry.getKey(), entry.getValue());
200    }
201  }
202
203  @Override
204  public void clear() {
205    entriesByLowerBound.clear();
206  }
207
208  @Override
209  public Range<K> span() {
210    Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry();
211    Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry();
212    // Either both are null or neither is, but we check both to satisfy the nullness checker.
213    if (firstEntry == null || lastEntry == null) {
214      throw new NoSuchElementException();
215    }
216    return Range.create(
217        firstEntry.getValue().getKey().lowerBound, lastEntry.getValue().getKey().upperBound);
218  }
219
220  private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
221    entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value));
222  }
223
224  @Override
225  public void remove(Range<K> rangeToRemove) {
226    if (rangeToRemove.isEmpty()) {
227      return;
228    }
229
230    /*
231     * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to
232     * indicate the bounds of ranges in the range map.
233     */
234    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate =
235        entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
236    if (mapEntryBelowToTruncate != null) {
237      // we know ( [
238      RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue();
239      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
240        // we know ( [ )
241        if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
242          // we know ( [ ] ), so insert the range ] ) back into the map --
243          // it's being split apart
244          putRangeMapEntry(
245              rangeToRemove.upperBound,
246              rangeMapEntry.getUpperBound(),
247              mapEntryBelowToTruncate.getValue().getValue());
248        }
249        // overwrite mapEntryToTruncateBelow with a truncated range
250        putRangeMapEntry(
251            rangeMapEntry.getLowerBound(),
252            rangeToRemove.lowerBound,
253            mapEntryBelowToTruncate.getValue().getValue());
254      }
255    }
256
257    Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate =
258        entriesByLowerBound.lowerEntry(rangeToRemove.upperBound);
259    if (mapEntryAboveToTruncate != null) {
260      // we know ( ]
261      RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue();
262      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
263        // we know ( ] ), and since we dealt with truncating below already,
264        // we know [ ( ] )
265        putRangeMapEntry(
266            rangeToRemove.upperBound,
267            rangeMapEntry.getUpperBound(),
268            mapEntryAboveToTruncate.getValue().getValue());
269      }
270    }
271    entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
272  }
273
274  @Override
275  public Map<Range<K>, V> asMapOfRanges() {
276    return new AsMapOfRanges(entriesByLowerBound.values());
277  }
278
279  @Override
280  public Map<Range<K>, V> asDescendingMapOfRanges() {
281    return new AsMapOfRanges(entriesByLowerBound.descendingMap().values());
282  }
283
284  private final class AsMapOfRanges extends IteratorBasedAbstractMap<Range<K>, V> {
285
286    final Iterable<Entry<Range<K>, V>> entryIterable;
287
288    @SuppressWarnings("unchecked") // it's safe to upcast iterables
289    AsMapOfRanges(Iterable<RangeMapEntry<K, V>> entryIterable) {
290      this.entryIterable = (Iterable) entryIterable;
291    }
292
293    @Override
294    public boolean containsKey(@CheckForNull Object key) {
295      return get(key) != null;
296    }
297
298    @Override
299    @CheckForNull
300    public V get(@CheckForNull Object key) {
301      if (key instanceof Range) {
302        Range<?> range = (Range<?>) key;
303        RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound);
304        if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) {
305          return rangeMapEntry.getValue();
306        }
307      }
308      return null;
309    }
310
311    @Override
312    public int size() {
313      return entriesByLowerBound.size();
314    }
315
316    @Override
317    Iterator<Entry<Range<K>, V>> entryIterator() {
318      return entryIterable.iterator();
319    }
320  }
321
322  @Override
323  public RangeMap<K, V> subRangeMap(Range<K> subRange) {
324    if (subRange.equals(Range.all())) {
325      return this;
326    } else {
327      return new SubRangeMap(subRange);
328    }
329  }
330
331  @SuppressWarnings("unchecked")
332  private RangeMap<K, V> emptySubRangeMap() {
333    return (RangeMap<K, V>) (RangeMap<?, ?>) EMPTY_SUB_RANGE_MAP;
334  }
335
336  @SuppressWarnings("ConstantCaseForConstants") // This RangeMap is immutable.
337  private static final RangeMap<Comparable<?>, Object> EMPTY_SUB_RANGE_MAP =
338      new RangeMap<Comparable<?>, Object>() {
339        @Override
340        @CheckForNull
341        public Object get(Comparable<?> key) {
342          return null;
343        }
344
345        @Override
346        @CheckForNull
347        public Entry<Range<Comparable<?>>, Object> getEntry(Comparable<?> key) {
348          return null;
349        }
350
351        @Override
352        public Range<Comparable<?>> span() {
353          throw new NoSuchElementException();
354        }
355
356        @Override
357        public void put(Range<Comparable<?>> range, Object value) {
358          checkNotNull(range);
359          throw new IllegalArgumentException(
360              "Cannot insert range " + range + " into an empty subRangeMap");
361        }
362
363        @Override
364        public void putCoalescing(Range<Comparable<?>> range, Object value) {
365          checkNotNull(range);
366          throw new IllegalArgumentException(
367              "Cannot insert range " + range + " into an empty subRangeMap");
368        }
369
370        @Override
371        public void putAll(RangeMap<Comparable<?>, ? extends Object> rangeMap) {
372          if (!rangeMap.asMapOfRanges().isEmpty()) {
373            throw new IllegalArgumentException(
374                "Cannot putAll(nonEmptyRangeMap) into an empty subRangeMap");
375          }
376        }
377
378        @Override
379        public void clear() {}
380
381        @Override
382        public void remove(Range<Comparable<?>> range) {
383          checkNotNull(range);
384        }
385
386        @Override
387        public Map<Range<Comparable<?>>, Object> asMapOfRanges() {
388          return emptyMap();
389        }
390
391        @Override
392        public Map<Range<Comparable<?>>, Object> asDescendingMapOfRanges() {
393          return emptyMap();
394        }
395
396        @Override
397        public RangeMap<Comparable<?>, Object> subRangeMap(Range<Comparable<?>> range) {
398          checkNotNull(range);
399          return this;
400        }
401      };
402
403  private class SubRangeMap implements RangeMap<K, V> {
404
405    private final Range<K> subRange;
406
407    SubRangeMap(Range<K> subRange) {
408      this.subRange = subRange;
409    }
410
411    @Override
412    @CheckForNull
413    public V get(K key) {
414      return subRange.contains(key) ? TreeRangeMap.this.get(key) : null;
415    }
416
417    @Override
418    @CheckForNull
419    public Entry<Range<K>, V> getEntry(K key) {
420      if (subRange.contains(key)) {
421        Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key);
422        if (entry != null) {
423          return immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
424        }
425      }
426      return null;
427    }
428
429    @Override
430    public Range<K> span() {
431      Cut<K> lowerBound;
432      Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
433          entriesByLowerBound.floorEntry(subRange.lowerBound);
434      if (lowerEntry != null
435          && lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) {
436        lowerBound = subRange.lowerBound;
437      } else {
438        lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound);
439        if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) {
440          throw new NoSuchElementException();
441        }
442      }
443
444      Cut<K> upperBound;
445      Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry =
446          entriesByLowerBound.lowerEntry(subRange.upperBound);
447      if (upperEntry == null) {
448        throw new NoSuchElementException();
449      } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) {
450        upperBound = subRange.upperBound;
451      } else {
452        upperBound = upperEntry.getValue().getUpperBound();
453      }
454      return Range.create(lowerBound, upperBound);
455    }
456
457    @Override
458    public void put(Range<K> range, V value) {
459      checkArgument(
460          subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, subRange);
461      TreeRangeMap.this.put(range, value);
462    }
463
464    @Override
465    public void putCoalescing(Range<K> range, V value) {
466      if (entriesByLowerBound.isEmpty() || !subRange.encloses(range)) {
467        put(range, value);
468        return;
469      }
470
471      Range<K> coalescedRange = coalescedRange(range, checkNotNull(value));
472      // only coalesce ranges within the subRange
473      put(coalescedRange.intersection(subRange), value);
474    }
475
476    @Override
477    public void putAll(RangeMap<K, ? extends V> rangeMap) {
478      if (rangeMap.asMapOfRanges().isEmpty()) {
479        return;
480      }
481      Range<K> span = rangeMap.span();
482      checkArgument(
483          subRange.encloses(span),
484          "Cannot putAll rangeMap with span %s into a subRangeMap(%s)",
485          span,
486          subRange);
487      TreeRangeMap.this.putAll(rangeMap);
488    }
489
490    @Override
491    public void clear() {
492      TreeRangeMap.this.remove(subRange);
493    }
494
495    @Override
496    public void remove(Range<K> range) {
497      if (range.isConnected(subRange)) {
498        TreeRangeMap.this.remove(range.intersection(subRange));
499      }
500    }
501
502    @Override
503    public RangeMap<K, V> subRangeMap(Range<K> range) {
504      if (!range.isConnected(subRange)) {
505        return emptySubRangeMap();
506      } else {
507        return TreeRangeMap.this.subRangeMap(range.intersection(subRange));
508      }
509    }
510
511    @Override
512    public Map<Range<K>, V> asMapOfRanges() {
513      return new SubRangeMapAsMap();
514    }
515
516    @Override
517    public Map<Range<K>, V> asDescendingMapOfRanges() {
518      return new SubRangeMapAsMap() {
519
520        @Override
521        Iterator<Entry<Range<K>, V>> entryIterator() {
522          if (subRange.isEmpty()) {
523            return emptyIterator();
524          }
525          final Iterator<RangeMapEntry<K, V>> backingItr =
526              entriesByLowerBound
527                  .headMap(subRange.upperBound, false)
528                  .descendingMap()
529                  .values()
530                  .iterator();
531          return new AbstractIterator<Entry<Range<K>, V>>() {
532
533            @Override
534            @CheckForNull
535            protected Entry<Range<K>, V> computeNext() {
536              if (backingItr.hasNext()) {
537                RangeMapEntry<K, V> entry = backingItr.next();
538                if (entry.getUpperBound().compareTo(subRange.lowerBound) <= 0) {
539                  return endOfData();
540                }
541                return immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
542              }
543              return endOfData();
544            }
545          };
546        }
547      };
548    }
549
550    @Override
551    public boolean equals(@CheckForNull Object o) {
552      if (o instanceof RangeMap) {
553        RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
554        return asMapOfRanges().equals(rangeMap.asMapOfRanges());
555      }
556      return false;
557    }
558
559    @Override
560    public int hashCode() {
561      return asMapOfRanges().hashCode();
562    }
563
564    @Override
565    public String toString() {
566      return asMapOfRanges().toString();
567    }
568
569    class SubRangeMapAsMap extends AbstractMap<Range<K>, V> {
570
571      @Override
572      public boolean containsKey(@CheckForNull Object key) {
573        return get(key) != null;
574      }
575
576      @Override
577      @CheckForNull
578      public V get(@CheckForNull Object key) {
579        try {
580          if (key instanceof Range) {
581            @SuppressWarnings("unchecked") // we catch ClassCastExceptions
582            Range<K> r = (Range<K>) key;
583            if (!subRange.encloses(r) || r.isEmpty()) {
584              return null;
585            }
586            RangeMapEntry<K, V> candidate = null;
587            if (r.lowerBound.compareTo(subRange.lowerBound) == 0) {
588              // r could be truncated on the left
589              Entry<Cut<K>, RangeMapEntry<K, V>> entry =
590                  entriesByLowerBound.floorEntry(r.lowerBound);
591              if (entry != null) {
592                candidate = entry.getValue();
593              }
594            } else {
595              candidate = entriesByLowerBound.get(r.lowerBound);
596            }
597
598            if (candidate != null
599                && candidate.getKey().isConnected(subRange)
600                && candidate.getKey().intersection(subRange).equals(r)) {
601              return candidate.getValue();
602            }
603          }
604        } catch (ClassCastException e) {
605          return null;
606        }
607        return null;
608      }
609
610      @Override
611      @CheckForNull
612      public V remove(@CheckForNull Object key) {
613        V value = get(key);
614        if (value != null) {
615          // it's definitely in the map, so the cast and requireNonNull are safe
616          @SuppressWarnings("unchecked")
617          Range<K> range = (Range<K>) requireNonNull(key);
618          TreeRangeMap.this.remove(range);
619          return value;
620        }
621        return null;
622      }
623
624      @Override
625      public void clear() {
626        SubRangeMap.this.clear();
627      }
628
629      private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) {
630        List<Range<K>> toRemove = Lists.newArrayList();
631        for (Entry<Range<K>, V> entry : entrySet()) {
632          if (predicate.apply(entry)) {
633            toRemove.add(entry.getKey());
634          }
635        }
636        for (Range<K> range : toRemove) {
637          TreeRangeMap.this.remove(range);
638        }
639        return !toRemove.isEmpty();
640      }
641
642      @Override
643      public Set<Range<K>> keySet() {
644        return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) {
645          @Override
646          public boolean remove(@CheckForNull Object o) {
647            return SubRangeMapAsMap.this.remove(o) != null;
648          }
649
650          @Override
651          public boolean retainAll(Collection<?> c) {
652            return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction()));
653          }
654        };
655      }
656
657      @Override
658      public Set<Entry<Range<K>, V>> entrySet() {
659        return new Maps.EntrySet<Range<K>, V>() {
660          @Override
661          Map<Range<K>, V> map() {
662            return SubRangeMapAsMap.this;
663          }
664
665          @Override
666          public Iterator<Entry<Range<K>, V>> iterator() {
667            return entryIterator();
668          }
669
670          @Override
671          public boolean retainAll(Collection<?> c) {
672            return removeEntryIf(not(in(c)));
673          }
674
675          @Override
676          public int size() {
677            return Iterators.size(iterator());
678          }
679
680          @Override
681          public boolean isEmpty() {
682            return !iterator().hasNext();
683          }
684        };
685      }
686
687      Iterator<Entry<Range<K>, V>> entryIterator() {
688        if (subRange.isEmpty()) {
689          return emptyIterator();
690        }
691        Cut<K> cutToStart =
692            MoreObjects.firstNonNull(
693                entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound);
694        final Iterator<RangeMapEntry<K, V>> backingItr =
695            entriesByLowerBound.tailMap(cutToStart, true).values().iterator();
696        return new AbstractIterator<Entry<Range<K>, V>>() {
697
698          @Override
699          @CheckForNull
700          protected Entry<Range<K>, V> computeNext() {
701            while (backingItr.hasNext()) {
702              RangeMapEntry<K, V> entry = backingItr.next();
703              if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) {
704                return endOfData();
705              } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) {
706                // this might not be true e.g. at the start of the iteration
707                return immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
708              }
709            }
710            return endOfData();
711          }
712        };
713      }
714
715      @Override
716      public Collection<V> values() {
717        return new Maps.Values<Range<K>, V>(this) {
718          @Override
719          public boolean removeAll(Collection<?> c) {
720            return removeEntryIf(compose(in(c), Maps.<V>valueFunction()));
721          }
722
723          @Override
724          public boolean retainAll(Collection<?> c) {
725            return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction()));
726          }
727        };
728      }
729    }
730  }
731
732  @Override
733  public boolean equals(@CheckForNull Object o) {
734    if (o instanceof RangeMap) {
735      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
736      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
737    }
738    return false;
739  }
740
741  @Override
742  public int hashCode() {
743    return asMapOfRanges().hashCode();
744  }
745
746  @Override
747  public String toString() {
748    return entriesByLowerBound.values().toString();
749  }
750}