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