View Javadoc
1   package org.djutils.immutablecollections;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertNull;
6   import static org.junit.Assert.assertTrue;
7   
8   import java.util.Collection;
9   import java.util.HashMap;
10  import java.util.Map;
11  import java.util.Map.Entry;
12  import java.util.Set;
13  import java.util.function.BiConsumer;
14  
15  import org.djutils.immutablecollections.ImmutableMap.ImmutableEntry;
16  import org.junit.Assert;
17  import org.junit.Test;
18  
19  /**
20   * TestImmutableHashMap.java.
21   * <p>
22   * Copyright (c) 2002-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
23   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
24   * distributed under a three-clause BSD-style license, which can be found at
25   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
26   * </p>
27   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
28   */
29  public class TestImmutableHashMap
30  {
31  
32      /**
33       * Test most of the equals and hashCode methods and the forEach method of the ImmutableAbstractMap class.
34       */
35      @SuppressWarnings({"unlikely-arg-type"})
36      @Test
37      public final void testMapEqualsAndHashCode()
38      {
39          Integer[] keys = new Integer[] {10, 20, 30, 40};
40          Map<Integer, Double> mutableMap1 = new HashMap<>();
41          Map<Integer, Double> mutableMap2 = new HashMap<>();
42          for (Integer key : keys)
43          {
44              mutableMap1.put(key, Math.PI + key);
45              mutableMap2.put(key, Math.PI + key);
46          }
47          assertEquals("maps with same content should be equal", mutableMap1, mutableMap2);
48          assertEquals("maps with same content should have same hash code", mutableMap1.hashCode(), mutableMap2.hashCode());
49          // No see that the same logic holds for our immutable maps
50          ImmutableMap<Integer, Double> im1 = new ImmutableHashMap<>(mutableMap1, Immutable.WRAP);
51          assertFalse(im1.isCopy());
52          ImmutableMap<Integer, Double> im2 = new ImmutableHashMap<>(mutableMap2, Immutable.WRAP);
53          assertEquals("immutable maps with same content should be equal", im1, im2);
54          assertEquals("immutable maps with same content should have same hash code", im1.hashCode(), im2.hashCode());
55          im2 = new ImmutableHashMap<>(mutableMap2, Immutable.COPY);
56          assertEquals("immutable maps with same content should be equal", im1, im2);
57          assertEquals("immutable maps with same content should be equal", im2, im1);
58          im1 = new ImmutableHashMap<>(mutableMap1, Immutable.COPY);
59          assertTrue(im1.isCopy());
60          assertEquals("immutable maps with same content should be equal", im1, im2);
61          assertEquals("immutable maps with same content should be equal", im2, im1);
62          // test the short cut path in equals
63          assertEquals("immutable map is equal to itself", im1, im1);
64          assertFalse("immutable map is not equal to null", im1.equals(null));
65          assertFalse("immutable map is not equal to some totally different object", im1.equals("abc"));
66          mutableMap2.put(keys[0], Math.E);
67          assertFalse("altered mutable map differs", mutableMap1.equals(mutableMap2));
68          assertEquals("immutable map holding copy is not altered", im1, im2);
69          ImmutableMap<Integer, Double> im1Wrap = new ImmutableHashMap<>(mutableMap1, Immutable.WRAP);
70          assertEquals("another immutable map from the same collection is equal", im1, im1Wrap);
71          assertEquals("another immutable map from the same collection has same hash code", im1.hashCode(), im1Wrap.hashCode());
72          mutableMap1.put(keys[0], -Math.PI);
73          assertFalse("wrapped immutable map re-checks content", im1.equals(im1Wrap));
74          assertFalse("wrapped immutable map re-checks content", im1Wrap.equals(im1));
75          assertFalse("wrapped immutable map re-computes hash code", im1.hashCode() == im1Wrap.hashCode());
76          assertFalse("wrapped immutable map re-computes hash code", im1Wrap.hashCode() == im1.hashCode());
77          // Test the get method
78          assertNull("result of get for non-existent key returns null", im1.get(-123));
79          for (Integer key : keys)
80          {
81              assertEquals("Immutable map returns same as underlying mutable map", mutableMap1.get(key), im1Wrap.get(key));
82          }
83          ImmutableMap<Integer, Double> map3 =
84                  new ImmutableHashMap<>((ImmutableAbstractMap<Integer, Double>) im1Wrap, Immutable.WRAP);
85          assertEquals("immutable map constructed by wrapping another immutable map is equals", im1Wrap, map3);
86          map3 = new ImmutableHashMap<>((ImmutableAbstractMap<Integer, Double>) im1Wrap, Immutable.COPY);
87          assertEquals("immutable map constructed by copyinig another immutable map is equals", im1Wrap, map3);
88          assertTrue("toString returns something descriptive", map3.toString().startsWith("ImmutableHashMap ["));
89          assertEquals("get with default returns value for key when it exists", mutableMap1.get(keys[0]),
90                  map3.getOrDefault(keys[0], Math.asin(2.0)));
91          assertEquals("get with default returns default for key when it does not exist", Math.asin(2.0),
92                  map3.getOrDefault(-123, Math.asin(2.0)), 0.00001);
93          final ImmutableMap<Integer, Double> map4 =
94                  new ImmutableHashMap<Integer, Double>((ImmutableAbstractMap<Integer, Double>) im1Wrap, Immutable.WRAP);
95          boolean[] tested = new boolean[keys.length];
96          map3.forEach(new BiConsumer<Integer, Double>()
97          {
98              @Override
99              public void accept(final Integer t, final Double u)
100             {
101                 assertEquals("accept got a value that matches the key", u, map4.get(t), 0.0001);
102                 int index = -1;
103                 for (int i = 0; i < keys.length; i++)
104                 {
105                     if (keys[i] == t)
106                     {
107                         index = i;
108                     }
109                 }
110                 assertTrue("key is contained in keys", index >= 0);
111                 assertFalse("key has not appeared before", tested[index]);
112                 tested[index] = true;
113             }
114         });
115         for (int index = 0; index < tested.length; index++)
116         {
117             assertTrue("each index got tested", tested[index]);
118         }
119     }
120 
121     /**
122      * ...
123      */
124     @Test
125     public final void testHashMap()
126     {
127         Map<Integer, Integer> isMap = new HashMap<>();
128         for (int i = 1; i <= 10; i++)
129         {
130             isMap.put(i, 100 * i);
131         }
132         Map<Integer, Integer> map = new HashMap<Integer, Integer>(isMap);
133         testIntMap(map, new ImmutableHashMap<Integer, Integer>(map, Immutable.WRAP), Immutable.WRAP);
134         map = new HashMap<Integer, Integer>(isMap);
135         testIntMap(map, new ImmutableHashMap<Integer, Integer>(map, Immutable.COPY), Immutable.COPY);
136         map = new HashMap<Integer, Integer>(isMap);
137         testIntMap(map, new ImmutableHashMap<Integer, Integer>(map), Immutable.COPY);
138         map = new HashMap<Integer, Integer>(isMap);
139         ImmutableHashMap<Integer, Integer> ihs = new ImmutableHashMap<Integer, Integer>(map);
140         testIntMap(map, new ImmutableHashMap<Integer, Integer>(ihs), Immutable.COPY);
141     }
142 
143     /**
144      * ...
145      * @param map map from int to int
146      * @param imMap immutable map from int to int
147      * @param copyOrWrap whether the map within the immutable map is copied or wrapped
148      */
149     private void testIntMap(final Map<Integer, Integer> map, final ImmutableMap<Integer, Integer> imMap,
150             final Immutable copyOrWrap)
151     {
152         Assert.assertTrue(map.size() == 10);
153         Assert.assertTrue(imMap.size() == 10);
154         for (int i = 0; i < 10; i++)
155         {
156             Assert.assertTrue(imMap.containsKey(i + 1));
157         }
158         for (int i = 0; i < 10; i++)
159         {
160             Assert.assertTrue(imMap.containsValue(100 * (i + 1)));
161         }
162         Assert.assertFalse(imMap.isEmpty());
163         Assert.assertFalse(imMap.containsKey(15));
164         Assert.assertFalse(imMap.containsValue(1500));
165 
166         Assert.assertTrue(imMap.keySet().size() == 10);
167         Assert.assertTrue(imMap.values().size() == 10);
168 
169         Assert.assertTrue(sameContent(map.keySet(), imMap.keySet().toSet()));
170         Assert.assertTrue(sameContent(map.values(), imMap.values().toCollection()));
171         Assert.assertTrue(sameContent(map.keySet(), imMap.keySet().toSet())); // cached
172         Assert.assertTrue(sameContent(map.values(), imMap.values().toCollection()));
173 
174         Assert.assertTrue(checkEntrySets(map.entrySet(), imMap.entrySet().toSet()));
175         Assert.assertTrue(checkEntrySets(map.entrySet(), imMap.entrySet().toSet())); // cached
176 
177         if (copyOrWrap == Immutable.COPY)
178         {
179             Assert.assertTrue(imMap.isCopy());
180             Assert.assertTrue(imMap.toMap().equals(map));
181             Assert.assertFalse(imMap.toMap() == map);
182         }
183         else
184         {
185             Assert.assertTrue(imMap.isWrap());
186             Assert.assertTrue(imMap.toMap().equals(map));
187             Assert.assertFalse(imMap.toMap() == map); // this WRAP method returns a NEW list
188         }
189 
190         Map<Integer, Integer> to = imMap.toMap();
191         Assert.assertTrue(map.equals(to));
192 
193         // modify the underlying data structure
194         map.put(11, 1100);
195         if (copyOrWrap == Immutable.COPY)
196         {
197             Assert.assertTrue(imMap.size() == 10);
198         }
199         else
200         {
201             Assert.assertTrue(imMap.size() == 11);
202         }
203     }
204 
205     /**
206      * Check that two collection contain the same objects.
207      * @param a Collection&lt;?&gt;;
208      * @param b Collection&lt;?&gt;;
209      * @return boolean;
210      */
211     private boolean sameContent(final Collection<?> a, final Collection<?> b)
212     {
213         return a.containsAll(b) && b.containsAll(a); // Oops: second half was b.containsAll(b)
214     }
215 
216     /**
217      * Check that two Sets of Entries contain the same entries.
218      * @param es Set&lt;Entry&ltInteger, Integer&gt;&gt;; a set of Entries
219      * @param ies Set&lt;Entry&ltInteger, Integer&gt;&gt;; an immutable set of Entries
220      * @return boolean; true if the two sets contain the same entries
221      */
222     private boolean checkEntrySets(final Set<Entry<Integer, Integer>> es, final Set<ImmutableEntry<Integer, Integer>> ies)
223     {
224         if (es.size() != ies.size())
225         {
226             return false;
227         }
228         for (Entry<Integer, Integer> entry : es)
229         {
230             boolean found = false;
231             for (ImmutableEntry<Integer, Integer> immEntry : ies)
232             {
233                 if (entry.getKey().equals(immEntry.getKey()) && entry.getValue().equals(immEntry.getValue()))
234                 {
235                     found = true;
236                     break;
237                 }
238             }
239             if (!found)
240             {
241                 return false;
242             }
243         }
244         return true;
245     }
246 
247 }