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.juneau.commons.collections;
18  
19  import static org.junit.jupiter.api.Assertions.*;
20  
21  import java.util.*;
22  
23  import org.apache.juneau.*;
24  import org.junit.jupiter.api.*;
25  
26  class HashKey_Test extends TestBase {
27  
28  	//====================================================================================================
29  	// Static factory method - of(Object...)
30  	//====================================================================================================
31  
32  	@Test
33  	void a01_of_emptyArray() {
34  		HashKey key = HashKey.of();
35  		assertNotNull(key);
36  		// Arrays.hashCode(new Object[0]) == 1
37  		assertEquals(1, key.hashCode());
38  	}
39  
40  	@Test
41  	void a02_of_singleValue() {
42  		HashKey key1 = HashKey.of("test");
43  		HashKey key2 = HashKey.of("test");
44  		assertNotNull(key1);
45  		// Equal keys should have equal hash codes
46  		assertEquals(key1.hashCode(), key2.hashCode());
47  	}
48  
49  	@Test
50  	void a03_of_multipleValues() {
51  		HashKey key = HashKey.of("a", "b", 42, true);
52  		assertNotNull(key);
53  	}
54  
55  	@Test
56  	void a04_of_withNullValues() {
57  		HashKey key = HashKey.of("a", null, "b", null);
58  		assertNotNull(key);
59  	}
60  
61  	@Test
62  	void a05_of_allNullValues() {
63  		HashKey key = HashKey.of((Object)null, null, null);
64  		assertNotNull(key);
65  	}
66  
67  	@Test
68  	void a06_of_variousTypes() {
69  		HashKey key = HashKey.of(
70  			"string",
71  			42,
72  			true,
73  			3.14,
74  			'c',
75  			Integer.valueOf(100),
76  			new ArrayList<>(),
77  			new HashMap<>()
78  		);
79  		assertNotNull(key);
80  	}
81  
82  	//====================================================================================================
83  	// equals(Object) method
84  	//====================================================================================================
85  
86  	@Test
87  	void b01_equals_sameValues() {
88  		HashKey key1 = HashKey.of("a", "b", 42);
89  		HashKey key2 = HashKey.of("a", "b", 42);
90  		assertEquals(key1, key2);
91  		assertTrue(key1.equals(key2));
92  		assertTrue(key2.equals(key1));
93  	}
94  
95  	@Test
96  	void b02_equals_differentValues() {
97  		HashKey key1 = HashKey.of("a", "b", 42);
98  		HashKey key2 = HashKey.of("a", "b", 43);
99  		assertNotEquals(key1, key2);
100 		assertFalse(key1.equals(key2));
101 	}
102 
103 	@Test
104 	void b03_equals_differentOrder() {
105 		HashKey key1 = HashKey.of("a", "b", 42);
106 		HashKey key2 = HashKey.of("b", "a", 42);
107 		assertNotEquals(key1, key2);
108 		assertFalse(key1.equals(key2));
109 	}
110 
111 	@Test
112 	void b04_equals_differentLengths() {
113 		HashKey key1 = HashKey.of("a", "b");
114 		HashKey key2 = HashKey.of("a", "b", "c");
115 		assertNotEquals(key1, key2);
116 		assertFalse(key1.equals(key2));
117 	}
118 
119 	@Test
120 	void b05_equals_withNullValues() {
121 		HashKey key1 = HashKey.of("a", null, "b");
122 		HashKey key2 = HashKey.of("a", null, "b");
123 		assertEquals(key1, key2);
124 	}
125 
126 	@Test
127 	void b06_equals_nullVsNonNull() {
128 		HashKey key1 = HashKey.of("a", null, "b");
129 		HashKey key2 = HashKey.of("a", "notnull", "b");
130 		assertNotEquals(key1, key2);
131 	}
132 
133 	@Test
134 	void b07_equals_emptyKeys() {
135 		HashKey key1 = HashKey.of();
136 		HashKey key2 = HashKey.of();
137 		assertEquals(key1, key2);
138 	}
139 
140 	@Test
141 	void b08_equals_singleValue() {
142 		HashKey key1 = HashKey.of("test");
143 		HashKey key2 = HashKey.of("test");
144 		assertEquals(key1, key2);
145 	}
146 
147 	@Test
148 	void b09_equals_differentSingleValues() {
149 		HashKey key1 = HashKey.of("test1");
150 		HashKey key2 = HashKey.of("test2");
151 		assertNotEquals(key1, key2);
152 	}
153 
154 	@Test
155 	void b10_equals_withNullObject() {
156 		HashKey key = HashKey.of("a", "b");
157 		// equals() now checks for null and returns false
158 		assertFalse(key.equals(null));
159 		assertNotEquals(key, null);
160 	}
161 
162 	@Test
163 	void b11_equals_withNonHashKeyObject() {
164 		HashKey key = HashKey.of("a", "b");
165 		// equals() now checks type and returns false for non-HashKey objects
166 		assertFalse(key.equals("not a HashKey"));
167 		assertNotEquals(key, "not a HashKey");
168 	}
169 
170 	@Test
171 	void b12_equals_reflexive() {
172 		HashKey key = HashKey.of("a", "b", 42);
173 		assertEquals(key, key);
174 		assertTrue(key.equals(key));
175 	}
176 
177 	@Test
178 	void b13_equals_symmetric() {
179 		HashKey key1 = HashKey.of("a", "b", 42);
180 		HashKey key2 = HashKey.of("a", "b", 42);
181 		assertEquals(key1, key2);
182 		assertEquals(key2, key1);
183 	}
184 
185 	@Test
186 	void b14_equals_transitive() {
187 		HashKey key1 = HashKey.of("a", "b", 42);
188 		HashKey key2 = HashKey.of("a", "b", 42);
189 		HashKey key3 = HashKey.of("a", "b", 42);
190 		assertEquals(key1, key2);
191 		assertEquals(key2, key3);
192 		assertEquals(key1, key3);
193 	}
194 
195 	@Test
196 	void b15_equals_differentTypes() {
197 		HashKey key1 = HashKey.of("42");
198 		HashKey key2 = HashKey.of(42);
199 		assertNotEquals(key1, key2);
200 	}
201 
202 	@Test
203 	void b16_equals_arrays() {
204 		String[] arr1 = {"a", "b"};
205 		String[] arr2 = {"a", "b"};
206 		HashKey key1 = HashKey.of((Object)arr1);
207 		HashKey key2 = HashKey.of((Object)arr2);
208 		// Arrays with same contents should be equal (ne() uses deep equality)
209 		assertTrue(key1.equals(key2), "Arrays with same contents should be equal");
210 		// And should have same hash code (using Arrays.deepHashCode())
211 		assertEquals(key1.hashCode(), key2.hashCode(),
212 			"Equal HashKeys must have equal hash codes");
213 	}
214 
215 	//====================================================================================================
216 	// hashCode() method
217 	//====================================================================================================
218 
219 	@Test
220 	void c01_hashCode_equalKeysHaveEqualHashCodes() {
221 		HashKey key1 = HashKey.of("a", "b", 42);
222 		HashKey key2 = HashKey.of("a", "b", 42);
223 		assertEquals(key1.hashCode(), key2.hashCode());
224 	}
225 
226 	@Test
227 	void c02_hashCode_differentKeysMayHaveDifferentHashCodes() {
228 		HashKey key1 = HashKey.of("a", "b", 42);
229 		HashKey key2 = HashKey.of("a", "b", 43);
230 		// Different keys should have different hash codes (though collisions are possible)
231 		assertNotEquals(key1.hashCode(), key2.hashCode());
232 	}
233 
234 	@Test
235 	void c03_hashCode_consistent() {
236 		HashKey key = HashKey.of("a", "b", 42);
237 		int hashCode1 = key.hashCode();
238 		int hashCode2 = key.hashCode();
239 		assertEquals(hashCode1, hashCode2);
240 	}
241 
242 	@Test
243 	void c04_hashCode_emptyKey() {
244 		HashKey key1 = HashKey.of();
245 		HashKey key2 = HashKey.of();
246 		// Empty keys should have the same hash code
247 		assertEquals(key1.hashCode(), key2.hashCode());
248 		// And should equal Arrays.hashCode(new Object[0])
249 		assertEquals(java.util.Arrays.hashCode(new Object[0]), key1.hashCode());
250 	}
251 
252 	@Test
253 	void c05_hashCode_withNullValues() {
254 		HashKey key1 = HashKey.of("a", null, "b");
255 		HashKey key2 = HashKey.of("a", null, "b");
256 		assertEquals(key1.hashCode(), key2.hashCode());
257 	}
258 
259 	@Test
260 	void c06_hashCode_orderMatters() {
261 		HashKey key1 = HashKey.of("a", "b");
262 		HashKey key2 = HashKey.of("b", "a");
263 		// Different order should produce different hash codes
264 		assertNotEquals(key1.hashCode(), key2.hashCode());
265 	}
266 
267 	//====================================================================================================
268 	// Usage in HashMap
269 	//====================================================================================================
270 
271 	@Test
272 	void d01_hashMap_putAndGet() {
273 		Map<HashKey, String> map = new HashMap<>();
274 		HashKey key = HashKey.of("a", "b", 42);
275 		map.put(key, "value");
276 		assertEquals("value", map.get(key));
277 	}
278 
279 	@Test
280 	void d02_hashMap_equivalentKeys() {
281 		Map<HashKey, String> map = new HashMap<>();
282 		HashKey key1 = HashKey.of("a", "b", 42);
283 		HashKey key2 = HashKey.of("a", "b", 42);
284 		map.put(key1, "value");
285 		assertEquals("value", map.get(key2));
286 	}
287 
288 	@Test
289 	void d03_hashMap_differentKeys() {
290 		Map<HashKey, String> map = new HashMap<>();
291 		HashKey key1 = HashKey.of("a", "b", 42);
292 		HashKey key2 = HashKey.of("a", "b", 43);
293 		map.put(key1, "value1");
294 		map.put(key2, "value2");
295 		assertEquals("value1", map.get(key1));
296 		assertEquals("value2", map.get(key2));
297 	}
298 
299 	@Test
300 	void d04_hashMap_multipleEntries() {
301 		Map<HashKey, Integer> map = new HashMap<>();
302 		map.put(HashKey.of("a"), 1);
303 		map.put(HashKey.of("b"), 2);
304 		map.put(HashKey.of("c"), 3);
305 		assertEquals(3, map.size());
306 		assertEquals(1, map.get(HashKey.of("a")));
307 		assertEquals(2, map.get(HashKey.of("b")));
308 		assertEquals(3, map.get(HashKey.of("c")));
309 	}
310 
311 	@Test
312 	void d05_hashMap_withNullValues() {
313 		Map<HashKey, String> map = new HashMap<>();
314 		HashKey key = HashKey.of("a", null, "b");
315 		map.put(key, "value");
316 		assertEquals("value", map.get(HashKey.of("a", null, "b")));
317 	}
318 
319 	@Test
320 	void d06_hashMap_emptyKey() {
321 		Map<HashKey, String> map = new HashMap<>();
322 		HashKey key = HashKey.of();
323 		map.put(key, "value");
324 		assertEquals("value", map.get(HashKey.of()));
325 	}
326 
327 	//====================================================================================================
328 	// Usage in HashSet
329 	//====================================================================================================
330 
331 	@Test
332 	void e01_hashSet_add() {
333 		Set<HashKey> set = new HashSet<>();
334 		HashKey key = HashKey.of("a", "b", 42);
335 		assertTrue(set.add(key));
336 		assertTrue(set.contains(key));
337 	}
338 
339 	@Test
340 	void e02_hashSet_equivalentKeys() {
341 		Set<HashKey> set = new HashSet<>();
342 		HashKey key1 = HashKey.of("a", "b", 42);
343 		HashKey key2 = HashKey.of("a", "b", 42);
344 		assertTrue(set.add(key1));
345 		assertFalse(set.add(key2)); // Should not add duplicate
346 		assertEquals(1, set.size());
347 		assertTrue(set.contains(key2));
348 	}
349 
350 	@Test
351 	void e03_hashSet_differentKeys() {
352 		Set<HashKey> set = new HashSet<>();
353 		HashKey key1 = HashKey.of("a", "b", 42);
354 		HashKey key2 = HashKey.of("a", "b", 43);
355 		assertTrue(set.add(key1));
356 		assertTrue(set.add(key2));
357 		assertEquals(2, set.size());
358 	}
359 
360 	//====================================================================================================
361 	// toString() method
362 	//====================================================================================================
363 
364 	@Test
365 	void f01_toString_notNull() {
366 		HashKey key = HashKey.of("a", "b", 42);
367 		String str = key.toString();
368 		assertNotNull(str);
369 		assertFalse(str.isEmpty());
370 	}
371 
372 	@Test
373 	void f02_toString_containsHashCode() {
374 		HashKey key = HashKey.of("a", "b", 42);
375 		String str = key.toString();
376 		assertTrue(str.contains("hashCode"));
377 	}
378 
379 	@Test
380 	void f03_toString_containsArray() {
381 		HashKey key = HashKey.of("a", "b", 42);
382 		String str = key.toString();
383 		assertTrue(str.contains("array"));
384 	}
385 
386 	@Test
387 	void f04_toString_emptyKey() {
388 		HashKey key = HashKey.of();
389 		String str = key.toString();
390 		assertNotNull(str);
391 	}
392 
393 	//====================================================================================================
394 	// Immutability
395 	//====================================================================================================
396 
397 	@Test
398 	void g01_immutability_hashCodeConsistent() {
399 		HashKey key = HashKey.of("a", "b", 42);
400 		int hashCode1 = key.hashCode();
401 		int hashCode2 = key.hashCode();
402 		assertEquals(hashCode1, hashCode2);
403 	}
404 
405 	@Test
406 	void g02_immutability_equalsConsistent() {
407 		HashKey key1 = HashKey.of("a", "b", 42);
408 		HashKey key2 = HashKey.of("a", "b", 42);
409 		boolean equals1 = key1.equals(key2);
410 		boolean equals2 = key1.equals(key2);
411 		assertEquals(equals1, equals2);
412 	}
413 
414 	//====================================================================================================
415 	// Edge cases
416 	//====================================================================================================
417 
418 	@Test
419 	void h01_edgeCase_veryLongArray() {
420 		Object[] values = new Object[1000];
421 		for (int i = 0; i < 1000; i++) {
422 			values[i] = i;
423 		}
424 		HashKey key = HashKey.of(values);
425 		assertNotNull(key);
426 		assertEquals(key, HashKey.of(values));
427 	}
428 
429 	@Test
430 	void h02_edgeCase_nestedHashKeys() {
431 		HashKey innerKey = HashKey.of("inner");
432 		HashKey outerKey = HashKey.of("outer", innerKey);
433 		assertNotNull(outerKey);
434 	}
435 
436 	@Test
437 	void h03_edgeCase_mixedPrimitives() {
438 		HashKey key = HashKey.of(
439 			(byte)1,
440 			(short)2,
441 			3,
442 			4L,
443 			5.0f,
444 			6.0d,
445 			true,
446 			'c'
447 		);
448 		assertNotNull(key);
449 		assertEquals(key, HashKey.of((byte)1, (short)2, 3, 4L, 5.0f, 6.0d, true, 'c'));
450 	}
451 
452 	@Test
453 	void h04_edgeCase_collections() {
454 		List<String> list = Arrays.asList("a", "b");
455 		Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
456 		HashKey key = HashKey.of(list, set);
457 		assertNotNull(key);
458 	}
459 }
460