001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.collect; 016 017import static com.google.common.base.Preconditions.checkArgument; 018import static com.google.common.base.Preconditions.checkElementIndex; 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtIncompatible; 023import com.google.common.annotations.J2ktIncompatible; 024import com.google.common.collect.SortedLists.KeyAbsentBehavior; 025import com.google.common.collect.SortedLists.KeyPresentBehavior; 026import com.google.errorprone.annotations.CanIgnoreReturnValue; 027import com.google.errorprone.annotations.DoNotCall; 028import com.google.errorprone.annotations.DoNotMock; 029import java.io.InvalidObjectException; 030import java.io.ObjectInputStream; 031import java.io.Serializable; 032import java.util.Collections; 033import java.util.List; 034import java.util.Map; 035import java.util.Map.Entry; 036import java.util.NoSuchElementException; 037import java.util.function.Function; 038import java.util.stream.Collector; 039import javax.annotation.CheckForNull; 040import org.checkerframework.checker.nullness.qual.Nullable; 041 042/** 043 * A {@link RangeMap} whose contents will never change, with many other important properties 044 * detailed at {@link ImmutableCollection}. 045 * 046 * @author Louis Wasserman 047 * @since 14.0 048 */ 049@GwtIncompatible // NavigableMap 050@ElementTypesAreNonnullByDefault 051public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V>, Serializable { 052 053 private static final ImmutableRangeMap<Comparable<?>, Object> EMPTY = 054 new ImmutableRangeMap<>(ImmutableList.<Range<Comparable<?>>>of(), ImmutableList.of()); 055 056 /** 057 * Returns a {@code Collector} that accumulates the input elements into a new {@code 058 * ImmutableRangeMap}. As in {@link Builder}, overlapping ranges are not permitted. 059 * 060 * @since 33.2.0 (available since 23.1 in guava-jre) 061 */ 062 @SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"}) 063 @IgnoreJRERequirement // Users will use this only if they're already using streams. 064 @Beta // TODO: b/288085449 - Remove. 065 public static <T extends @Nullable Object, K extends Comparable<? super K>, V> 066 Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap( 067 Function<? super T, Range<K>> keyFunction, 068 Function<? super T, ? extends V> valueFunction) { 069 return CollectCollectors.toImmutableRangeMap(keyFunction, valueFunction); 070 } 071 072 /** 073 * Returns an empty immutable range map. 074 * 075 * <p><b>Performance note:</b> the instance returned is a singleton. 076 */ 077 @SuppressWarnings("unchecked") 078 public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() { 079 return (ImmutableRangeMap<K, V>) EMPTY; 080 } 081 082 /** Returns an immutable range map mapping a single range to a single value. */ 083 public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of(Range<K> range, V value) { 084 return new ImmutableRangeMap<>(ImmutableList.of(range), ImmutableList.of(value)); 085 } 086 087 @SuppressWarnings("unchecked") 088 public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf( 089 RangeMap<K, ? extends V> rangeMap) { 090 if (rangeMap instanceof ImmutableRangeMap) { 091 return (ImmutableRangeMap<K, V>) rangeMap; 092 } 093 Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges(); 094 ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(map.size()); 095 ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<>(map.size()); 096 for (Entry<Range<K>, ? extends V> entry : map.entrySet()) { 097 rangesBuilder.add(entry.getKey()); 098 valuesBuilder.add(entry.getValue()); 099 } 100 return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build()); 101 } 102 103 /** Returns a new builder for an immutable range map. */ 104 public static <K extends Comparable<?>, V> Builder<K, V> builder() { 105 return new Builder<>(); 106 } 107 108 /** 109 * A builder for immutable range maps. Overlapping ranges are prohibited. 110 * 111 * @since 14.0 112 */ 113 @DoNotMock 114 public static final class Builder<K extends Comparable<?>, V> { 115 private final List<Entry<Range<K>, V>> entries; 116 117 public Builder() { 118 this.entries = Lists.newArrayList(); 119 } 120 121 /** 122 * Associates the specified range with the specified value. 123 * 124 * @throws IllegalArgumentException if {@code range} is empty 125 */ 126 @CanIgnoreReturnValue 127 public Builder<K, V> put(Range<K> range, V value) { 128 checkNotNull(range); 129 checkNotNull(value); 130 checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); 131 entries.add(Maps.immutableEntry(range, value)); 132 return this; 133 } 134 135 /** Copies all associations from the specified range map into this builder. */ 136 @CanIgnoreReturnValue 137 public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) { 138 for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { 139 put(entry.getKey(), entry.getValue()); 140 } 141 return this; 142 } 143 144 @CanIgnoreReturnValue 145 Builder<K, V> combine(Builder<K, V> builder) { 146 entries.addAll(builder.entries); 147 return this; 148 } 149 150 /** 151 * Returns an {@code ImmutableRangeMap} containing the associations previously added to this 152 * builder. 153 * 154 * @throws IllegalArgumentException if any two ranges inserted into this builder overlap 155 */ 156 public ImmutableRangeMap<K, V> build() { 157 Collections.sort(entries, Range.<K>rangeLexOrdering().onKeys()); 158 ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(entries.size()); 159 ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<>(entries.size()); 160 for (int i = 0; i < entries.size(); i++) { 161 Range<K> range = entries.get(i).getKey(); 162 if (i > 0) { 163 Range<K> prevRange = entries.get(i - 1).getKey(); 164 if (range.isConnected(prevRange) && !range.intersection(prevRange).isEmpty()) { 165 throw new IllegalArgumentException( 166 "Overlapping ranges: range " + prevRange + " overlaps with entry " + range); 167 } 168 } 169 rangesBuilder.add(range); 170 valuesBuilder.add(entries.get(i).getValue()); 171 } 172 return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build()); 173 } 174 } 175 176 private final transient ImmutableList<Range<K>> ranges; 177 private final transient ImmutableList<V> values; 178 179 ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) { 180 this.ranges = ranges; 181 this.values = values; 182 } 183 184 @Override 185 @CheckForNull 186 public V get(K key) { 187 int index = 188 SortedLists.binarySearch( 189 ranges, 190 Range::lowerBound, 191 Cut.belowValue(key), 192 KeyPresentBehavior.ANY_PRESENT, 193 KeyAbsentBehavior.NEXT_LOWER); 194 if (index == -1) { 195 return null; 196 } else { 197 Range<K> range = ranges.get(index); 198 return range.contains(key) ? values.get(index) : null; 199 } 200 } 201 202 @Override 203 @CheckForNull 204 public Entry<Range<K>, V> getEntry(K key) { 205 int index = 206 SortedLists.binarySearch( 207 ranges, 208 Range::lowerBound, 209 Cut.belowValue(key), 210 KeyPresentBehavior.ANY_PRESENT, 211 KeyAbsentBehavior.NEXT_LOWER); 212 if (index == -1) { 213 return null; 214 } else { 215 Range<K> range = ranges.get(index); 216 return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; 217 } 218 } 219 220 @Override 221 public Range<K> span() { 222 if (ranges.isEmpty()) { 223 throw new NoSuchElementException(); 224 } 225 Range<K> firstRange = ranges.get(0); 226 Range<K> lastRange = ranges.get(ranges.size() - 1); 227 return Range.create(firstRange.lowerBound, lastRange.upperBound); 228 } 229 230 /** 231 * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. 232 * 233 * @throws UnsupportedOperationException always 234 * @deprecated Unsupported operation. 235 */ 236 @Deprecated 237 @Override 238 @DoNotCall("Always throws UnsupportedOperationException") 239 public final void put(Range<K> range, V value) { 240 throw new UnsupportedOperationException(); 241 } 242 243 /** 244 * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. 245 * 246 * @throws UnsupportedOperationException always 247 * @deprecated Unsupported operation. 248 */ 249 @Deprecated 250 @Override 251 @DoNotCall("Always throws UnsupportedOperationException") 252 public final void putCoalescing(Range<K> range, V value) { 253 throw new UnsupportedOperationException(); 254 } 255 256 /** 257 * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. 258 * 259 * @throws UnsupportedOperationException always 260 * @deprecated Unsupported operation. 261 */ 262 @Deprecated 263 @Override 264 @DoNotCall("Always throws UnsupportedOperationException") 265 public final void putAll(RangeMap<K, ? extends V> rangeMap) { 266 throw new UnsupportedOperationException(); 267 } 268 269 /** 270 * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. 271 * 272 * @throws UnsupportedOperationException always 273 * @deprecated Unsupported operation. 274 */ 275 @Deprecated 276 @Override 277 @DoNotCall("Always throws UnsupportedOperationException") 278 public final void clear() { 279 throw new UnsupportedOperationException(); 280 } 281 282 /** 283 * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. 284 * 285 * @throws UnsupportedOperationException always 286 * @deprecated Unsupported operation. 287 */ 288 @Deprecated 289 @Override 290 @DoNotCall("Always throws UnsupportedOperationException") 291 public final void remove(Range<K> range) { 292 throw new UnsupportedOperationException(); 293 } 294 295 @Override 296 public ImmutableMap<Range<K>, V> asMapOfRanges() { 297 if (ranges.isEmpty()) { 298 return ImmutableMap.of(); 299 } 300 RegularImmutableSortedSet<Range<K>> rangeSet = 301 new RegularImmutableSortedSet<>(ranges, Range.<K>rangeLexOrdering()); 302 return new ImmutableSortedMap<>(rangeSet, values); 303 } 304 305 @Override 306 public ImmutableMap<Range<K>, V> asDescendingMapOfRanges() { 307 if (ranges.isEmpty()) { 308 return ImmutableMap.of(); 309 } 310 RegularImmutableSortedSet<Range<K>> rangeSet = 311 new RegularImmutableSortedSet<>(ranges.reverse(), Range.<K>rangeLexOrdering().reverse()); 312 return new ImmutableSortedMap<>(rangeSet, values.reverse()); 313 } 314 315 @Override 316 public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) { 317 if (checkNotNull(range).isEmpty()) { 318 return ImmutableRangeMap.of(); 319 } else if (ranges.isEmpty() || range.encloses(span())) { 320 return this; 321 } 322 int lowerIndex = 323 SortedLists.binarySearch( 324 ranges, 325 Range::upperBound, 326 range.lowerBound, 327 KeyPresentBehavior.FIRST_AFTER, 328 KeyAbsentBehavior.NEXT_HIGHER); 329 int upperIndex = 330 SortedLists.binarySearch( 331 ranges, 332 Range::lowerBound, 333 range.upperBound, 334 KeyPresentBehavior.ANY_PRESENT, 335 KeyAbsentBehavior.NEXT_HIGHER); 336 if (lowerIndex >= upperIndex) { 337 return ImmutableRangeMap.of(); 338 } 339 final int off = lowerIndex; 340 final int len = upperIndex - lowerIndex; 341 ImmutableList<Range<K>> subRanges = 342 new ImmutableList<Range<K>>() { 343 @Override 344 public int size() { 345 return len; 346 } 347 348 @Override 349 public Range<K> get(int index) { 350 checkElementIndex(index, len); 351 if (index == 0 || index == len - 1) { 352 return ranges.get(index + off).intersection(range); 353 } else { 354 return ranges.get(index + off); 355 } 356 } 357 358 @Override 359 boolean isPartialView() { 360 return true; 361 } 362 363 // redeclare to help optimizers with b/310253115 364 @SuppressWarnings("RedundantOverride") 365 @Override 366 @J2ktIncompatible // serialization 367 Object writeReplace() { 368 return super.writeReplace(); 369 } 370 }; 371 final ImmutableRangeMap<K, V> outer = this; 372 return new ImmutableRangeMap<K, V>(subRanges, values.subList(lowerIndex, upperIndex)) { 373 @Override 374 public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) { 375 if (range.isConnected(subRange)) { 376 return outer.subRangeMap(subRange.intersection(range)); 377 } else { 378 return ImmutableRangeMap.of(); 379 } 380 } 381 382 // redeclare to help optimizers with b/310253115 383 @SuppressWarnings("RedundantOverride") 384 @Override 385 @J2ktIncompatible // serialization 386 Object writeReplace() { 387 return super.writeReplace(); 388 } 389 }; 390 } 391 392 @Override 393 public int hashCode() { 394 return asMapOfRanges().hashCode(); 395 } 396 397 @Override 398 public boolean equals(@CheckForNull Object o) { 399 if (o instanceof RangeMap) { 400 RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; 401 return asMapOfRanges().equals(rangeMap.asMapOfRanges()); 402 } 403 return false; 404 } 405 406 @Override 407 public String toString() { 408 return asMapOfRanges().toString(); 409 } 410 411 /** 412 * This class is used to serialize ImmutableRangeMap instances. Serializes the {@link 413 * #asMapOfRanges()} form. 414 */ 415 private static class SerializedForm<K extends Comparable<?>, V> implements Serializable { 416 417 private final ImmutableMap<Range<K>, V> mapOfRanges; 418 419 SerializedForm(ImmutableMap<Range<K>, V> mapOfRanges) { 420 this.mapOfRanges = mapOfRanges; 421 } 422 423 Object readResolve() { 424 if (mapOfRanges.isEmpty()) { 425 return of(); 426 } else { 427 return createRangeMap(); 428 } 429 } 430 431 Object createRangeMap() { 432 Builder<K, V> builder = new Builder<>(); 433 for (Entry<Range<K>, V> entry : mapOfRanges.entrySet()) { 434 builder.put(entry.getKey(), entry.getValue()); 435 } 436 return builder.build(); 437 } 438 439 private static final long serialVersionUID = 0; 440 } 441 442 Object writeReplace() { 443 return new SerializedForm<>(asMapOfRanges()); 444 } 445 446 @J2ktIncompatible // java.io.ObjectInputStream 447 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 448 throw new InvalidObjectException("Use SerializedForm"); 449 } 450 451 private static final long serialVersionUID = 0; 452}