001 /* 002 * Copyright (C) 2010 Google Inc. 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 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkNotNull; 020 021 import com.google.common.annotations.Beta; 022 import com.google.common.annotations.GwtCompatible; 023 import com.google.common.annotations.GwtIncompatible; 024 import com.google.common.base.Function; 025 import com.google.common.base.Objects; 026 import com.google.common.base.Predicate; 027 import com.google.common.base.Predicates; 028 import com.google.common.collect.MapDifference.ValueDifference; 029 import com.google.common.collect.Maps.EntryTransformer; 030 import com.google.common.collect.Maps.MapDifferenceImpl; 031 import com.google.common.collect.Maps.TransformedEntriesMap; 032 import com.google.common.collect.Maps.ValueDifferenceImpl; 033 034 import java.util.Collections; 035 import java.util.Comparator; 036 import java.util.Map; 037 import java.util.Map.Entry; 038 import java.util.SortedMap; 039 040 import javax.annotation.Nullable; 041 042 /** 043 * Static utility methods pertaining to {@link SortedMap} instances. 044 * 045 * @author Louis Wasserman 046 * @since 8 047 */ 048 @Beta 049 @GwtCompatible 050 public final class SortedMaps { 051 private SortedMaps() {} 052 053 /** 054 * Returns a view of a sorted map where each value is transformed by a 055 * function. All other properties of the map, such as iteration order, are 056 * left intact. For example, the code: <pre> {@code 057 * 058 * SortedMap<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9); 059 * Function<Integer, Double> sqrt = 060 * new Function<Integer, Double>() { 061 * public Double apply(Integer in) { 062 * return Math.sqrt((int) in); 063 * } 064 * }; 065 * SortedMap<String, Double> transformed = 066 * Maps.transformSortedValues(map, sqrt); 067 * System.out.println(transformed);}</pre> 068 * 069 * ... prints {@code {a=2.0, b=3.0}}. 070 * 071 * <p>Changes in the underlying map are reflected in this view. Conversely, 072 * this view supports removal operations, and these are reflected in the 073 * underlying map. 074 * 075 * <p>It's acceptable for the underlying map to contain null keys, and even 076 * null values provided that the function is capable of accepting null input. 077 * The transformed map might contain null values, if the function sometimes 078 * gives a null result. 079 * 080 * <p>The returned map is not thread-safe or serializable, even if the 081 * underlying map is. 082 * 083 * <p>The function is applied lazily, invoked when needed. This is necessary 084 * for the returned map to be a view, but it means that the function will be 085 * applied many times for bulk operations like {@link Map#containsValue} and 086 * {@code Map.toString()}. For this to perform well, {@code function} should 087 * be fast. To avoid lazy evaluation when the returned map doesn't need to be 088 * a view, copy the returned map into a new map of your choosing. 089 */ 090 public static <K, V1, V2> SortedMap<K, V2> transformValues( 091 SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) { 092 checkNotNull(function); 093 EntryTransformer<K, V1, V2> transformer = 094 new EntryTransformer<K, V1, V2>() { 095 public V2 transformEntry(K key, V1 value) { 096 return function.apply(value); 097 } 098 }; 099 return transformEntries(fromMap, transformer); 100 } 101 102 /** 103 * Returns a view of a sorted map whose values are derived from the original 104 * sorted map's entries. In contrast to {@link #transformValues}, this 105 * method's entry-transformation logic may depend on the key as well as the 106 * value. 107 * 108 * <p>All other properties of the transformed map, such as iteration order, 109 * are left intact. For example, the code: <pre> {@code 110 * 111 * Map<String, Boolean> options = 112 * ImmutableSortedMap.of("verbose", true, "sort", false); 113 * EntryTransformer<String, Boolean, String> flagPrefixer = 114 * new EntryTransformer<String, Boolean, String>() { 115 * public String transformEntry(String key, Boolean value) { 116 * return value ? key : "yes" + key; 117 * } 118 * }; 119 * SortedMap<String, String> transformed = 120 * LabsMaps.transformSortedEntries(options, flagPrefixer); 121 * System.out.println(transformed);}</pre> 122 * 123 * ... prints {@code {sort=yessort, verbose=verbose}}. 124 * 125 * <p>Changes in the underlying map are reflected in this view. Conversely, 126 * this view supports removal operations, and these are reflected in the 127 * underlying map. 128 * 129 * <p>It's acceptable for the underlying map to contain null keys and null 130 * values provided that the transformer is capable of accepting null inputs. 131 * The transformed map might contain null values if the transformer sometimes 132 * gives a null result. 133 * 134 * <p>The returned map is not thread-safe or serializable, even if the 135 * underlying map is. 136 * 137 * <p>The transformer is applied lazily, invoked when needed. This is 138 * necessary for the returned map to be a view, but it means that the 139 * transformer will be applied many times for bulk operations like {@link 140 * Map#containsValue} and {@link Object#toString}. For this to perform well, 141 * {@code transformer} should be fast. To avoid lazy evaluation when the 142 * returned map doesn't need to be a view, copy the returned map into a new 143 * map of your choosing. 144 * 145 * <p><b>Warning:</b> This method assumes that for any instance {@code k} of 146 * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies 147 * that {@code k2} is also of type {@code K}. Using an {@code 148 * EntryTransformer} key type for which this may not hold, such as {@code 149 * ArrayList}, may risk a {@code ClassCastException} when calling methods on 150 * the transformed map. 151 */ 152 public static <K, V1, V2> SortedMap<K, V2> transformEntries( 153 final SortedMap<K, V1> fromMap, 154 EntryTransformer<? super K, ? super V1, V2> transformer) { 155 return new TransformedEntriesSortedMap<K, V1, V2>(fromMap, transformer); 156 } 157 158 static class TransformedEntriesSortedMap<K, V1, V2> 159 extends TransformedEntriesMap<K, V1, V2> implements SortedMap<K, V2> { 160 161 protected SortedMap<K, V1> fromMap() { 162 return (SortedMap<K, V1>) fromMap; 163 } 164 165 TransformedEntriesSortedMap(SortedMap<K, V1> fromMap, 166 EntryTransformer<? super K, ? super V1, V2> transformer) { 167 super(fromMap, transformer); 168 } 169 170 @Override public Comparator<? super K> comparator() { 171 return fromMap().comparator(); 172 } 173 174 @Override public K firstKey() { 175 return fromMap().firstKey(); 176 } 177 178 @Override public SortedMap<K, V2> headMap(K toKey) { 179 return transformEntries(fromMap().headMap(toKey), transformer); 180 } 181 182 @Override public K lastKey() { 183 return fromMap().lastKey(); 184 } 185 186 @Override public SortedMap<K, V2> subMap(K fromKey, K toKey) { 187 return transformEntries( 188 fromMap().subMap(fromKey, toKey), transformer); 189 } 190 191 @Override public SortedMap<K, V2> tailMap(K fromKey) { 192 return transformEntries(fromMap().tailMap(fromKey), transformer); 193 } 194 195 } 196 197 /** 198 * Computes the difference between two sorted maps, using the comparator of 199 * the left map, or {@code Ordering.natural()} if the left map uses the 200 * natural ordering of its elements. This difference is an immutable snapshot 201 * of the state of the maps at the time this method is called. It will never 202 * change, even if the maps change at a later time. 203 * 204 * <p>Since this method uses {@code TreeMap} instances internally, the keys of 205 * the right map must all compare as distinct according to the comparator 206 * of the left map. 207 * 208 * <p><b>Note:</b>If you only need to know whether two sorted maps have the 209 * same mappings, call {@code left.equals(right)} instead of this method. 210 * 211 * @param left the map to treat as the "left" map for purposes of comparison 212 * @param right the map to treat as the "right" map for purposes of comparison 213 * @return the difference between the two maps 214 */ 215 public static <K, V> SortedMapDifference<K, V> difference( 216 SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) { 217 Comparator<? super K> comparator = orNaturalOrder(left.comparator()); 218 SortedMap<K, V> onlyOnLeft = Maps.newTreeMap(comparator); 219 SortedMap<K, V> onlyOnRight = Maps.newTreeMap(comparator); 220 onlyOnRight.putAll(right); // will whittle it down 221 SortedMap<K, V> onBoth = Maps.newTreeMap(comparator); 222 SortedMap<K, MapDifference.ValueDifference<V>> differences = 223 Maps.newTreeMap(comparator); 224 boolean eq = true; 225 226 for (Entry<? extends K, ? extends V> entry : left.entrySet()) { 227 K leftKey = entry.getKey(); 228 V leftValue = entry.getValue(); 229 if (right.containsKey(leftKey)) { 230 V rightValue = onlyOnRight.remove(leftKey); 231 if (Objects.equal(leftValue, rightValue)) { 232 onBoth.put(leftKey, leftValue); 233 } else { 234 eq = false; 235 differences.put( 236 leftKey, new ValueDifferenceImpl<V>(leftValue, rightValue)); 237 } 238 } else { 239 eq = false; 240 onlyOnLeft.put(leftKey, leftValue); 241 } 242 } 243 244 boolean areEqual = eq && onlyOnRight.isEmpty(); 245 return sortedMapDifference( 246 areEqual, onlyOnLeft, onlyOnRight, onBoth, differences); 247 } 248 249 private static <K, V> SortedMapDifference<K, V> sortedMapDifference( 250 boolean areEqual, SortedMap<K, V> onlyOnLeft, SortedMap<K, V> onlyOnRight, 251 SortedMap<K, V> onBoth, SortedMap<K, ValueDifference<V>> differences) { 252 return new SortedMapDifferenceImpl<K, V>(areEqual, 253 Collections.unmodifiableSortedMap(onlyOnLeft), 254 Collections.unmodifiableSortedMap(onlyOnRight), 255 Collections.unmodifiableSortedMap(onBoth), 256 Collections.unmodifiableSortedMap(differences)); 257 } 258 259 static class SortedMapDifferenceImpl<K, V> extends MapDifferenceImpl<K, V> 260 implements SortedMapDifference<K, V> { 261 SortedMapDifferenceImpl(boolean areEqual, SortedMap<K, V> onlyOnLeft, 262 SortedMap<K, V> onlyOnRight, SortedMap<K, V> onBoth, 263 SortedMap<K, ValueDifference<V>> differences) { 264 super(areEqual, onlyOnLeft, onlyOnRight, onBoth, differences); 265 } 266 267 @Override public SortedMap<K, ValueDifference<V>> entriesDiffering() { 268 return (SortedMap<K, ValueDifference<V>>) super.entriesDiffering(); 269 } 270 271 @Override public SortedMap<K, V> entriesInCommon() { 272 return (SortedMap<K, V>) super.entriesInCommon(); 273 } 274 275 @Override public SortedMap<K, V> entriesOnlyOnLeft() { 276 return (SortedMap<K, V>) super.entriesOnlyOnLeft(); 277 } 278 279 @Override public SortedMap<K, V> entriesOnlyOnRight() { 280 return (SortedMap<K, V>) super.entriesOnlyOnRight(); 281 } 282 } 283 284 /** 285 * Returns the specified comparator if not null; otherwise returns {@code 286 * Ordering.natural()}. This method is an abomination of generics; the only 287 * purpose of this method is to contain the ugly type-casting in one place. 288 */ 289 @SuppressWarnings("unchecked") 290 static <E> Comparator<? super E> orNaturalOrder( 291 @Nullable Comparator<? super E> comparator) { 292 if (comparator != null) { // can't use ? : because of javac bug 5080917 293 return comparator; 294 } 295 return (Comparator<E>) Ordering.natural(); 296 } 297 298 /** 299 * Returns a sorted map containing the mappings in {@code unfiltered} whose 300 * keys satisfy a predicate. The returned map is a live view of {@code 301 * unfiltered}; changes to one affect the other. 302 * 303 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 304 * values()} views have iterators that don't support {@code remove()}, but all 305 * other methods are supported by the map and its views. When given a key that 306 * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} 307 * methods throw an {@link IllegalArgumentException}. 308 * 309 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 310 * on the filtered map or its views, only mappings whose keys satisfy the 311 * filter will be removed from the underlying map. 312 * 313 * <p>The returned map isn't threadsafe or serializable, even if {@code 314 * unfiltered} is. 315 * 316 * <p>Many of the filtered map's methods, such as {@code size()}, 317 * iterate across every key/value mapping in the underlying map and determine 318 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 319 * faster to copy the filtered map and use the copy. 320 * 321 * <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with 322 * equals</i>, as documented at {@link Predicate#apply}. Do not provide a 323 * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is 324 * inconsistent with equals. 325 */ 326 @GwtIncompatible("untested") 327 public static <K, V> SortedMap<K, V> filterKeys( 328 SortedMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { 329 // TODO: Return a subclass of Maps.FilteredKeyMap for slightly better 330 // performance. 331 checkNotNull(keyPredicate); 332 Predicate<Entry<K, V>> entryPredicate = new Predicate<Entry<K, V>>() { 333 public boolean apply(Entry<K, V> input) { 334 return keyPredicate.apply(input.getKey()); 335 } 336 }; 337 return filterEntries(unfiltered, entryPredicate); 338 } 339 340 /** 341 * Returns a sorted map containing the mappings in {@code unfiltered} whose 342 * values satisfy a predicate. The returned map is a live view of {@code 343 * unfiltered}; changes to one affect the other. 344 * 345 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 346 * values()} views have iterators that don't support {@code remove()}, but all 347 * other methods are supported by the map and its views. When given a value 348 * that doesn't satisfy the predicate, the map's {@code put()}, {@code 349 * putAll()}, and {@link Entry#setValue} methods throw an {@link 350 * IllegalArgumentException}. 351 * 352 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 353 * on the filtered map or its views, only mappings whose values satisfy the 354 * filter will be removed from the underlying map. 355 * 356 * <p>The returned map isn't threadsafe or serializable, even if {@code 357 * unfiltered} is. 358 * 359 * <p>Many of the filtered map's methods, such as {@code size()}, 360 * iterate across every key/value mapping in the underlying map and determine 361 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 362 * faster to copy the filtered map and use the copy. 363 * 364 * <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with 365 * equals</i>, as documented at {@link Predicate#apply}. Do not provide a 366 * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is 367 * inconsistent with equals. 368 */ 369 @GwtIncompatible("untested") 370 public static <K, V> SortedMap<K, V> filterValues( 371 SortedMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { 372 checkNotNull(valuePredicate); 373 Predicate<Entry<K, V>> entryPredicate = 374 new Predicate<Entry<K, V>>() { 375 public boolean apply(Entry<K, V> input) { 376 return valuePredicate.apply(input.getValue()); 377 } 378 }; 379 return filterEntries(unfiltered, entryPredicate); 380 } 381 382 /** 383 * Returns a sorted map containing the mappings in {@code unfiltered} that 384 * satisfy a predicate. The returned map is a live view of {@code unfiltered}; 385 * changes to one affect the other. 386 * 387 * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code 388 * values()} views have iterators that don't support {@code remove()}, but all 389 * other methods are supported by the map and its views. When given a 390 * key/value pair that doesn't satisfy the predicate, the map's {@code put()} 391 * and {@code putAll()} methods throw an {@link IllegalArgumentException}. 392 * Similarly, the map's entries have a {@link Entry#setValue} method that 393 * throws an {@link IllegalArgumentException} when the existing key and the 394 * provided value don't satisfy the predicate. 395 * 396 * <p>When methods such as {@code removeAll()} and {@code clear()} are called 397 * on the filtered map or its views, only mappings that satisfy the filter 398 * will be removed from the underlying map. 399 * 400 * <p>The returned map isn't threadsafe or serializable, even if {@code 401 * unfiltered} is. 402 * 403 * <p>Many of the filtered map's methods, such as {@code size()}, 404 * iterate across every key/value mapping in the underlying map and determine 405 * which satisfy the filter. When a live view is <i>not</i> needed, it may be 406 * faster to copy the filtered map and use the copy. 407 * 408 * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with 409 * equals</i>, as documented at {@link Predicate#apply}. 410 */ 411 @GwtIncompatible("untested") 412 public static <K, V> SortedMap<K, V> filterEntries( 413 SortedMap<K, V> unfiltered, 414 Predicate<? super Entry<K, V>> entryPredicate) { 415 checkNotNull(entryPredicate); 416 return (unfiltered instanceof FilteredSortedMap) 417 ? filterFiltered((FilteredSortedMap<K, V>) unfiltered, entryPredicate) 418 : new FilteredSortedMap<K, V>(checkNotNull(unfiltered), entryPredicate); 419 } 420 421 /** 422 * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when 423 * filtering a filtered sorted map. 424 */ 425 private static <K, V> SortedMap<K, V> filterFiltered( 426 FilteredSortedMap<K, V> map, 427 Predicate<? super Entry<K, V>> entryPredicate) { 428 Predicate<Entry<K, V>> predicate 429 = Predicates.and(map.predicate, entryPredicate); 430 return new FilteredSortedMap<K, V>(map.sortedMap(), predicate); 431 } 432 433 private static class FilteredSortedMap<K, V> 434 extends Maps.FilteredEntryMap<K, V> implements SortedMap<K, V> { 435 436 FilteredSortedMap(SortedMap<K, V> unfiltered, 437 Predicate<? super Entry<K, V>> entryPredicate) { 438 super(unfiltered, entryPredicate); 439 } 440 441 SortedMap<K, V> sortedMap() { 442 return (SortedMap<K, V>) unfiltered; 443 } 444 445 @Override public Comparator<? super K> comparator() { 446 return sortedMap().comparator(); 447 } 448 449 @Override public K firstKey() { 450 // correctly throws NoSuchElementException when filtered map is empty. 451 return keySet().iterator().next(); 452 } 453 454 @Override public K lastKey() { 455 SortedMap<K, V> headMap = sortedMap(); 456 while (true) { 457 // correctly throws NoSuchElementException when filtered map is empty. 458 K key = headMap.lastKey(); 459 if (apply(key, unfiltered.get(key))) { 460 return key; 461 } 462 headMap = sortedMap().headMap(key); 463 } 464 } 465 466 @Override public SortedMap<K, V> headMap(K toKey) { 467 return new FilteredSortedMap<K, V>(sortedMap().headMap(toKey), predicate); 468 } 469 470 @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { 471 return new FilteredSortedMap<K, V>( 472 sortedMap().subMap(fromKey, toKey), predicate); 473 } 474 475 @Override public SortedMap<K, V> tailMap(K fromKey) { 476 return new FilteredSortedMap<K, V>( 477 sortedMap().tailMap(fromKey), predicate); 478 } 479 } 480 }