TreeRangeMap.java revision 3ecfa412eddc4b084663f38d562537b86b9734d5
1/*
2 * Copyright (C) 2012 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.common.base.Preconditions.checkNotNull;
21import static com.google.common.base.Predicates.compose;
22import static com.google.common.base.Predicates.in;
23import static com.google.common.base.Predicates.not;
24
25import com.google.common.annotations.Beta;
26import com.google.common.annotations.GwtIncompatible;
27import com.google.common.base.MoreObjects;
28import com.google.common.base.Predicate;
29
30import java.util.AbstractMap;
31import java.util.AbstractSet;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.Iterator;
35import java.util.List;
36import java.util.Map;
37import java.util.Map.Entry;
38import java.util.NavigableMap;
39import java.util.NoSuchElementException;
40import java.util.Set;
41
42import javax.annotation.Nullable;
43
44/**
45 * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting
46 * all optional operations.
47 *
48 * <p>Like all {@code RangeMap} implementations, this supports neither null
49 * keys nor null values.
50 *
51 * @author Louis Wasserman
52 * @since 14.0
53 */
54@Beta
55@GwtIncompatible("NavigableMap")
56public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {
57
58  private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound;
59
60  public static <K extends Comparable, V> TreeRangeMap<K, V> create() {
61    return new TreeRangeMap<K, V>();
62  }
63
64  private TreeRangeMap() {
65    this.entriesByLowerBound = Maps.newTreeMap();
66  }
67
68  private static final class RangeMapEntry<K extends Comparable, V>
69      extends AbstractMapEntry<Range<K>, V> {
70    private final Range<K> range;
71    private final V value;
72
73    RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
74      this(Range.create(lowerBound, upperBound), value);
75    }
76
77    RangeMapEntry(Range<K> range, V value) {
78      this.range = range;
79      this.value = value;
80    }
81
82    @Override
83    public Range<K> getKey() {
84      return range;
85    }
86
87    @Override
88    public V getValue() {
89      return value;
90    }
91
92    public boolean contains(K value) {
93      return range.contains(value);
94    }
95
96    Cut<K> getLowerBound() {
97      return range.lowerBound;
98    }
99
100    Cut<K> getUpperBound() {
101      return range.upperBound;
102    }
103  }
104
105  @Override
106  @Nullable
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  @Nullable
114  public Entry<Range<K>, V> getEntry(K key) {
115    Map.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 putAll(RangeMap<K, V> rangeMap) {
135    for (Map.Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) {
136      put(entry.getKey(), entry.getValue());
137    }
138  }
139
140  @Override
141  public void clear() {
142    entriesByLowerBound.clear();
143  }
144
145  @Override
146  public Range<K> span() {
147    Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry();
148    Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry();
149    if (firstEntry == null) {
150      throw new NoSuchElementException();
151    }
152    return Range.create(
153        firstEntry.getValue().getKey().lowerBound,
154        lastEntry.getValue().getKey().upperBound);
155  }
156
157  private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
158    entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value));
159  }
160
161  @Override
162  public void remove(Range<K> rangeToRemove) {
163    if (rangeToRemove.isEmpty()) {
164      return;
165    }
166
167    /*
168     * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to
169     * indicate the bounds of ranges in the range map.
170     */
171    Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate =
172        entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
173    if (mapEntryBelowToTruncate != null) {
174      // we know ( [
175      RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue();
176      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
177        // we know ( [ )
178        if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
179          // we know ( [ ] ), so insert the range ] ) back into the map --
180          // it's being split apart
181          putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(),
182              mapEntryBelowToTruncate.getValue().getValue());
183        }
184        // overwrite mapEntryToTruncateBelow with a truncated range
185        putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound,
186            mapEntryBelowToTruncate.getValue().getValue());
187      }
188    }
189
190    Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate =
191        entriesByLowerBound.lowerEntry(rangeToRemove.upperBound);
192    if (mapEntryAboveToTruncate != null) {
193      // we know ( ]
194      RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue();
195      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
196        // we know ( ] ), and since we dealt with truncating below already,
197        // we know [ ( ] )
198        putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(),
199            mapEntryAboveToTruncate.getValue().getValue());
200        entriesByLowerBound.remove(rangeToRemove.lowerBound);
201      }
202    }
203    entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
204  }
205
206  @Override
207  public Map<Range<K>, V> asMapOfRanges() {
208    return new AsMapOfRanges();
209  }
210
211  private final class AsMapOfRanges extends AbstractMap<Range<K>, V> {
212
213    @Override
214    public boolean containsKey(@Nullable Object key) {
215      return get(key) != null;
216    }
217
218    @Override
219    public V get(@Nullable Object key) {
220      if (key instanceof Range) {
221        Range<?> range = (Range<?>) key;
222        RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound);
223        if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) {
224          return rangeMapEntry.getValue();
225        }
226      }
227      return null;
228    }
229
230    @Override
231    public Set<Entry<Range<K>, V>> entrySet() {
232      return new AbstractSet<Entry<Range<K>, V>>() {
233
234        @SuppressWarnings("unchecked") // it's safe to upcast iterators
235        @Override
236        public Iterator<Entry<Range<K>, V>> iterator() {
237          return (Iterator) entriesByLowerBound.values().iterator();
238        }
239
240        @Override
241        public int size() {
242          return entriesByLowerBound.size();
243        }
244      };
245    }
246  }
247
248  @Override
249  public RangeMap<K, V> subRangeMap(Range<K> subRange) {
250    if (subRange.equals(Range.all())) {
251      return this;
252    } else {
253      return new SubRangeMap(subRange);
254    }
255  }
256
257  @SuppressWarnings("unchecked")
258  private RangeMap<K, V> emptySubRangeMap() {
259    return EMPTY_SUB_RANGE_MAP;
260  }
261
262  private static final RangeMap EMPTY_SUB_RANGE_MAP =
263      new RangeMap() {
264        @Override
265        @Nullable
266        public Object get(Comparable key) {
267          return null;
268        }
269
270        @Override
271        @Nullable
272        public Entry<Range, Object> getEntry(Comparable key) {
273          return null;
274        }
275
276        @Override
277        public Range span() {
278          throw new NoSuchElementException();
279        }
280
281        @Override
282        public void put(Range range, Object value) {
283          checkNotNull(range);
284          throw new IllegalArgumentException(
285              "Cannot insert range " + range + " into an empty subRangeMap");
286        }
287
288        @Override
289        public void putAll(RangeMap rangeMap) {
290          if (!rangeMap.asMapOfRanges().isEmpty()) {
291            throw new IllegalArgumentException(
292                "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap");
293          }
294        }
295
296        @Override
297        public void clear() {}
298
299        @Override
300        public void remove(Range range) {
301          checkNotNull(range);
302        }
303
304        @Override
305        public Map<Range, Object> asMapOfRanges() {
306          return Collections.emptyMap();
307        }
308
309        @Override
310        public RangeMap subRangeMap(Range range) {
311          checkNotNull(range);
312          return this;
313        }
314      };
315
316  private class SubRangeMap implements RangeMap<K, V> {
317
318    private final Range<K> subRange;
319
320    SubRangeMap(Range<K> subRange) {
321      this.subRange = subRange;
322    }
323
324    @Override
325    @Nullable
326    public V get(K key) {
327      return subRange.contains(key)
328          ? TreeRangeMap.this.get(key)
329          : null;
330    }
331
332    @Override
333    @Nullable
334    public Entry<Range<K>, V> getEntry(K key) {
335      if (subRange.contains(key)) {
336        Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key);
337        if (entry != null) {
338          return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
339        }
340      }
341      return null;
342    }
343
344    @Override
345    public Range<K> span() {
346      Cut<K> lowerBound;
347      Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
348          entriesByLowerBound.floorEntry(subRange.lowerBound);
349      if (lowerEntry != null &&
350          lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) {
351        lowerBound = subRange.lowerBound;
352      } else {
353        lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound);
354        if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) {
355          throw new NoSuchElementException();
356        }
357      }
358
359      Cut<K> upperBound;
360      Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry =
361          entriesByLowerBound.lowerEntry(subRange.upperBound);
362      if (upperEntry == null) {
363        throw new NoSuchElementException();
364      } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) {
365        upperBound = subRange.upperBound;
366      } else {
367        upperBound = upperEntry.getValue().getUpperBound();
368      }
369      return Range.create(lowerBound, upperBound);
370    }
371
372    @Override
373    public void put(Range<K> range, V value) {
374      checkArgument(subRange.encloses(range),
375          "Cannot put range %s into a subRangeMap(%s)", range, subRange);
376      TreeRangeMap.this.put(range, value);
377    }
378
379    @Override
380    public void putAll(RangeMap<K, V> rangeMap) {
381      if (rangeMap.asMapOfRanges().isEmpty()) {
382        return;
383      }
384      Range<K> span = rangeMap.span();
385      checkArgument(subRange.encloses(span),
386          "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, subRange);
387      TreeRangeMap.this.putAll(rangeMap);
388    }
389
390    @Override
391    public void clear() {
392      TreeRangeMap.this.remove(subRange);
393    }
394
395    @Override
396    public void remove(Range<K> range) {
397      if (range.isConnected(subRange)) {
398        TreeRangeMap.this.remove(range.intersection(subRange));
399      }
400    }
401
402    @Override
403    public RangeMap<K, V> subRangeMap(Range<K> range) {
404      if (!range.isConnected(subRange)) {
405        return emptySubRangeMap();
406      } else {
407        return TreeRangeMap.this.subRangeMap(range.intersection(subRange));
408      }
409    }
410
411    @Override
412    public Map<Range<K>, V> asMapOfRanges() {
413      return new SubRangeMapAsMap();
414    }
415
416    @Override
417    public boolean equals(@Nullable Object o) {
418      if (o instanceof RangeMap) {
419        RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
420        return asMapOfRanges().equals(rangeMap.asMapOfRanges());
421      }
422      return false;
423    }
424
425    @Override
426    public int hashCode() {
427      return asMapOfRanges().hashCode();
428    }
429
430    @Override
431    public String toString() {
432      return asMapOfRanges().toString();
433    }
434
435    class SubRangeMapAsMap extends AbstractMap<Range<K>, V> {
436
437      @Override
438      public boolean containsKey(Object key) {
439        return get(key) != null;
440      }
441
442      @Override
443      public V get(Object key) {
444        try {
445          if (key instanceof Range) {
446            @SuppressWarnings("unchecked") // we catch ClassCastExceptions
447            Range<K> r = (Range<K>) key;
448            if (!subRange.encloses(r) || r.isEmpty()) {
449              return null;
450            }
451            RangeMapEntry<K, V> candidate = null;
452            if (r.lowerBound.compareTo(subRange.lowerBound) == 0) {
453              // r could be truncated on the left
454              Entry<Cut<K>, RangeMapEntry<K, V>> entry =
455                  entriesByLowerBound.floorEntry(r.lowerBound);
456              if (entry != null) {
457                candidate = entry.getValue();
458              }
459            } else {
460              candidate = entriesByLowerBound.get(r.lowerBound);
461            }
462
463            if (candidate != null && candidate.getKey().isConnected(subRange)
464                && candidate.getKey().intersection(subRange).equals(r)) {
465              return candidate.getValue();
466            }
467          }
468        } catch (ClassCastException e) {
469          return null;
470        }
471        return null;
472      }
473
474      @Override
475      public V remove(Object key) {
476        V value = get(key);
477        if (value != null) {
478          @SuppressWarnings("unchecked") // it's definitely in the map, so safe
479          Range<K> range = (Range<K>) key;
480          TreeRangeMap.this.remove(range);
481          return value;
482        }
483        return null;
484      }
485
486      @Override
487      public void clear() {
488        SubRangeMap.this.clear();
489      }
490
491      private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) {
492        List<Range<K>> toRemove = Lists.newArrayList();
493        for (Entry<Range<K>, V> entry : entrySet()) {
494          if (predicate.apply(entry)) {
495            toRemove.add(entry.getKey());
496          }
497        }
498        for (Range<K> range : toRemove) {
499          TreeRangeMap.this.remove(range);
500        }
501        return !toRemove.isEmpty();
502      }
503
504      @Override
505      public Set<Range<K>> keySet() {
506        return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) {
507          @Override
508          public boolean remove(@Nullable Object o) {
509            return SubRangeMapAsMap.this.remove(o) != null;
510          }
511
512          @Override
513          public boolean retainAll(Collection<?> c) {
514            return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction()));
515          }
516        };
517      }
518
519      @Override
520      public Set<Entry<Range<K>, V>> entrySet() {
521        return new Maps.EntrySet<Range<K>, V>() {
522          @Override
523          Map<Range<K>, V> map() {
524            return SubRangeMapAsMap.this;
525          }
526
527          @Override
528          public Iterator<Entry<Range<K>, V>> iterator() {
529            if (subRange.isEmpty()) {
530              return Iterators.emptyIterator();
531            }
532            Cut<K> cutToStart = MoreObjects.firstNonNull(
533                entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound);
534            final Iterator<RangeMapEntry<K, V>> backingItr =
535                entriesByLowerBound.tailMap(cutToStart, true).values().iterator();
536            return new AbstractIterator<Entry<Range<K>, V>>() {
537
538              @Override
539              protected Entry<Range<K>, V> computeNext() {
540                while (backingItr.hasNext()) {
541                  RangeMapEntry<K, V> entry = backingItr.next();
542                  if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) {
543                    break;
544                  } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) {
545                    // this might not be true e.g. at the start of the iteration
546                    return Maps.immutableEntry(
547                        entry.getKey().intersection(subRange), entry.getValue());
548                  }
549                }
550                return endOfData();
551              }
552            };
553          }
554
555          @Override
556          public boolean retainAll(Collection<?> c) {
557            return removeEntryIf(not(in(c)));
558          }
559
560          @Override
561          public int size() {
562            return Iterators.size(iterator());
563          }
564
565          @Override
566          public boolean isEmpty() {
567            return !iterator().hasNext();
568          }
569        };
570      }
571
572      @Override
573      public Collection<V> values() {
574        return new Maps.Values<Range<K>, V>(this) {
575          @Override
576          public boolean removeAll(Collection<?> c) {
577            return removeEntryIf(compose(in(c), Maps.<V>valueFunction()));
578          }
579
580          @Override
581          public boolean retainAll(Collection<?> c) {
582            return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction()));
583          }
584        };
585      }
586    }
587  }
588
589  @Override
590  public boolean equals(@Nullable Object o) {
591    if (o instanceof RangeMap) {
592      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
593      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
594    }
595    return false;
596  }
597
598  @Override
599  public int hashCode() {
600    return asMapOfRanges().hashCode();
601  }
602
603  @Override
604  public String toString() {
605    return entriesByLowerBound.values().toString();
606  }
607}
608