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}