View Javadoc
1   /*
2    * Copyright (C) 2007 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  
17  package com.google.common.collect;
18  
19  import static com.google.common.collect.Maps.transformEntries;
20  import static com.google.common.collect.Maps.transformValues;
21  import static com.google.common.collect.testing.Helpers.mapEntry;
22  import static com.google.common.truth.Truth.assertThat;
23  
24  import com.google.common.annotations.GwtCompatible;
25  import com.google.common.base.Converter;
26  import com.google.common.base.Equivalence;
27  import com.google.common.base.Function;
28  import com.google.common.base.Functions;
29  import com.google.common.base.Predicate;
30  import com.google.common.base.Predicates;
31  import com.google.common.collect.Maps.EntryTransformer;
32  import com.google.common.collect.Maps.ValueDifferenceImpl;
33  import com.google.common.collect.SetsTest.Derived;
34  import com.google.common.testing.EqualsTester;
35  import com.google.common.testing.SerializableTester;
36  
37  import junit.framework.TestCase;
38  
39  import java.util.Arrays;
40  import java.util.Collections;
41  import java.util.Comparator;
42  import java.util.EnumMap;
43  import java.util.HashMap;
44  import java.util.IdentityHashMap;
45  import java.util.Iterator;
46  import java.util.LinkedHashMap;
47  import java.util.List;
48  import java.util.Map;
49  import java.util.Map.Entry;
50  import java.util.Set;
51  import java.util.SortedMap;
52  import java.util.SortedSet;
53  import java.util.TreeMap;
54  import java.util.concurrent.ConcurrentMap;
55  
56  /**
57   * Unit test for {@code Maps}.
58   *
59   * @author Kevin Bourrillion
60   * @author Mike Bostock
61   * @author Jared Levy
62   */
63  @GwtCompatible(emulated = true)
64  public class MapsTest extends TestCase {
65  
66    private static final Comparator<Integer> SOME_COMPARATOR =
67        Collections.reverseOrder();
68  
69    public void testHashMap() {
70      HashMap<Integer, Integer> map = Maps.newHashMap();
71      assertEquals(Collections.emptyMap(), map);
72    }
73  
74    public void testHashMapWithInitialMap() {
75      Map<String, Integer> original = new TreeMap<String, Integer>();
76      original.put("a", 1);
77      original.put("b", 2);
78      original.put("c", 3);
79      HashMap<String, Integer> map = Maps.newHashMap(original);
80      assertEquals(original, map);
81    }
82  
83    public void testHashMapGeneralizesTypes() {
84      Map<String, Integer> original = new TreeMap<String, Integer>();
85      original.put("a", 1);
86      original.put("b", 2);
87      original.put("c", 3);
88      HashMap<Object, Object> map =
89          Maps.newHashMap((Map<? extends Object, ? extends Object>) original);
90      assertEquals(original, map);
91    }
92  
93    public void testCapacityForNegativeSizeFails() {
94      try {
95        Maps.capacity(-1);
96        fail("Negative expected size must result in IllegalArgumentException");
97      } catch (IllegalArgumentException ex) {
98      }
99    }
100 
101   public void testCapacityForLargeSizes() {
102     int[] largeExpectedSizes = new int[] {
103       Integer.MAX_VALUE / 2 - 1,
104       Integer.MAX_VALUE / 2,
105       Integer.MAX_VALUE / 2 + 1,
106       Integer.MAX_VALUE - 1,
107       Integer.MAX_VALUE};
108     for (int expectedSize : largeExpectedSizes) {
109       int capacity = Maps.capacity(expectedSize);
110       assertTrue(
111           "capacity (" + capacity + ") must be >= expectedSize (" + expectedSize + ")",
112           capacity >= expectedSize);
113     }
114   }
115 
116   public void testLinkedHashMap() {
117     LinkedHashMap<Integer, Integer> map = Maps.newLinkedHashMap();
118     assertEquals(Collections.emptyMap(), map);
119   }
120 
121   @SuppressWarnings("serial")
122   public void testLinkedHashMapWithInitialMap() {
123     Map<String, String> map = new LinkedHashMap<String, String>() {{
124       put("Hello", "World");
125       put("first", "second");
126       put("polygene", "lubricants");
127       put("alpha", "betical");
128     }};
129 
130     LinkedHashMap<String, String> copy = Maps.newLinkedHashMap(map);
131 
132     Iterator<Entry<String, String>> iter = copy.entrySet().iterator();
133     assertTrue(iter.hasNext());
134     Entry<String, String> entry = iter.next();
135     assertEquals("Hello", entry.getKey());
136     assertEquals("World", entry.getValue());
137     assertTrue(iter.hasNext());
138 
139     entry = iter.next();
140     assertEquals("first", entry.getKey());
141     assertEquals("second", entry.getValue());
142     assertTrue(iter.hasNext());
143 
144     entry = iter.next();
145     assertEquals("polygene", entry.getKey());
146     assertEquals("lubricants", entry.getValue());
147     assertTrue(iter.hasNext());
148 
149     entry = iter.next();
150     assertEquals("alpha", entry.getKey());
151     assertEquals("betical", entry.getValue());
152     assertFalse(iter.hasNext());
153   }
154 
155   public void testLinkedHashMapGeneralizesTypes() {
156     Map<String, Integer> original = new LinkedHashMap<String, Integer>();
157     original.put("a", 1);
158     original.put("b", 2);
159     original.put("c", 3);
160     HashMap<Object, Object> map
161         = Maps.<Object, Object>newLinkedHashMap(original);
162     assertEquals(original, map);
163   }
164 
165   public void testIdentityHashMap() {
166     IdentityHashMap<Integer, Integer> map = Maps.newIdentityHashMap();
167     assertEquals(Collections.emptyMap(), map);
168   }
169 
170   public void testConcurrentMap() {
171     ConcurrentMap<Integer, Integer> map = Maps.newConcurrentMap();
172     assertEquals(Collections.emptyMap(), map);
173   }
174 
175   public void testTreeMap() {
176     TreeMap<Integer, Integer> map = Maps.newTreeMap();
177     assertEquals(Collections.emptyMap(), map);
178     assertNull(map.comparator());
179   }
180 
181   public void testTreeMapDerived() {
182     TreeMap<Derived, Integer> map = Maps.newTreeMap();
183     assertEquals(Collections.emptyMap(), map);
184     map.put(new Derived("foo"), 1);
185     map.put(new Derived("bar"), 2);
186     assertThat(map.keySet()).has().exactly(
187         new Derived("bar"), new Derived("foo")).inOrder();
188     assertThat(map.values()).has().exactly(2, 1).inOrder();
189     assertNull(map.comparator());
190   }
191 
192   public void testTreeMapNonGeneric() {
193     TreeMap<LegacyComparable, Integer> map = Maps.newTreeMap();
194     assertEquals(Collections.emptyMap(), map);
195     map.put(new LegacyComparable("foo"), 1);
196     map.put(new LegacyComparable("bar"), 2);
197     assertThat(map.keySet()).has().exactly(
198         new LegacyComparable("bar"), new LegacyComparable("foo")).inOrder();
199     assertThat(map.values()).has().exactly(2, 1).inOrder();
200     assertNull(map.comparator());
201   }
202 
203   public void testTreeMapWithComparator() {
204     TreeMap<Integer, Integer> map = Maps.newTreeMap(SOME_COMPARATOR);
205     assertEquals(Collections.emptyMap(), map);
206     assertSame(SOME_COMPARATOR, map.comparator());
207   }
208 
209   public void testTreeMapWithInitialMap() {
210     SortedMap<Integer, Integer> map = Maps.newTreeMap();
211     map.put(5, 10);
212     map.put(3, 20);
213     map.put(1, 30);
214     TreeMap<Integer, Integer> copy = Maps.newTreeMap(map);
215     assertEquals(copy, map);
216     assertSame(copy.comparator(), map.comparator());
217   }
218 
219   public enum SomeEnum { SOME_INSTANCE }
220 
221   public void testEnumMap() {
222     EnumMap<SomeEnum, Integer> map = Maps.newEnumMap(SomeEnum.class);
223     assertEquals(Collections.emptyMap(), map);
224     map.put(SomeEnum.SOME_INSTANCE, 0);
225     assertEquals(Collections.singletonMap(SomeEnum.SOME_INSTANCE, 0), map);
226   }
227 
228   public void testEnumMapNullClass() {
229     try {
230       Maps.<SomeEnum, Long>newEnumMap((Class<MapsTest.SomeEnum>) null);
231       fail("no exception thrown");
232     } catch (NullPointerException expected) {
233     }
234   }
235 
236   public void testEnumMapWithInitialEnumMap() {
237     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
238     original.put(SomeEnum.SOME_INSTANCE, 0);
239     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
240     assertEquals(original, copy);
241   }
242 
243   public void testEnumMapWithInitialEmptyEnumMap() {
244     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
245     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
246     assertEquals(original, copy);
247     assertNotSame(original, copy);
248   }
249 
250   public void testEnumMapWithInitialMap() {
251     HashMap<SomeEnum, Integer> original = Maps.newHashMap();
252     original.put(SomeEnum.SOME_INSTANCE, 0);
253     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
254     assertEquals(original, copy);
255   }
256 
257   public void testEnumMapWithInitialEmptyMap() {
258     Map<SomeEnum, Integer> original = Maps.newHashMap();
259     try {
260       Maps.newEnumMap(original);
261       fail("Empty map must result in an IllegalArgumentException");
262     } catch (IllegalArgumentException expected) {}
263   }
264 
265   public void testToStringImplWithNullKeys() throws Exception {
266     Map<String, String> hashmap = Maps.newHashMap();
267     hashmap.put("foo", "bar");
268     hashmap.put(null, "baz");
269 
270     assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
271   }
272 
273   public void testToStringImplWithNullValues() throws Exception {
274     Map<String, String> hashmap = Maps.newHashMap();
275     hashmap.put("foo", "bar");
276     hashmap.put("baz", null);
277 
278     assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
279   }
280 
281   private static final Map<Integer, Integer> EMPTY
282       = Collections.emptyMap();
283   private static final Map<Integer, Integer> SINGLETON
284       = Collections.singletonMap(1, 2);
285 
286   public void testMapDifferenceEmptyEmpty() {
287     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, EMPTY);
288     assertTrue(diff.areEqual());
289     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
290     assertEquals(EMPTY, diff.entriesOnlyOnRight());
291     assertEquals(EMPTY, diff.entriesInCommon());
292     assertEquals(EMPTY, diff.entriesDiffering());
293     assertEquals("equal", diff.toString());
294   }
295 
296   public void testMapDifferenceEmptySingleton() {
297     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, SINGLETON);
298     assertFalse(diff.areEqual());
299     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
300     assertEquals(SINGLETON, diff.entriesOnlyOnRight());
301     assertEquals(EMPTY, diff.entriesInCommon());
302     assertEquals(EMPTY, diff.entriesDiffering());
303     assertEquals("not equal: only on right={1=2}", diff.toString());
304   }
305 
306   public void testMapDifferenceSingletonEmpty() {
307     MapDifference<Integer, Integer> diff = Maps.difference(SINGLETON, EMPTY);
308     assertFalse(diff.areEqual());
309     assertEquals(SINGLETON, diff.entriesOnlyOnLeft());
310     assertEquals(EMPTY, diff.entriesOnlyOnRight());
311     assertEquals(EMPTY, diff.entriesInCommon());
312     assertEquals(EMPTY, diff.entriesDiffering());
313     assertEquals("not equal: only on left={1=2}", diff.toString());
314   }
315 
316   public void testMapDifferenceTypical() {
317     Map<Integer, String> left = ImmutableMap.of(
318         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
319     Map<Integer, String> right = ImmutableMap.of(
320         1, "a", 3, "f", 5, "g", 6, "z");
321 
322     MapDifference<Integer, String> diff1 = Maps.difference(left, right);
323     assertFalse(diff1.areEqual());
324     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
325     assertEquals(ImmutableMap.of(6, "z"), diff1.entriesOnlyOnRight());
326     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
327     assertEquals(ImmutableMap.of(3,
328         ValueDifferenceImpl.create("c", "f"), 5,
329         ValueDifferenceImpl.create("e", "g")),
330         diff1.entriesDiffering());
331     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=z}: "
332         + "value differences={3=(c, f), 5=(e, g)}", diff1.toString());
333 
334     MapDifference<Integer, String> diff2 = Maps.difference(right, left);
335     assertFalse(diff2.areEqual());
336     assertEquals(ImmutableMap.of(6, "z"), diff2.entriesOnlyOnLeft());
337     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
338     assertEquals(ImmutableMap.of(1, "a"), diff2.entriesInCommon());
339     assertEquals(ImmutableMap.of(3,
340         ValueDifferenceImpl.create("f", "c"), 5,
341         ValueDifferenceImpl.create("g", "e")),
342         diff2.entriesDiffering());
343     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
344         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
345   }
346 
347   public void testMapDifferenceEquals() {
348     Map<Integer, String> left = ImmutableMap.of(
349         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
350     Map<Integer, String> right = ImmutableMap.of(
351         1, "a", 3, "f", 5, "g", 6, "z");
352     Map<Integer, String> right2 = ImmutableMap.of(
353         1, "a", 3, "h", 5, "g", 6, "z");
354     MapDifference<Integer, String> original = Maps.difference(left, right);
355     MapDifference<Integer, String> same = Maps.difference(left, right);
356     MapDifference<Integer, String> reverse = Maps.difference(right, left);
357     MapDifference<Integer, String> diff2 = Maps.difference(left, right2);
358 
359     new EqualsTester()
360         .addEqualityGroup(original, same)
361         .addEqualityGroup(reverse)
362         .addEqualityGroup(diff2)
363         .testEquals();
364   }
365 
366   public void testMapDifferencePredicateTypical() {
367     Map<Integer, String> left = ImmutableMap.of(
368         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
369     Map<Integer, String> right = ImmutableMap.of(
370         1, "A", 3, "F", 5, "G", 6, "Z");
371 
372     // TODO(kevinb): replace with Ascii.caseInsensitiveEquivalence() when it
373     // exists
374     Equivalence<String> caseInsensitiveEquivalence = Equivalence.equals().onResultOf(
375         new Function<String, String>() {
376           @Override public String apply(String input) {
377             return input.toLowerCase();
378           }
379         });
380 
381     MapDifference<Integer, String> diff1 = Maps.difference(left, right,
382         caseInsensitiveEquivalence);
383     assertFalse(diff1.areEqual());
384     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
385     assertEquals(ImmutableMap.of(6, "Z"), diff1.entriesOnlyOnRight());
386     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
387     assertEquals(ImmutableMap.of(3,
388         ValueDifferenceImpl.create("c", "F"), 5,
389         ValueDifferenceImpl.create("e", "G")),
390         diff1.entriesDiffering());
391     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=Z}: "
392         + "value differences={3=(c, F), 5=(e, G)}", diff1.toString());
393 
394     MapDifference<Integer, String> diff2 = Maps.difference(right, left,
395         caseInsensitiveEquivalence);
396     assertFalse(diff2.areEqual());
397     assertEquals(ImmutableMap.of(6, "Z"), diff2.entriesOnlyOnLeft());
398     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
399     assertEquals(ImmutableMap.of(1, "A"), diff2.entriesInCommon());
400     assertEquals(ImmutableMap.of(3,
401         ValueDifferenceImpl.create("F", "c"), 5,
402         ValueDifferenceImpl.create("G", "e")),
403         diff2.entriesDiffering());
404     assertEquals("not equal: only on left={6=Z}: only on right={2=b, 4=d}: "
405         + "value differences={3=(F, c), 5=(G, e)}", diff2.toString());
406   }
407 
408   private static final SortedMap<Integer, Integer> SORTED_EMPTY = Maps.newTreeMap();
409   private static final SortedMap<Integer, Integer> SORTED_SINGLETON =
410       ImmutableSortedMap.of(1, 2);
411 
412   public void testMapDifferenceOfSortedMapIsSorted() {
413     Map<Integer, Integer> map = SORTED_SINGLETON;
414     MapDifference<Integer, Integer> difference = Maps.difference(map, EMPTY);
415     assertTrue(difference instanceof SortedMapDifference);
416   }
417 
418   public void testSortedMapDifferenceEmptyEmpty() {
419     SortedMapDifference<Integer, Integer> diff =
420         Maps.difference(SORTED_EMPTY, SORTED_EMPTY);
421     assertTrue(diff.areEqual());
422     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
423     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
424     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
425     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
426     assertEquals("equal", diff.toString());
427   }
428 
429   public void testSortedMapDifferenceEmptySingleton() {
430     SortedMapDifference<Integer, Integer> diff =
431         Maps.difference(SORTED_EMPTY, SORTED_SINGLETON);
432     assertFalse(diff.areEqual());
433     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
434     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnRight());
435     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
436     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
437     assertEquals("not equal: only on right={1=2}", diff.toString());
438   }
439 
440   public void testSortedMapDifferenceSingletonEmpty() {
441     SortedMapDifference<Integer, Integer> diff =
442         Maps.difference(SORTED_SINGLETON, SORTED_EMPTY);
443     assertFalse(diff.areEqual());
444     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnLeft());
445     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
446     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
447     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
448     assertEquals("not equal: only on left={1=2}", diff.toString());
449   }
450 
451   public void testSortedMapDifferenceTypical() {
452     SortedMap<Integer, String> left =
453         ImmutableSortedMap.<Integer, String>reverseOrder()
454         .put(1, "a").put(2, "b").put(3, "c").put(4, "d").put(5, "e")
455         .build();
456 
457     SortedMap<Integer, String> right =
458         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
459 
460     SortedMapDifference<Integer, String> diff1 =
461         Maps.difference(left, right);
462     assertFalse(diff1.areEqual());
463     assertThat(diff1.entriesOnlyOnLeft().entrySet()).has().exactly(
464         Maps.immutableEntry(4, "d"), Maps.immutableEntry(2, "b")).inOrder();
465     assertThat(diff1.entriesOnlyOnRight().entrySet()).has().item(
466         Maps.immutableEntry(6, "z"));
467     assertThat(diff1.entriesInCommon().entrySet()).has().item(
468         Maps.immutableEntry(1, "a"));
469     assertThat(diff1.entriesDiffering().entrySet()).has().exactly(
470         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")),
471         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f"))).inOrder();
472     assertEquals("not equal: only on left={4=d, 2=b}: only on right={6=z}: "
473         + "value differences={5=(e, g), 3=(c, f)}", diff1.toString());
474 
475     SortedMapDifference<Integer, String> diff2 =
476         Maps.difference(right, left);
477     assertFalse(diff2.areEqual());
478     assertThat(diff2.entriesOnlyOnLeft().entrySet()).has().item(
479         Maps.immutableEntry(6, "z"));
480     assertThat(diff2.entriesOnlyOnRight().entrySet()).has().exactly(
481         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
482     assertThat(diff1.entriesInCommon().entrySet()).has().item(
483         Maps.immutableEntry(1, "a"));
484     assertEquals(ImmutableMap.of(
485             3, ValueDifferenceImpl.create("f", "c"),
486             5, ValueDifferenceImpl.create("g", "e")),
487         diff2.entriesDiffering());
488     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
489         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
490   }
491 
492   public void testSortedMapDifferenceImmutable() {
493     SortedMap<Integer, String> left = Maps.newTreeMap(
494         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e"));
495     SortedMap<Integer, String> right =
496         Maps.newTreeMap(ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z"));
497 
498     SortedMapDifference<Integer, String> diff1 =
499         Maps.difference(left, right);
500     left.put(6, "z");
501     assertFalse(diff1.areEqual());
502     assertThat(diff1.entriesOnlyOnLeft().entrySet()).has().exactly(
503         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
504     assertThat(diff1.entriesOnlyOnRight().entrySet()).has().item(
505         Maps.immutableEntry(6, "z"));
506     assertThat(diff1.entriesInCommon().entrySet()).has().item(
507         Maps.immutableEntry(1, "a"));
508     assertThat(diff1.entriesDiffering().entrySet()).has().exactly(
509         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")),
510         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g"))).inOrder();
511     try {
512       diff1.entriesInCommon().put(7, "x");
513       fail();
514     } catch (UnsupportedOperationException expected) {
515     }
516     try {
517       diff1.entriesOnlyOnLeft().put(7, "x");
518       fail();
519     } catch (UnsupportedOperationException expected) {
520     }
521     try {
522       diff1.entriesOnlyOnRight().put(7, "x");
523       fail();
524     } catch (UnsupportedOperationException expected) {
525     }
526   }
527 
528   public void testSortedMapDifferenceEquals() {
529     SortedMap<Integer, String> left =
530         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
531     SortedMap<Integer, String> right =
532         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
533     SortedMap<Integer, String> right2 =
534         ImmutableSortedMap.of(1, "a", 3, "h", 5, "g", 6, "z");
535     SortedMapDifference<Integer, String> original =
536         Maps.difference(left, right);
537     SortedMapDifference<Integer, String> same =
538         Maps.difference(left, right);
539     SortedMapDifference<Integer, String> reverse =
540         Maps.difference(right, left);
541     SortedMapDifference<Integer, String> diff2 =
542         Maps.difference(left, right2);
543 
544     new EqualsTester()
545         .addEqualityGroup(original, same)
546         .addEqualityGroup(reverse)
547         .addEqualityGroup(diff2)
548         .testEquals();
549   }
550 
551   private static final Function<String, Integer> LENGTH_FUNCTION =
552       new Function<String, Integer>() {
553         @Override
554         public Integer apply(String input) {
555           return input.length();
556         }
557       };
558 
559   public void testAsMap() {
560     Set<String> strings = ImmutableSet.of("one", "two", "three");
561     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
562     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
563     assertEquals(Integer.valueOf(5), map.get("three"));
564     assertNull(map.get("five"));
565     assertThat(map.entrySet()).has().exactly(
566         mapEntry("one", 3),
567         mapEntry("two", 3),
568         mapEntry("three", 5)).inOrder();
569   }
570 
571   public void testAsMapReadsThrough() {
572     Set<String> strings = Sets.newLinkedHashSet();
573     Collections.addAll(strings, "one", "two", "three");
574     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
575     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
576     assertNull(map.get("four"));
577     strings.add("four");
578     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5, "four", 4), map);
579     assertEquals(Integer.valueOf(4), map.get("four"));
580   }
581 
582   public void testAsMapWritesThrough() {
583     Set<String> strings = Sets.newLinkedHashSet();
584     Collections.addAll(strings, "one", "two", "three");
585     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
586     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
587     assertEquals(Integer.valueOf(3), map.remove("two"));
588     assertThat(strings).has().exactly("one", "three").inOrder();
589   }
590 
591   public void testAsMapEmpty() {
592     Set<String> strings = ImmutableSet.of();
593     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
594     assertThat(map.entrySet()).isEmpty();
595     assertTrue(map.isEmpty());
596     assertNull(map.get("five"));
597   }
598 
599   private static class NonNavigableSortedSet
600       extends ForwardingSortedSet<String> {
601     private final SortedSet<String> delegate = Sets.newTreeSet();
602 
603     @Override
604     protected SortedSet<String> delegate() {
605       return delegate;
606     }
607   }
608 
609   public void testAsMapReturnsSortedMapForSortedSetInput() {
610     Set<String> set = new NonNavigableSortedSet();
611     assertTrue(Maps.asMap(set, Functions.identity()) instanceof SortedMap);
612   }
613 
614   public void testAsMapSorted() {
615     SortedSet<String> strings = new NonNavigableSortedSet();
616     Collections.addAll(strings, "one", "two", "three");
617     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
618     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
619     assertEquals(Integer.valueOf(5), map.get("three"));
620     assertNull(map.get("five"));
621     assertThat(map.entrySet()).has().exactly(
622         mapEntry("one", 3),
623         mapEntry("three", 5),
624         mapEntry("two", 3)).inOrder();
625     assertThat(map.tailMap("onea").entrySet()).has().exactly(
626         mapEntry("three", 5),
627         mapEntry("two", 3)).inOrder();
628     assertThat(map.subMap("one", "two").entrySet()).has().exactly(
629         mapEntry("one", 3),
630         mapEntry("three", 5)).inOrder();
631   }
632 
633   public void testAsMapSortedReadsThrough() {
634     SortedSet<String> strings = new NonNavigableSortedSet();
635     Collections.addAll(strings, "one", "two", "three");
636     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
637     assertNull(map.comparator());
638     assertEquals(ImmutableSortedMap.of("one", 3, "two", 3, "three", 5), map);
639     assertNull(map.get("four"));
640     strings.add("four");
641     assertEquals(
642         ImmutableSortedMap.of("one", 3, "two", 3, "three", 5, "four", 4),
643         map);
644     assertEquals(Integer.valueOf(4), map.get("four"));
645     SortedMap<String, Integer> headMap = map.headMap("two");
646     assertEquals(
647         ImmutableSortedMap.of("four", 4, "one", 3, "three", 5),
648         headMap);
649     strings.add("five");
650     strings.remove("one");
651     assertEquals(
652         ImmutableSortedMap.of("five", 4, "four", 4, "three", 5),
653         headMap);
654     assertThat(map.entrySet()).has().exactly(
655         mapEntry("five", 4),
656         mapEntry("four", 4),
657         mapEntry("three", 5),
658         mapEntry("two", 3)).inOrder();
659   }
660 
661   public void testAsMapSortedWritesThrough() {
662     SortedSet<String> strings = new NonNavigableSortedSet();
663     Collections.addAll(strings, "one", "two", "three");
664     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
665     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
666     assertEquals(Integer.valueOf(3), map.remove("two"));
667     assertThat(strings).has().exactly("one", "three").inOrder();
668   }
669 
670   public void testAsMapSortedSubViewKeySetsDoNotSupportAdd() {
671     SortedMap<String, Integer> map = Maps.asMap(
672         new NonNavigableSortedSet(), LENGTH_FUNCTION);
673     try {
674       map.subMap("a", "z").keySet().add("a");
675       fail();
676     } catch (UnsupportedOperationException expected) {
677     }
678     try {
679       map.tailMap("a").keySet().add("a");
680       fail();
681     } catch (UnsupportedOperationException expected) {
682     }
683     try {
684       map.headMap("r").keySet().add("a");
685       fail();
686     } catch (UnsupportedOperationException expected) {
687     }
688     try {
689       map.headMap("r").tailMap("m").keySet().add("a");
690       fail();
691     } catch (UnsupportedOperationException expected) {
692     }
693   }
694 
695   public void testAsMapSortedEmpty() {
696     SortedSet<String> strings = new NonNavigableSortedSet();
697     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
698     assertThat(map.entrySet()).isEmpty();
699     assertTrue(map.isEmpty());
700     assertNull(map.get("five"));
701   }
702 
703   public void testToMap() {
704     Iterable<String> strings = ImmutableList.of("one", "two", "three");
705     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
706     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
707     assertThat(map.entrySet()).has().exactly(
708         mapEntry("one", 3),
709         mapEntry("two", 3),
710         mapEntry("three", 5)).inOrder();
711   }
712 
713   public void testToMapIterator() {
714     Iterator<String> strings = ImmutableList.of("one", "two", "three").iterator();
715     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
716     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
717     assertThat(map.entrySet()).has().exactly(
718         mapEntry("one", 3),
719         mapEntry("two", 3),
720         mapEntry("three", 5)).inOrder();
721   }
722 
723   public void testToMapWithDuplicateKeys() {
724     Iterable<String> strings = ImmutableList.of("one", "two", "three", "two", "one");
725     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
726     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
727     assertThat(map.entrySet()).has().exactly(
728         mapEntry("one", 3),
729         mapEntry("two", 3),
730         mapEntry("three", 5)).inOrder();
731   }
732 
733   public void testToMapWithNullKeys() {
734     Iterable<String> strings = Arrays.asList("one", null, "three");
735     try {
736       Maps.toMap(strings, Functions.constant("foo"));
737       fail();
738     } catch (NullPointerException expected) {
739     }
740   }
741 
742   public void testToMapWithNullValues() {
743     Iterable<String> strings = ImmutableList.of("one", "two", "three");
744     try {
745       Maps.toMap(strings, Functions.constant(null));
746       fail();
747     } catch (NullPointerException expected) {
748     }
749   }
750 
751   private static final BiMap<Integer, String> INT_TO_STRING_MAP =
752       new ImmutableBiMap.Builder<Integer, String>()
753           .put(1, "one")
754           .put(2, "two")
755           .put(3, "three")
756           .build();
757 
758   public void testUniqueIndexCollection() {
759     ImmutableMap<Integer, String> outputMap =
760         Maps.uniqueIndex(INT_TO_STRING_MAP.values(),
761             Functions.forMap(INT_TO_STRING_MAP.inverse()));
762     assertEquals(INT_TO_STRING_MAP, outputMap);
763   }
764 
765   public void testUniqueIndexIterable() {
766     ImmutableMap<Integer, String> outputMap =
767         Maps.uniqueIndex(new Iterable<String>() {
768           @Override
769           public Iterator<String> iterator() {
770             return INT_TO_STRING_MAP.values().iterator();
771           }
772         },
773         Functions.forMap(INT_TO_STRING_MAP.inverse()));
774     assertEquals(INT_TO_STRING_MAP, outputMap);
775   }
776 
777   public void testUniqueIndexIterator() {
778     ImmutableMap<Integer, String> outputMap =
779         Maps.uniqueIndex(INT_TO_STRING_MAP.values().iterator(),
780             Functions.forMap(INT_TO_STRING_MAP.inverse()));
781     assertEquals(INT_TO_STRING_MAP, outputMap);
782   }
783 
784   /** Can't create the map if more than one value maps to the same key. */
785   public void testUniqueIndexDuplicates() {
786     try {
787       Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1));
788       fail();
789     } catch (IllegalArgumentException expected) {
790     }
791   }
792 
793   /** Null values are not allowed. */
794   public void testUniqueIndexNullValue() {
795     List<String> listWithNull = Lists.newArrayList((String) null);
796     try {
797       Maps.uniqueIndex(listWithNull, Functions.constant(1));
798       fail();
799     } catch (NullPointerException expected) {
800     }
801   }
802 
803   /** Null keys aren't allowed either. */
804   public void testUniqueIndexNullKey() {
805     List<String> oneStringList = Lists.newArrayList("foo");
806     try {
807       Maps.uniqueIndex(oneStringList, Functions.constant(null));
808       fail();
809     } catch (NullPointerException expected) {
810     }
811   }
812 
813   public void testAsConverter_nominal() throws Exception {
814     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
815         "one", 1,
816         "two", 2);
817     Converter<String, Integer> converter = Maps.asConverter(biMap);
818     for (Entry<String, Integer> entry : biMap.entrySet()) {
819       assertSame(entry.getValue(), converter.convert(entry.getKey()));
820     }
821   }
822 
823   public void testAsConverter_inverse() throws Exception {
824     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
825         "one", 1,
826         "two", 2);
827     Converter<String, Integer> converter = Maps.asConverter(biMap);
828     for (Entry<String, Integer> entry : biMap.entrySet()) {
829       assertSame(entry.getKey(), converter.reverse().convert(entry.getValue()));
830     }
831   }
832 
833   public void testAsConverter_noMapping() throws Exception {
834     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
835         "one", 1,
836         "two", 2);
837     Converter<String, Integer> converter = Maps.asConverter(biMap);
838     try {
839       converter.convert("three");
840       fail();
841     } catch (IllegalArgumentException expected) {
842     }
843   }
844 
845   public void testAsConverter_nullConversions() throws Exception {
846     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
847         "one", 1,
848         "two", 2);
849     Converter<String, Integer> converter = Maps.asConverter(biMap);
850     assertNull(converter.convert(null));
851     assertNull(converter.reverse().convert(null));
852   }
853 
854   public void testAsConverter_isAView() throws Exception {
855     BiMap<String, Integer> biMap = HashBiMap.create();
856     biMap.put("one", 1);
857     biMap.put("two", 2);
858     Converter<String, Integer> converter = Maps.asConverter(biMap);
859 
860     assertSame(1, converter.convert("one"));
861     assertSame(2, converter.convert("two"));
862     try {
863       converter.convert("three");
864       fail();
865     } catch (IllegalArgumentException expected) {
866     }
867 
868     biMap.put("three", 3);
869 
870     assertSame(1, converter.convert("one"));
871     assertSame(2, converter.convert("two"));
872     assertSame(3, converter.convert("three"));
873   }
874 
875   public void testAsConverter_withNullMapping() throws Exception {
876     BiMap<String, Integer> biMap = HashBiMap.create();
877     biMap.put("one", 1);
878     biMap.put("two", 2);
879     biMap.put("three", null);
880     try {
881       Maps.asConverter(biMap).convert("three");
882       fail();
883     } catch (IllegalArgumentException expected) {
884     }
885   }
886 
887   public void testAsConverter_toString() {
888     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
889         "one", 1,
890         "two", 2);
891     Converter<String, Integer> converter = Maps.asConverter(biMap);
892     assertEquals("Maps.asConverter({one=1, two=2})", converter.toString());
893   }
894 
895   public void testAsConverter_serialization() {
896     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
897         "one", 1,
898         "two", 2);
899     Converter<String, Integer> converter = Maps.asConverter(biMap);
900     SerializableTester.reserializeAndAssert(converter);
901   }
902 
903   public void testUnmodifiableBiMap() {
904     BiMap<Integer, String> mod = HashBiMap.create();
905     mod.put(1, "one");
906     mod.put(2, "two");
907     mod.put(3, "three");
908 
909     BiMap<Number, String> unmod = Maps.<Number, String>unmodifiableBiMap(mod);
910 
911     /* No aliasing on inverse operations. */
912     assertSame(unmod.inverse(), unmod.inverse());
913     assertSame(unmod, unmod.inverse().inverse());
914 
915     /* Unmodifiable is a view. */
916     mod.put(4, "four");
917     assertEquals(true, unmod.get(4).equals("four"));
918     assertEquals(true, unmod.inverse().get("four").equals(4));
919 
920     /* UnsupportedOperationException on direct modifications. */
921     try {
922       unmod.put(4, "four");
923       fail("UnsupportedOperationException expected");
924     } catch (UnsupportedOperationException expected) {}
925     try {
926       unmod.forcePut(4, "four");
927       fail("UnsupportedOperationException expected");
928     } catch (UnsupportedOperationException expected) {}
929     try {
930       unmod.putAll(Collections.singletonMap(4, "four"));
931       fail("UnsupportedOperationException expected");
932     } catch (UnsupportedOperationException expected) {}
933 
934     /* UnsupportedOperationException on indirect modifications. */
935     BiMap<String, Number> inverse = unmod.inverse();
936     try {
937       inverse.put("four", 4);
938       fail("UnsupportedOperationException expected");
939     } catch (UnsupportedOperationException expected) {}
940     try {
941       inverse.forcePut("four", 4);
942       fail("UnsupportedOperationException expected");
943     } catch (UnsupportedOperationException expected) {}
944     try {
945       inverse.putAll(Collections.singletonMap("four", 4));
946       fail("UnsupportedOperationException expected");
947     } catch (UnsupportedOperationException expected) {}
948     Set<String> values = unmod.values();
949     try {
950       values.remove("four");
951       fail("UnsupportedOperationException expected");
952     } catch (UnsupportedOperationException expected) {}
953     Set<Map.Entry<Number, String>> entries = unmod.entrySet();
954     Map.Entry<Number, String> entry = entries.iterator().next();
955     try {
956       entry.setValue("four");
957       fail("UnsupportedOperationException expected");
958     } catch (UnsupportedOperationException expected) {}
959     @SuppressWarnings("unchecked")
960     Map.Entry<Integer, String> entry2
961         = (Map.Entry<Integer, String>) entries.toArray()[0];
962     try {
963       entry2.setValue("four");
964       fail("UnsupportedOperationException expected");
965     } catch (UnsupportedOperationException expected) {}
966   }
967 
968   public void testImmutableEntry() {
969     Map.Entry<String, Integer> e = Maps.immutableEntry("foo", 1);
970     assertEquals("foo", e.getKey());
971     assertEquals(1, (int) e.getValue());
972     try {
973       e.setValue(2);
974       fail("UnsupportedOperationException expected");
975     } catch (UnsupportedOperationException expected) {}
976     assertEquals("foo=1", e.toString());
977     assertEquals(101575, e.hashCode());
978   }
979 
980   public void testImmutableEntryNull() {
981     Map.Entry<String, Integer> e
982         = Maps.immutableEntry((String) null, (Integer) null);
983     assertNull(e.getKey());
984     assertNull(e.getValue());
985     try {
986       e.setValue(null);
987       fail("UnsupportedOperationException expected");
988     } catch (UnsupportedOperationException expected) {}
989     assertEquals("null=null", e.toString());
990     assertEquals(0, e.hashCode());
991   }
992 
993   /** See {@link SynchronizedBiMapTest} for more tests. */
994   public void testSynchronizedBiMap() {
995     BiMap<String, Integer> bimap = HashBiMap.create();
996     bimap.put("one", 1);
997     BiMap<String, Integer> sync = Maps.synchronizedBiMap(bimap);
998     bimap.put("two", 2);
999     sync.put("three", 3);
1000     assertEquals(ImmutableSet.of(1, 2, 3), bimap.inverse().keySet());
1001     assertEquals(ImmutableSet.of(1, 2, 3), sync.inverse().keySet());
1002   }
1003 
1004   private static final Predicate<String> NOT_LENGTH_3
1005       = new Predicate<String>() {
1006         @Override
1007         public boolean apply(String input) {
1008           return input == null || input.length() != 3;
1009         }
1010       };
1011 
1012   private static final Predicate<Integer> EVEN
1013       = new Predicate<Integer>() {
1014         @Override
1015         public boolean apply(Integer input) {
1016           return input == null || input % 2 == 0;
1017         }
1018       };
1019 
1020   private static final Predicate<Entry<String, Integer>> CORRECT_LENGTH
1021       = new Predicate<Entry<String, Integer>>() {
1022         @Override
1023         public boolean apply(Entry<String, Integer> input) {
1024           return input.getKey().length() == input.getValue();
1025         }
1026       };
1027 
1028   private static final Function<Integer, Double> SQRT_FUNCTION = new Function<Integer, Double>() {
1029       @Override
1030       public Double apply(Integer in) {
1031         return Math.sqrt(in);
1032       }
1033     };
1034 
1035   public static class FilteredMapTest extends TestCase {
1036     Map<String, Integer> createUnfiltered() {
1037       return Maps.newHashMap();
1038     }
1039 
1040     public void testFilteredKeysIllegalPut() {
1041       Map<String, Integer> unfiltered = createUnfiltered();
1042       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1043       filtered.put("a", 1);
1044       filtered.put("b", 2);
1045       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1046 
1047       try {
1048         filtered.put("yyy", 3);
1049         fail();
1050       } catch (IllegalArgumentException expected) {}
1051     }
1052 
1053     public void testFilteredKeysIllegalPutAll() {
1054       Map<String, Integer> unfiltered = createUnfiltered();
1055       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1056       filtered.put("a", 1);
1057       filtered.put("b", 2);
1058       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1059 
1060       try {
1061         filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5));
1062         fail();
1063       } catch (IllegalArgumentException expected) {}
1064 
1065       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1066     }
1067 
1068     public void testFilteredKeysFilteredReflectsBackingChanges() {
1069       Map<String, Integer> unfiltered = createUnfiltered();
1070       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1071       unfiltered.put("two", 2);
1072       unfiltered.put("three", 3);
1073       unfiltered.put("four", 4);
1074       assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered);
1075       assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered);
1076 
1077       unfiltered.remove("three");
1078       assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered);
1079       assertEquals(ImmutableMap.of("four", 4), filtered);
1080 
1081       unfiltered.clear();
1082       assertEquals(ImmutableMap.of(), unfiltered);
1083       assertEquals(ImmutableMap.of(), filtered);
1084     }
1085 
1086     public void testFilteredValuesIllegalPut() {
1087       Map<String, Integer> unfiltered = createUnfiltered();
1088       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1089       filtered.put("a", 2);
1090       unfiltered.put("b", 4);
1091       unfiltered.put("c", 5);
1092       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1093 
1094       try {
1095         filtered.put("yyy", 3);
1096         fail();
1097       } catch (IllegalArgumentException expected) {}
1098       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1099     }
1100 
1101     public void testFilteredValuesIllegalPutAll() {
1102       Map<String, Integer> unfiltered = createUnfiltered();
1103       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1104       filtered.put("a", 2);
1105       unfiltered.put("b", 4);
1106       unfiltered.put("c", 5);
1107       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1108 
1109       try {
1110         filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6));
1111         fail();
1112       } catch (IllegalArgumentException expected) {}
1113       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1114     }
1115 
1116     public void testFilteredValuesIllegalSetValue() {
1117       Map<String, Integer> unfiltered = createUnfiltered();
1118       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1119       filtered.put("a", 2);
1120       filtered.put("b", 4);
1121       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1122 
1123       Entry<String, Integer> entry = filtered.entrySet().iterator().next();
1124       try {
1125         entry.setValue(5);
1126         fail();
1127       } catch (IllegalArgumentException expected) {}
1128 
1129       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1130     }
1131 
1132     public void testFilteredValuesClear() {
1133       Map<String, Integer> unfiltered = createUnfiltered();
1134       unfiltered.put("one", 1);
1135       unfiltered.put("two", 2);
1136       unfiltered.put("three", 3);
1137       unfiltered.put("four", 4);
1138       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1139       assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4),
1140           unfiltered);
1141       assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered);
1142 
1143       filtered.clear();
1144       assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered);
1145       assertTrue(filtered.isEmpty());
1146     }
1147 
1148     public void testFilteredEntriesIllegalPut() {
1149       Map<String, Integer> unfiltered = createUnfiltered();
1150       unfiltered.put("cat", 3);
1151       unfiltered.put("dog", 2);
1152       unfiltered.put("horse", 5);
1153       Map<String, Integer> filtered
1154           = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1155       assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1156 
1157       filtered.put("chicken", 7);
1158       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1159 
1160       try {
1161         filtered.put("cow", 7);
1162         fail();
1163       } catch (IllegalArgumentException expected) {}
1164       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1165     }
1166 
1167     public void testFilteredEntriesIllegalPutAll() {
1168       Map<String, Integer> unfiltered = createUnfiltered();
1169       unfiltered.put("cat", 3);
1170       unfiltered.put("dog", 2);
1171       unfiltered.put("horse", 5);
1172       Map<String, Integer> filtered
1173           = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1174       assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1175 
1176       filtered.put("chicken", 7);
1177       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1178 
1179       try {
1180         filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7));
1181         fail();
1182       } catch (IllegalArgumentException expected) {}
1183       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1184     }
1185 
1186     public void testFilteredEntriesObjectPredicate() {
1187       Map<String, Integer> unfiltered = createUnfiltered();
1188       unfiltered.put("cat", 3);
1189       unfiltered.put("dog", 2);
1190       unfiltered.put("horse", 5);
1191       Predicate<Object> predicate = Predicates.alwaysFalse();
1192       Map<String, Integer> filtered
1193           = Maps.filterEntries(unfiltered, predicate);
1194       assertTrue(filtered.isEmpty());
1195     }
1196 
1197     public void testFilteredEntriesWildCardEntryPredicate() {
1198       Map<String, Integer> unfiltered = createUnfiltered();
1199       unfiltered.put("cat", 3);
1200       unfiltered.put("dog", 2);
1201       unfiltered.put("horse", 5);
1202       Predicate<Entry<?, ?>> predicate = new Predicate<Entry<?, ?>>() {
1203         @Override
1204         public boolean apply(Entry<?, ?> input) {
1205           return "cat".equals(input.getKey())
1206               || Integer.valueOf(2) == input.getValue();
1207         }
1208       };
1209       Map<String, Integer> filtered
1210           = Maps.filterEntries(unfiltered, predicate);
1211       assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered);
1212     }
1213   }
1214 
1215   public static class FilteredSortedMapTest extends FilteredMapTest {
1216     @Override
1217     SortedMap<String, Integer> createUnfiltered() {
1218       return Maps.newTreeMap();
1219     }
1220 
1221     public void testFilterKeysIdentifiesSortedMap() {
1222       SortedMap<String, Integer> map = createUnfiltered();
1223       assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3)
1224           instanceof SortedMap);
1225     }
1226 
1227     public void testFilterValuesIdentifiesSortedMap() {
1228       SortedMap<String, Integer> map = createUnfiltered();
1229       assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN)
1230           instanceof SortedMap);
1231     }
1232 
1233     public void testFilterEntriesIdentifiesSortedMap() {
1234       SortedMap<String, Integer> map = createUnfiltered();
1235       assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH)
1236           instanceof SortedMap);
1237     }
1238 
1239     public void testFirstAndLastKeyFilteredMap() {
1240       SortedMap<String, Integer> unfiltered = createUnfiltered();
1241       unfiltered.put("apple", 2);
1242       unfiltered.put("banana", 6);
1243       unfiltered.put("cat", 3);
1244       unfiltered.put("dog", 5);
1245 
1246       SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1247       assertEquals("banana", filtered.firstKey());
1248       assertEquals("cat", filtered.lastKey());
1249     }
1250 
1251     public void testHeadSubTailMap_FilteredMap() {
1252       SortedMap<String, Integer> unfiltered = createUnfiltered();
1253       unfiltered.put("apple", 2);
1254       unfiltered.put("banana", 6);
1255       unfiltered.put("cat", 4);
1256       unfiltered.put("dog", 3);
1257       SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1258 
1259       assertEquals(ImmutableMap.of("banana", 6), filtered.headMap("dog"));
1260       assertEquals(ImmutableMap.of(), filtered.headMap("banana"));
1261       assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.headMap("emu"));
1262 
1263       assertEquals(ImmutableMap.of("banana", 6), filtered.subMap("banana", "dog"));
1264       assertEquals(ImmutableMap.of("dog", 3), filtered.subMap("cat", "emu"));
1265 
1266       assertEquals(ImmutableMap.of("dog", 3), filtered.tailMap("cat"));
1267       assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.tailMap("banana"));
1268     }
1269   }
1270 
1271   public static class FilteredBiMapTest extends FilteredMapTest {
1272     @Override
1273     BiMap<String, Integer> createUnfiltered() {
1274       return HashBiMap.create();
1275     }
1276 
1277     public void testFilterKeysIdentifiesBiMap() {
1278       BiMap<String, Integer> map = createUnfiltered();
1279       assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3)
1280           instanceof BiMap);
1281     }
1282 
1283     public void testFilterValuesIdentifiesBiMap() {
1284       BiMap<String, Integer> map = createUnfiltered();
1285       assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN)
1286           instanceof BiMap);
1287     }
1288 
1289     public void testFilterEntriesIdentifiesBiMap() {
1290       BiMap<String, Integer> map = createUnfiltered();
1291       assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH)
1292           instanceof BiMap);
1293     }
1294   }
1295 
1296   public void testTransformValues() {
1297     Map<String, Integer> map = ImmutableMap.of("a", 4, "b", 9);
1298     Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1299 
1300     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1301   }
1302 
1303   public void testTransformValuesSecretlySorted() {
1304     Map<String, Integer> map =
1305         sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1306     Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1307 
1308     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1309     assertTrue(transformed instanceof SortedMap);
1310   }
1311 
1312   public void testTransformEntries() {
1313     Map<String, String> map = ImmutableMap.of("a", "4", "b", "9");
1314     EntryTransformer<String, String, String> concat =
1315         new EntryTransformer<String, String, String>() {
1316           @Override
1317           public String transformEntry(String key, String value) {
1318             return key + value;
1319           }
1320         };
1321     Map<String, String> transformed = transformEntries(map, concat);
1322 
1323     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1324   }
1325 
1326   public void testTransformEntriesSecretlySorted() {
1327     Map<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1328     EntryTransformer<String, String, String> concat =
1329         new EntryTransformer<String, String, String>() {
1330           @Override
1331           public String transformEntry(String key, String value) {
1332             return key + value;
1333           }
1334         };
1335     Map<String, String> transformed = transformEntries(map, concat);
1336 
1337     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1338     assertTrue(transformed instanceof SortedMap);
1339   }
1340 
1341   @SuppressWarnings("unused")
1342   public void testTransformEntriesGenerics() {
1343     Map<Object, Object> map1 = ImmutableMap.<Object, Object>of(1, 2);
1344     Map<Object, Number> map2 = ImmutableMap.<Object, Number>of(1, 2);
1345     Map<Object, Integer> map3 = ImmutableMap.<Object, Integer>of(1, 2);
1346     Map<Number, Object> map4 = ImmutableMap.<Number, Object>of(1, 2);
1347     Map<Number, Number> map5 = ImmutableMap.<Number, Number>of(1, 2);
1348     Map<Number, Integer> map6 = ImmutableMap.<Number, Integer>of(1, 2);
1349     Map<Integer, Object> map7 = ImmutableMap.<Integer, Object>of(1, 2);
1350     Map<Integer, Number> map8 = ImmutableMap.<Integer, Number>of(1, 2);
1351     Map<Integer, Integer> map9 = ImmutableMap.<Integer, Integer>of(1, 2);
1352     Map<? extends Number, ? extends Number> map0 = ImmutableMap.of(1, 2);
1353 
1354     EntryTransformer<Number, Number, Double> transformer =
1355         new EntryTransformer<Number, Number, Double>() {
1356           @Override
1357           public Double transformEntry(Number key, Number value) {
1358             return key.doubleValue() + value.doubleValue();
1359           }
1360         };
1361 
1362     Map<Object, Double> objectKeyed;
1363     Map<Number, Double> numberKeyed;
1364     Map<Integer, Double> integerKeyed;
1365 
1366     numberKeyed = transformEntries(map5, transformer);
1367     numberKeyed = transformEntries(map6, transformer);
1368     integerKeyed = transformEntries(map8, transformer);
1369     integerKeyed = transformEntries(map9, transformer);
1370 
1371     Map<? extends Number, Double> wildcarded = transformEntries(map0, transformer);
1372 
1373     // Can't loosen the key type:
1374     // objectKeyed = transformEntries(map5, transformer);
1375     // objectKeyed = transformEntries(map6, transformer);
1376     // objectKeyed = transformEntries(map8, transformer);
1377     // objectKeyed = transformEntries(map9, transformer);
1378     // numberKeyed = transformEntries(map8, transformer);
1379     // numberKeyed = transformEntries(map9, transformer);
1380 
1381     // Can't loosen the value type:
1382     // Map<Number, Number> looseValued1 = transformEntries(map5, transformer);
1383     // Map<Number, Number> looseValued2 = transformEntries(map6, transformer);
1384     // Map<Integer, Number> looseValued3 = transformEntries(map8, transformer);
1385     // Map<Integer, Number> looseValued4 = transformEntries(map9, transformer);
1386 
1387     // Can't call with too loose a key:
1388     // transformEntries(map1, transformer);
1389     // transformEntries(map2, transformer);
1390     // transformEntries(map3, transformer);
1391 
1392     // Can't call with too loose a value:
1393     // transformEntries(map1, transformer);
1394     // transformEntries(map4, transformer);
1395     // transformEntries(map7, transformer);
1396   }
1397 
1398   public void testTransformEntriesExample() {
1399     Map<String, Boolean> options =
1400         ImmutableMap.of("verbose", true, "sort", false);
1401     EntryTransformer<String, Boolean, String> flagPrefixer =
1402         new EntryTransformer<String, Boolean, String>() {
1403           @Override
1404           public String transformEntry(String key, Boolean value) {
1405             return value ? key : "no" + key;
1406           }
1407         };
1408     Map<String, String> transformed = transformEntries(options, flagPrefixer);
1409     assertEquals("{verbose=verbose, sort=nosort}", transformed.toString());
1410   }
1411 
1412   // Logically this would accept a NavigableMap, but that won't work under GWT.
1413   private static <K, V> SortedMap<K, V> sortedNotNavigable(
1414       final SortedMap<K, V> map) {
1415     return new ForwardingSortedMap<K, V>() {
1416       @Override protected SortedMap<K, V> delegate() {
1417         return map;
1418       }
1419     };
1420   }
1421 
1422   public void testSortedMapTransformValues() {
1423     SortedMap<String, Integer> map =
1424         sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1425     SortedMap<String, Double> transformed =
1426         transformValues(map, SQRT_FUNCTION);
1427 
1428     /*
1429      * We'd like to sanity check that we didn't get a NavigableMap out, but we
1430      * can't easily do so while maintaining GWT compatibility.
1431      */
1432     assertEquals(ImmutableSortedMap.of("a", 2.0, "b", 3.0), transformed);
1433   }
1434 
1435   public void testSortedMapTransformEntries() {
1436     SortedMap<String, String> map =
1437         sortedNotNavigable(ImmutableSortedMap.of("a", "4", "b", "9"));
1438     EntryTransformer<String, String, String> concat =
1439         new EntryTransformer<String, String, String>() {
1440           @Override
1441           public String transformEntry(String key, String value) {
1442             return key + value;
1443           }
1444         };
1445     SortedMap<String, String> transformed = transformEntries(map, concat);
1446 
1447     /*
1448      * We'd like to sanity check that we didn't get a NavigableMap out, but we
1449      * can't easily do so while maintaining GWT compatibility.
1450      */
1451     assertEquals(ImmutableSortedMap.of("a", "a4", "b", "b9"), transformed);
1452   }
1453 }
1454