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