99import java .util .Arrays ;
1010import java .util .Objects ;
1111import java .util .Optional ;
12+ import java .util .concurrent .ThreadLocalRandom ;
1213import java .util .stream .Collectors ;
1314import java .util .stream .Stream ;
1415
1516import static java .lang .reflect .Modifier .isStatic ;
1617import static org .assertj .core .api .Assertions .assertThat ;
1718import static org .assertj .core .api .Assertions .assertThatThrownBy ;
1819import 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 .*;
2121
2222/**
2323 * 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() {
200200 class HashFunctionTest {
201201 @ Test
202202 @ 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 ());
208209
209- verify ( key , atLeastOnce ()). hashCode ( );
210+ assertThat ( indexSet ). hasSize ( 1 );
210211 }
211212
212213 @ Test
213214 @ 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 () {
216217 var arrayCapacity = 8 ;
217218 var indexSet = Stream .of ("A" , "Aa" , "AaB" , "4234" , "2234fasdf" , "ASDFDFSD34234234" , "afsd-fdfd-ae43-5gd3" )
218219 .map (str -> HashTable .calculateIndex (str , arrayCapacity ))
219220 .collect (Collectors .toSet ());
220221
221222 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 ()
223241 .allMatch (i -> i >= 0 && i < arrayCapacity );
224242 }
225243
226244 @ Test
227- @ Order (3 )
245+ @ Order (4 )
228246 @ DisplayName ("calculateIndex return non-negative value when hashCode is negative" )
229247 void calculateIndexReturnPositiveIndexWhenHashCodeIsNegative () {
230248 var key = Long .MAX_VALUE ;
@@ -248,7 +266,7 @@ class HashTableMethodsTest {
248266 void putWhenTableIsEmpty () {
249267 var previousValue = hashTable .put ("madmax" , 833 );
250268
251- var keyValueExists = checkKeyValueMappingExists ("madmax" , 833 );
269+ var keyValueExists = checkKeyValueExists ("madmax" , 833 );
252270
253271 assertNull (previousValue );
254272 assertTrue (keyValueExists );
@@ -262,8 +280,8 @@ void putTwoElementsWithTheSameHashCode() {
262280 var table = getInternalTable (hashTable );
263281 var prevValueA = hashTable .put ("AaAa" , 123 );
264282 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 );
267285 var bucketIndexA = HashTable .calculateIndex ("AaAa" , table .length );
268286 var bucketIndexB = HashTable .calculateIndex ("BBBB" , table .length );
269287
@@ -283,7 +301,7 @@ void putElementWithTheSameKey() {
283301
284302 var previousValue = hashTable .put ("madmax" , 876 );
285303 System .out .println (hashTable );
286- var containsNewValueByKey = checkKeyValueMappingExists ("madmax" , 876 );
304+ var containsNewValueByKey = checkKeyValueExists ("madmax" , 876 );
287305
288306 assertThat (previousValue ).isEqualTo (833 );
289307 assertTrue (containsNewValueByKey );
@@ -419,7 +437,7 @@ void remove() {
419437 var result = hashTable .remove ("madmax" );
420438
421439 assertThat (result ).isEqualTo (833 );
422- assertFalse (checkKeyValueMappingExists ("madmaxx" , 833 ));
440+ assertFalse (checkKeyValueExists ("madmaxx" , 833 ));
423441 }
424442
425443 @ Test
@@ -430,6 +448,38 @@ void removeWhenKeyDoesNotExists() {
430448
431449 assertNull (result );
432450 }
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+ }
433483 }
434484
435485 @ Nested
@@ -452,10 +502,10 @@ void resizeTable() {
452502 hashTable .resizeTable (16 );
453503
454504 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 ));
459509 }
460510
461511 @ Test
@@ -502,21 +552,30 @@ private void addToTable(String key, Integer value) {
502552 }
503553 }
504554
505- @ SneakyThrows
506- private boolean checkKeyValueMappingExists (Object key , Object value ) {
555+ private NodeProxy getNodeByKey (Object key ) {
507556 var table = getInternalTable (hashTable );
508557 for (var head : table ) {
509558 if (head != null ) {
510559 var current = new NodeProxy (head );
511560 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 ;
514563 }
515564 current = current .next ();
516565 }
517566 }
518567 }
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 ;
520579 }
521580
522581 @ SneakyThrows
0 commit comments