View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math4.legacy.linear;
18  
19  import java.util.ConcurrentModificationException;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Map;
23  import java.util.NoSuchElementException;
24  import java.util.Random;
25  import java.util.Set;
26  
27  import org.apache.commons.numbers.core.Precision;
28  import org.junit.Assert;
29  import org.junit.Before;
30  import org.junit.Test;
31  
32  
33  /**
34   * Test cases for the {@link OpenIntToDoubleHashMap}.
35   */
36  @SuppressWarnings("boxing")
37  public class OpenIntToDoubleHashMapTest {
38  
39      private Map<Integer, Double> javaMap = new HashMap<>();
40  
41      @Before
42      public void setUp() throws Exception {
43          javaMap.put(50, 100.0);
44          javaMap.put(75, 75.0);
45          javaMap.put(25, 500.0);
46          javaMap.put(Integer.MAX_VALUE, Double.MAX_VALUE);
47          javaMap.put(0, -1.0);
48          javaMap.put(1, 0.0);
49          javaMap.put(33, -0.1);
50          javaMap.put(23234234, -242343.0);
51          javaMap.put(23321, Double.MIN_VALUE);
52          javaMap.put(-4444, 332.0);
53          javaMap.put(-1, -2323.0);
54          javaMap.put(Integer.MIN_VALUE, 44.0);
55  
56          /* Add a few more to cause the table to rehash */
57          javaMap.putAll(generate());
58      }
59  
60      private Map<Integer, Double> generate() {
61          Map<Integer, Double> map = new HashMap<>();
62          Random r = new Random();
63          for (int i = 0; i < 2000; ++i) {
64              map.put(r.nextInt(), r.nextDouble());
65          }
66          return map;
67      }
68  
69      private OpenIntToDoubleHashMap createFromJavaMap() {
70          OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
71          for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
72              map.put(mapEntry.getKey(), mapEntry.getValue());
73          }
74          return map;
75      }
76  
77      @Test
78      public void testPutAndGetWith0ExpectedSize() {
79          OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap(0);
80          assertPutAndGet(map);
81      }
82  
83      @Test
84      public void testPutAndGetWithExpectedSize() {
85          OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap(500);
86          assertPutAndGet(map);
87      }
88  
89      @Test
90      public void testPutAndGet() {
91          OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
92          assertPutAndGet(map);
93      }
94  
95      private void assertPutAndGet(OpenIntToDoubleHashMap map) {
96          assertPutAndGet(map, 0, new HashSet<>());
97      }
98  
99      private void assertPutAndGet(OpenIntToDoubleHashMap map, int mapSize,
100             Set<Integer> keysInMap) {
101         Assert.assertEquals(mapSize, map.size());
102         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
103             map.put(mapEntry.getKey(), mapEntry.getValue());
104             if (!keysInMap.contains(mapEntry.getKey())) {
105                 ++mapSize;
106             }
107             Assert.assertEquals(mapSize, map.size());
108             Assert.assertTrue(Precision.equals(mapEntry.getValue(), map.get(mapEntry.getKey()), 1));
109         }
110     }
111 
112     @Test
113     public void testPutAbsentOnExisting() {
114         OpenIntToDoubleHashMap map = createFromJavaMap();
115         int size = javaMap.size();
116         for (Map.Entry<Integer, Double> mapEntry : generateAbsent().entrySet()) {
117             map.put(mapEntry.getKey(), mapEntry.getValue());
118             Assert.assertEquals(++size, map.size());
119             Assert.assertTrue(Precision.equals(mapEntry.getValue(), map.get(mapEntry.getKey()), 1));
120         }
121     }
122 
123     @Test
124     public void testPutOnExisting() {
125         OpenIntToDoubleHashMap map = createFromJavaMap();
126         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
127             map.put(mapEntry.getKey(), mapEntry.getValue());
128             Assert.assertEquals(javaMap.size(), map.size());
129             Assert.assertTrue(Precision.equals(mapEntry.getValue(), map.get(mapEntry.getKey()), 1));
130         }
131     }
132 
133     @Test
134     public void testGetAbsent() {
135         Map<Integer, Double> generated = generateAbsent();
136         OpenIntToDoubleHashMap map = createFromJavaMap();
137 
138         for (Map.Entry<Integer, Double> mapEntry : generated.entrySet()) {
139             Assert.assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
140         }
141     }
142 
143     @Test
144     public void testGetFromEmpty() {
145         OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
146         Assert.assertTrue(Double.isNaN(map.get(5)));
147         Assert.assertTrue(Double.isNaN(map.get(0)));
148         Assert.assertTrue(Double.isNaN(map.get(50)));
149     }
150 
151     @Test
152     public void testRemove() {
153         OpenIntToDoubleHashMap map = createFromJavaMap();
154         int mapSize = javaMap.size();
155         Assert.assertEquals(mapSize, map.size());
156         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
157             map.remove(mapEntry.getKey());
158             Assert.assertEquals(--mapSize, map.size());
159             Assert.assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
160         }
161 
162         /* Ensure that put and get still work correctly after removals */
163         assertPutAndGet(map);
164     }
165 
166     /* This time only remove some entries */
167     @Test
168     public void testRemove2() {
169         OpenIntToDoubleHashMap map = createFromJavaMap();
170         int mapSize = javaMap.size();
171         int count = 0;
172         Set<Integer> keysInMap = new HashSet<>(javaMap.keySet());
173         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
174             keysInMap.remove(mapEntry.getKey());
175             map.remove(mapEntry.getKey());
176             Assert.assertEquals(--mapSize, map.size());
177             Assert.assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
178             if (count++ > 5) {
179                 break;
180             }
181         }
182 
183         /* Ensure that put and get still work correctly after removals */
184         assertPutAndGet(map, mapSize, keysInMap);
185     }
186 
187     @Test
188     public void testRemoveFromEmpty() {
189         OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
190         Assert.assertTrue(Double.isNaN(map.remove(50)));
191     }
192 
193     @Test
194     public void testRemoveAbsent() {
195         Map<Integer, Double> generated = generateAbsent();
196 
197         OpenIntToDoubleHashMap map = createFromJavaMap();
198         int mapSize = map.size();
199 
200         for (Map.Entry<Integer, Double> mapEntry : generated.entrySet()) {
201             map.remove(mapEntry.getKey());
202             Assert.assertEquals(mapSize, map.size());
203             Assert.assertTrue(Double.isNaN(map.get(mapEntry.getKey())));
204         }
205     }
206 
207     /**
208      * Returns a map with at least 100 elements where each element is absent from javaMap.
209      */
210     private Map<Integer, Double> generateAbsent() {
211         Map<Integer, Double> generated = new HashMap<>();
212         do {
213             generated.putAll(generate());
214             for (Integer key : javaMap.keySet()) {
215                 generated.remove(key);
216             }
217         } while (generated.size() < 100);
218         return generated;
219     }
220 
221     @Test
222     public void testCopy() {
223         OpenIntToDoubleHashMap copy =
224             new OpenIntToDoubleHashMap(createFromJavaMap());
225         Assert.assertEquals(javaMap.size(), copy.size());
226 
227         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
228             Assert.assertTrue(Precision.equals(mapEntry.getValue(), copy.get(mapEntry.getKey()), 1));
229         }
230     }
231 
232     @Test
233     public void testContainsKey() {
234         OpenIntToDoubleHashMap map = createFromJavaMap();
235         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
236             Assert.assertTrue(map.containsKey(mapEntry.getKey()));
237         }
238         for (Map.Entry<Integer, Double> mapEntry : generateAbsent().entrySet()) {
239             Assert.assertFalse(map.containsKey(mapEntry.getKey()));
240         }
241         for (Map.Entry<Integer, Double> mapEntry : javaMap.entrySet()) {
242             int key = mapEntry.getKey();
243             Assert.assertTrue(map.containsKey(key));
244             map.remove(key);
245             Assert.assertFalse(map.containsKey(key));
246         }
247     }
248 
249     @Test
250     public void testIterator() {
251         OpenIntToDoubleHashMap map = createFromJavaMap();
252         OpenIntToDoubleHashMap.Iterator iterator = map.iterator();
253         for (int i = 0; i < map.size(); ++i) {
254             Assert.assertTrue(iterator.hasNext());
255             iterator.advance();
256             int key = iterator.key();
257             Assert.assertTrue(map.containsKey(key));
258             Assert.assertEquals(javaMap.get(key), map.get(key), 0);
259             Assert.assertEquals(javaMap.get(key), iterator.value(), 0);
260             Assert.assertTrue(javaMap.containsKey(key));
261         }
262         Assert.assertFalse(iterator.hasNext());
263         try {
264             iterator.advance();
265             Assert.fail("an exception should have been thrown");
266         } catch (NoSuchElementException nsee) {
267             // expected
268         }
269     }
270 
271     @Test
272     public void testConcurrentModification() {
273         OpenIntToDoubleHashMap map = createFromJavaMap();
274         OpenIntToDoubleHashMap.Iterator iterator = map.iterator();
275         map.put(3, 3);
276         try {
277             iterator.advance();
278             Assert.fail("an exception should have been thrown");
279         } catch (ConcurrentModificationException cme) {
280             // expected
281         }
282     }
283 
284     /**
285      * Regression test for a bug in findInsertionIndex where the hashing in the second probing
286      * loop was inconsistent with the first causing duplicate keys after the right sequence
287      * of puts and removes.
288      */
289     @Test
290     public void testPutKeysWithCollisions() {
291         OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
292         int key1 = -1996012590;
293         double value1 = 1.0;
294         map.put(key1, value1);
295         int key2 = 835099822;
296         map.put(key2, value1);
297         int key3 = 1008859686;
298         map.put(key3, value1);
299         Assert.assertTrue(Precision.equals(value1, map.get(key3), 1));
300         Assert.assertEquals(3, map.size());
301 
302         map.remove(key2);
303         double value2 = 2.0;
304         map.put(key3, value2);
305         Assert.assertTrue(Precision.equals(value2, map.get(key3), 1));
306         Assert.assertEquals(2, map.size());
307     }
308 
309     /**
310      * Similar to testPutKeysWithCollisions() but exercises the codepaths in a slightly
311      * different manner.
312      */
313     @Test
314     public void testPutKeysWithCollision2() {
315         OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap();
316         int key1 = 837989881;
317         double value1 = 1.0;
318         map.put(key1, value1);
319         int key2 = 476463321;
320         map.put(key2, value1);
321         Assert.assertEquals(2, map.size());
322         Assert.assertTrue(Precision.equals(value1, map.get(key2), 1));
323 
324         map.remove(key1);
325         double value2 = 2.0;
326         map.put(key2, value2);
327         Assert.assertEquals(1, map.size());
328         Assert.assertTrue(Precision.equals(value2, map.get(key2), 1));
329     }
330 }