9
9
import java .util .Arrays ;
10
10
import java .util .Objects ;
11
11
import java .util .Optional ;
12
+ import java .util .concurrent .ThreadLocalRandom ;
12
13
import java .util .stream .Collectors ;
13
14
import java .util .stream .Stream ;
14
15
15
16
import static java .lang .reflect .Modifier .isStatic ;
16
17
import static org .assertj .core .api .Assertions .assertThat ;
17
18
import static org .assertj .core .api .Assertions .assertThatThrownBy ;
18
19
import static org .junit .jupiter .api .Assertions .*;
19
- import static org .mockito .Mockito .atLeastOnce ;
20
- import static org .mockito .Mockito .verify ;
20
+ import static org .mockito .Mockito .*;
21
21
22
22
/**
23
23
* A Reflection-based step by step test for a {@link HashTable} class. PLEASE NOTE that Reflection API should not be used
@@ -200,31 +200,49 @@ void constructorWithTableCapacityWhenArgumentIsNegative() {
200
200
class HashFunctionTest {
201
201
@ Test
202
202
@ Order (1 )
203
- @ DisplayName ("calculateIndex uses hashCode to compute an index for a given key" )
204
- void calculateIndexReturnDifferentValues () {
205
- var key = Mockito .spy (new Object ());
206
-
207
- HashTable .calculateIndex (key , 8 );
203
+ @ DisplayName ("calculateIndex returns the same value for the same key" )
204
+ void calculateIndexReturnTheSameValueWhenKeyIsTheSame () {
205
+ var indexSet = Stream .generate (() -> "ASDFDFSD34234234" )
206
+ .limit (10 )
207
+ .map (key -> HashTable .calculateIndex (key , 8 ))
208
+ .collect (Collectors .toSet ());
208
209
209
- verify ( key , atLeastOnce ()). hashCode ( );
210
+ assertThat ( indexSet ). hasSize ( 1 );
210
211
}
211
212
212
213
@ Test
213
214
@ Order (2 )
214
- @ DisplayName ("calculateIndex returns different values in array bounds " )
215
- void calculateIndexReturnIndexInArrayBounds () {
215
+ @ DisplayName ("calculateIndex returns different values for different keys " )
216
+ void calculateIndexReturnDifferentValuesWheKeysAreDifferent () {
216
217
var arrayCapacity = 8 ;
217
218
var indexSet = Stream .of ("A" , "Aa" , "AaB" , "4234" , "2234fasdf" , "ASDFDFSD34234234" , "afsd-fdfd-ae43-5gd3" )
218
219
.map (str -> HashTable .calculateIndex (str , arrayCapacity ))
219
220
.collect (Collectors .toSet ());
220
221
221
222
assertThat (indexSet )
222
- .hasSizeGreaterThan (1 )
223
+ .hasSizeGreaterThan (1 );
224
+ }
225
+
226
+ @ Test
227
+ @ Order (3 )
228
+ @ DisplayName ("calculateIndex returns values in array bounds" )
229
+ void calculateIndexReturnIndexInArrayBounds () {
230
+ var arrayCapacity = 8 ;
231
+ var keys = Stream .generate (() -> ThreadLocalRandom .current ().nextLong ())
232
+ .limit (100 )
233
+ .toList ();
234
+
235
+ var indexes = keys .stream ()
236
+ .map (key -> HashTable .calculateIndex (key , arrayCapacity ))
237
+ .toList ();
238
+
239
+ assertThat (indexes )
240
+ .isNotEmpty ()
223
241
.allMatch (i -> i >= 0 && i < arrayCapacity );
224
242
}
225
243
226
244
@ Test
227
- @ Order (3 )
245
+ @ Order (4 )
228
246
@ DisplayName ("calculateIndex return non-negative value when hashCode is negative" )
229
247
void calculateIndexReturnPositiveIndexWhenHashCodeIsNegative () {
230
248
var key = Long .MAX_VALUE ;
@@ -248,7 +266,7 @@ class HashTableMethodsTest {
248
266
void putWhenTableIsEmpty () {
249
267
var previousValue = hashTable .put ("madmax" , 833 );
250
268
251
- var keyValueExists = checkKeyValueMappingExists ("madmax" , 833 );
269
+ var keyValueExists = checkKeyValueExists ("madmax" , 833 );
252
270
253
271
assertNull (previousValue );
254
272
assertTrue (keyValueExists );
@@ -262,8 +280,8 @@ void putTwoElementsWithTheSameHashCode() {
262
280
var table = getInternalTable (hashTable );
263
281
var prevValueA = hashTable .put ("AaAa" , 123 );
264
282
var prevValueB = hashTable .put ("BBBB" , 456 );
265
- var containsKeyValueA = checkKeyValueMappingExists ("AaAa" , 123 );
266
- var containsKeyValueB = checkKeyValueMappingExists ("BBBB" , 456 );
283
+ var containsKeyValueA = checkKeyValueExists ("AaAa" , 123 );
284
+ var containsKeyValueB = checkKeyValueExists ("BBBB" , 456 );
267
285
var bucketIndexA = HashTable .calculateIndex ("AaAa" , table .length );
268
286
var bucketIndexB = HashTable .calculateIndex ("BBBB" , table .length );
269
287
@@ -283,7 +301,7 @@ void putElementWithTheSameKey() {
283
301
284
302
var previousValue = hashTable .put ("madmax" , 876 );
285
303
System .out .println (hashTable );
286
- var containsNewValueByKey = checkKeyValueMappingExists ("madmax" , 876 );
304
+ var containsNewValueByKey = checkKeyValueExists ("madmax" , 876 );
287
305
288
306
assertThat (previousValue ).isEqualTo (833 );
289
307
assertTrue (containsNewValueByKey );
@@ -419,7 +437,7 @@ void remove() {
419
437
var result = hashTable .remove ("madmax" );
420
438
421
439
assertThat (result ).isEqualTo (833 );
422
- assertFalse (checkKeyValueMappingExists ("madmaxx" , 833 ));
440
+ assertFalse (checkKeyValueExists ("madmaxx" , 833 ));
423
441
}
424
442
425
443
@ Test
@@ -430,6 +448,38 @@ void removeWhenKeyDoesNotExists() {
430
448
431
449
assertNull (result );
432
450
}
451
+
452
+ @ Test
453
+ @ Order (15 )
454
+ @ DisplayName ("remove deletes the element when it's in the middle of the list" )
455
+ void removeFromTheMiddleOfTheList () {
456
+ addToTable ("AaAa" , 843 );
457
+ addToTable ("BBBB" , 434 );
458
+ addToTable ("AaBB" , 587 );
459
+
460
+ var removedValue = hashTable .remove ("BBBB" );
461
+
462
+ assertTrue (checkKeyValueExists ("AaAa" , 843 ));
463
+ assertFalse (checkKeyExists ("BBBB" ));
464
+ assertTrue (checkKeyValueExists ("AaBB" , 587 ));
465
+ assertThat (removedValue ).isEqualTo (434 );
466
+ }
467
+
468
+ @ Test
469
+ @ Order (16 )
470
+ @ DisplayName ("remove deletes the element when it's in the end of the list" )
471
+ void removeFromTheEndOfTheList () {
472
+ addToTable ("AaAa" , 843 );
473
+ addToTable ("BBBB" , 434 );
474
+ addToTable ("AaBB" , 587 );
475
+
476
+ var removedValue = hashTable .remove ("AaBB" );
477
+
478
+ assertTrue (checkKeyValueExists ("AaAa" , 843 ));
479
+ assertTrue (checkKeyValueExists ("BBBB" , 434 ));
480
+ assertFalse (checkKeyExists ("AaBB" ));
481
+ assertThat (removedValue ).isEqualTo (587 );
482
+ }
433
483
}
434
484
435
485
@ Nested
@@ -452,10 +502,10 @@ void resizeTable() {
452
502
hashTable .resizeTable (16 );
453
503
454
504
assertThat (getInternalTable (hashTable )).hasSize (16 );
455
- assertTrue (checkKeyValueMappingExists ("madmax" , 833 ));
456
- assertTrue (checkKeyValueMappingExists ("altea" , 553 ));
457
- assertTrue (checkKeyValueMappingExists ("AaAa" , 123 ));
458
- assertTrue (checkKeyValueMappingExists ("BBBB" , 456 ));
505
+ assertTrue (checkKeyValueExists ("madmax" , 833 ));
506
+ assertTrue (checkKeyValueExists ("altea" , 553 ));
507
+ assertTrue (checkKeyValueExists ("AaAa" , 123 ));
508
+ assertTrue (checkKeyValueExists ("BBBB" , 456 ));
459
509
}
460
510
461
511
@ Test
@@ -502,21 +552,30 @@ private void addToTable(String key, Integer value) {
502
552
}
503
553
}
504
554
505
- @ SneakyThrows
506
- private boolean checkKeyValueMappingExists (Object key , Object value ) {
555
+ private NodeProxy getNodeByKey (Object key ) {
507
556
var table = getInternalTable (hashTable );
508
557
for (var head : table ) {
509
558
if (head != null ) {
510
559
var current = new NodeProxy (head );
511
560
while (current != null ) {
512
- if (current .key ().equals (key ) && current . value (). equals ( value ) ) {
513
- return true ;
561
+ if (current .key ().equals (key )) {
562
+ return current ;
514
563
}
515
564
current = current .next ();
516
565
}
517
566
}
518
567
}
519
- return false ;
568
+ return null ;
569
+ }
570
+
571
+ private boolean checkKeyValueExists (Object key , Object value ) {
572
+ var node = getNodeByKey (key );
573
+ return node != null && node .value ().equals (value );
574
+ }
575
+
576
+ private boolean checkKeyExists (Object key ) {
577
+ var node = getNodeByKey (key );
578
+ return node != null ;
520
579
}
521
580
522
581
@ SneakyThrows
0 commit comments