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