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