Last active
February 5, 2023 13:36
-
-
Save tkaczenko/cabf9f08b4686e141f2793550e7c5046 to your computer and use it in GitHub Desktop.
Custom java implementation of HashMap with Stream API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyMap; | |
import java.util.*; | |
import java.util.stream.Collectors; | |
import static java.util.Objects.requireNonNull; | |
/** | |
* Custom implementation of HashMap with Stream API | |
* @author tkacz- | |
* @version 1.0 | |
* @see java.util.HashMap | |
*/ | |
public class MyMap<K, V> implements Map<K, V> { | |
/** | |
* Hash table for lists of map elements | |
*/ | |
private ArrayList<MyEntry<K, V>>[] table; | |
/** | |
* Default capacity of the hash table | |
*/ | |
private final int defaultSize = 10; | |
private int size = 0; | |
private static class MyEntry<K, V> implements Map.Entry<K, V> { | |
K key; | |
V value; | |
public MyEntry(K key, V value) { | |
this.key = key; | |
this.value = value; | |
} | |
@Override | |
public K getKey() { | |
return this.key; | |
} | |
@Override | |
public V getValue() { | |
return this.value; | |
} | |
@Override | |
public V setValue(V value) { | |
V temp = this.value; | |
this.value = value; | |
return temp; | |
} | |
} | |
/** | |
* Returns index of bucket in hash table | |
* @param key key of the value | |
* @return index of buckt in hash table | |
*/ | |
private int indexFor(Object key) { | |
return key.hashCode() % table.length; | |
} | |
public MyMap() { | |
table = new ArrayList[defaultSize]; | |
for (int i = 0; i < defaultSize; i++) { | |
table[i] = new ArrayList<>(); | |
} | |
} | |
/** | |
* Returns size of collection | |
* @return size | |
*/ | |
@Override | |
public int size() { | |
return size; | |
} | |
/** | |
* Check if the map is empty | |
* @return true or false | |
*/ | |
@Override | |
public boolean isEmpty() { | |
return size == 0; | |
} | |
/** | |
* Check if the key is in map | |
* @param key key of value | |
* @return true or false | |
* @throws NullPointerException If the key is null | |
*/ | |
@Override | |
public boolean containsKey(Object key) { | |
requireNonNull(key); | |
int index = indexFor(key); | |
return table[index].stream() | |
.anyMatch((e) -> e.getKey().equals(key)); | |
} | |
/** | |
* Check if the value is in map | |
* @param value value which must be in map | |
* @return true or false | |
* @throws NullPointerException If the value is null | |
*/ | |
@Override | |
public boolean containsValue(Object value) { | |
requireNonNull(value); | |
return Arrays.stream(table) | |
.anyMatch((l) -> l.stream() | |
.anyMatch((e) -> e.getValue().equals(value))); | |
} | |
/** | |
* Ruturns value by the key | |
* @param key the key of value | |
* @return value by the key or null | |
* @throws NullPointerException If the key is null | |
*/ | |
@Override | |
public V get(Object key) { | |
requireNonNull(key); | |
int index = indexFor(key); | |
for (MyEntry<K, V> entry : table[index]) { | |
if (entry.getKey().equals(key)) { | |
return entry.getValue(); | |
} | |
} | |
return null; | |
} | |
/** | |
* Put the key and the value in map. | |
* @param key key of the value | |
* @param value value of the map element | |
* @return old value by the key or null | |
* @throws NullPointerException If a key or value is null | |
*/ | |
@Override | |
public V put(K key, V value) { | |
requireNonNull(key); | |
requireNonNull(value); | |
int index = indexFor(key); | |
for (MyEntry<K, V> entry : table[index]) { | |
if (entry.getKey().equals(key)) { | |
V oldValue = entry.getValue(); | |
entry.setValue(value); | |
return oldValue; | |
} | |
} | |
table[index].add(new MyEntry<K, V>(key, value)); | |
size++; | |
return null; | |
} | |
/** | |
* Remove value from map by the key | |
* @param key key of the value which will be removed | |
* @return old value be the key or null | |
*/ | |
@Override | |
public V remove(Object key) { | |
requireNonNull(key); | |
int index = indexFor(key); | |
for (MyEntry<K, V> entry : table[index]) { | |
if (entry.getKey().equals(key)) { | |
V oldValue = entry.getValue(); | |
table[index].remove(entry); | |
return oldValue; | |
} | |
} | |
return null; | |
} | |
/** | |
* Put the map into the MyMap | |
* @param m the map which will be put | |
*/ | |
@Override | |
public void putAll(Map<? extends K, ? extends V> m) { | |
requireNonNull(m); | |
if (!m.isEmpty()) { | |
m.keySet().stream() | |
.forEach((k) -> put(k, m.get(k))); | |
} | |
} | |
/** | |
* Clear MyMap | |
*/ | |
@Override | |
public void clear() { | |
Arrays.stream(table) | |
.forEach((l) -> l.clear()); | |
size = 0; | |
} | |
/** | |
* Returns the set of keys from the map | |
* @return the set of keys | |
*/ | |
@Override | |
public Set<K> keySet() { | |
Set<K> set = Arrays.stream(table) | |
.flatMap((l) -> l.stream() | |
.map((e) -> e.getKey())) | |
.collect(Collectors.toCollection(LinkedHashSet::new)); | |
return set; | |
} | |
/** | |
* Returns collection of values from the map | |
* @return collection of values | |
*/ | |
@Override | |
public Collection<V> values() { | |
Collection<V> collection = Arrays.stream(table) | |
.flatMap((l) -> l.stream() | |
.map((e) -> e.getValue())) | |
.collect(Collectors.toCollection(ArrayList::new)); | |
return collection; | |
} | |
/** | |
* Returns set of keys and values from the map | |
* @return set of keys and values from the map | |
*/ | |
@Override | |
public Set<Map.Entry<K, V>> entrySet() { | |
Set<Map.Entry<K, V>> set = Arrays.stream(table) | |
.flatMap((l) -> l.stream() | |
.map((e) -> new AbstractMap.SimpleEntry<K, V>(e.getKey(), e.getValue()))) | |
.collect(Collectors.toCollection(LinkedHashSet::new)); | |
return set; | |
} | |
/** | |
* Put the map into String | |
* @return string of keys and values from the map | |
*/ | |
@Override | |
public String toString() { | |
Set<Map.Entry<K, V>> set = entrySet(); | |
String result = "{"; | |
result += set.stream() | |
.map((e) -> e.getKey() + "=" + e.getValue()) | |
.collect(Collectors.joining(", ")); | |
result += "}"; | |
return result; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyMap; | |
import org.junit.Before; | |
import org.junit.Test; | |
import java.util.*; | |
import static org.junit.Assert.*; | |
/** | |
* Unit tests for testing MyMap | |
*/ | |
public class MyMapTest { | |
private Map<String, String> instance; | |
/** | |
* Create a new MyMap for the all tests | |
* @throws Exception | |
*/ | |
@Before | |
public void setUp() throws Exception { | |
instance = new MyMap<String, String>(); | |
} | |
/** | |
* Check size of an empty MyMap | |
* @result Should be zero | |
* @throws Exception | |
*/ | |
@Test | |
public void newMapShouldHasZeroSize() throws Exception { | |
assertEquals(instance.size(), 0); | |
} | |
/** | |
* When add one element size should grows accordingly | |
* @result Size equals one | |
* @throws Exception | |
*/ | |
@Test | |
public void whenAddOneElementSizeShouldGrowsAccordingly() throws Exception { | |
instance.put("key", "value"); | |
assertEquals(instance.size(), 1); | |
} | |
/** | |
* A new map should be empty | |
* @result true | |
* @throws Exception | |
*/ | |
@Test | |
public void newMapShouldBeEmpty() throws Exception { | |
assertTrue(instance.isEmpty()); | |
} | |
/** | |
* When add one element, map should be non empty | |
* @result false | |
* @throws Exception | |
*/ | |
@Test | |
public void whenAddOneElementMapShouldBeNonEmpty() throws Exception { | |
instance.put("key", "value"); | |
assertFalse(instance.isEmpty()); | |
} | |
/** | |
* Searching for the null key should be threw NullPointerException | |
* @result NullPointerException | |
* @throws Exception | |
*/ | |
@Test(expected = NullPointerException.class) | |
public void searchForNullKeyShouldBeThrewNullPointerException() throws Exception { | |
instance.containsKey(null); | |
} | |
/** | |
* Searching for the key when key is not in map should be not found key | |
* @result false | |
* @throws Exception | |
*/ | |
@Test | |
public void searchForKeyWhenKeyIsNotInMapShouldBeNotFoundKey() throws Exception { | |
instance.put("key", "value"); | |
assertFalse(instance.containsKey("0")); | |
} | |
/** | |
* Searching for key when key is in map should be found key | |
* @result true | |
* @throws Exception | |
*/ | |
@Test | |
public void searchForKeyWhenKeyIsInMapShouldBeFoundKey() throws Exception { | |
instance.put("key", "value"); | |
assertTrue(instance.containsKey("key")); | |
} | |
/** | |
* Searching for value when value is in map should be found value | |
* @result true | |
* @throws Exception | |
*/ | |
@Test | |
public void searchForValueWhenValueIsInMapShouldBeFoundValue() throws Exception { | |
instance.put("key", "value"); | |
assertTrue(instance.containsValue("value")); | |
} | |
/** | |
* Searching for value when value is not in map should be not found value | |
* @result false | |
* @throws Exception | |
*/ | |
@Test | |
public void searchForValueWhenValueIsNotInMapShouldBeNotFoundValue() throws Exception { | |
instance.put("key", "value"); | |
assertFalse(instance.containsValue("0")); | |
} | |
/** | |
* Searching for null value should be threw NullPointerException | |
* @result NullPointerException | |
* @throws Exception | |
*/ | |
@Test(expected = NullPointerException.class) | |
public void searchForNullValueShouldBeThrewNullPointerException() throws Exception { | |
instance.containsValue(null); | |
} | |
/** | |
* Getting value be the key when key is in map should return correct value | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void getValueByKeyWhenKeyIsInMapShouldReturnCorrectValue() throws Exception { | |
instance.put("key", "value"); | |
String expResult = "value"; | |
String result = instance.get("key"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Getting value by the key when key is not in map should return null value | |
* @result null | |
* @throws Exception | |
*/ | |
@Test | |
public void getValueByKeyWhenKeyIsNotInMapShouldReturnNullValue() throws Exception { | |
instance.put("key", "value"); | |
String expResult = null; | |
String result = instance.get("yek"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Getting value by the key when the key is null should be threw NullPointerException | |
* @result NullPointerException | |
* @throws Exception | |
*/ | |
@Test(expected = NullPointerException.class) | |
public void getValueByKeyWhenKeyIsNullShouldBeThrewNullPointerException() throws Exception { | |
instance.get(null); | |
} | |
/** | |
* Putting value into head of the list of map should return old value | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void putValueInToHeadOfListOfMapShouldReturnOldValue() throws Exception { | |
instance.put("key1", "value1"); | |
String expResult = "value1"; | |
String result = instance.put("key1", "value2"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Putting value into tail of the list of map should return null | |
* @result null | |
* @throws Exception | |
*/ | |
@Test | |
public void putValueInToTailOfListOfMapShouldReturnNull() throws Exception { | |
String expResult = null; | |
String result = instance.put("key", "value"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Putting null value should be threw null pointer exception | |
* @result NullPointerException | |
* @throws Exception | |
*/ | |
@Test(expected = NullPointerException.class) | |
public void putNullValueShouldBeThrewNullPointerException() throws Exception { | |
instance.put(null, "value"); | |
} | |
/** | |
* Removing value when the values are in map should return old value | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void removeValueWhenValuesAreInMapShouldReturnOldValue() throws Exception { | |
instance.put("key1", "value1"); | |
instance.put("key2", "value2"); | |
instance.put("key3", "value3"); | |
String expResult = "value2"; | |
String result = instance.remove("key2"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Removing value when the values are not in map should return null | |
* @result null | |
* @throws Exception | |
*/ | |
@Test | |
public void removeValueWhenValuesAreNotInMapShouldReturnNull() throws Exception { | |
String expResult = null; | |
String result = instance.remove("0"); | |
assertEquals(expResult, result); | |
} | |
/** | |
* Removing null value should be threw NullPointerException | |
* @result NullPointerException | |
* @throws Exception | |
*/ | |
@Test(expected = NullPointerException.class) | |
public void removeNullValueShouldBeThrewNullPointerException() throws Exception { | |
instance.remove(null); | |
} | |
/** | |
* When add all elements size should be grows accordingly | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenAddAllElementsSizeShouldGrowsAccordingly() throws Exception { | |
Map<String, String> map = new HashMap<>(); | |
map.put("key1", "value1"); | |
map.put("key2", "value2"); | |
map.put("key3", "value3"); | |
instance.putAll(map); | |
assertEquals(3, instance.size()); | |
} | |
/** | |
* When clear the map, the map should has zero size | |
* @result zero | |
* @throws Exception | |
*/ | |
@Test | |
public void whenClearMapMapShouldHasZeroSize() throws Exception { | |
instance.put("key1", "value1"); | |
instance.put("key2", "value2"); | |
instance.clear(); | |
assertEquals(0, instance.size()); | |
} | |
/** | |
* When search all keys in empty map should return empty set of keys | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenSearchAllKeysInEmptyMapShouldReturnEmptySetOfKeys() throws Exception { | |
Set<String> expResult = new HashSet<>(); | |
Set<String> result = instance.keySet(); | |
assertEquals(expResult, result); | |
} | |
/** | |
* When search all keys in the map should be return the set of keys | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenSearchAllKeysInMapShouldReturnSetOfKeys() throws Exception { | |
instance.put("key1", "value1"); | |
instance.put("key2", "value2"); | |
Set<String> expResult = new HashSet<>(); | |
expResult.add("key1"); | |
expResult.add("key2"); | |
assertEquals(expResult, instance.keySet()); | |
} | |
/** | |
* When search all values in empty map should return empty set of values | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenSearchAllValuesInEmptyMapShouldReturnEmptySetOfValues() throws Exception { | |
Collection<String> expResult = new HashSet<>(); | |
Collection<String> result = instance.keySet(); | |
assertEquals(expResult, result); | |
} | |
/** | |
* When search all values in the map should return the set of values | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenSearchAllValuesInMapShouldReturnSetOfValues() throws Exception { | |
instance.put("key1", "value1"); | |
instance.put("key2", "value2"); | |
Collection<String> expResult = new ArrayList<>(); | |
expResult.add("value1"); | |
expResult.add("value2"); | |
assertEquals(expResult, instance.values()); | |
} | |
/** | |
* When search all keys and values should return set of keys and values | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void whenSearchAllKeysAndValuesShouldRuturnSetOfKeysAndValues() throws Exception { | |
String key1 = "key1"; | |
String key2 = "key2"; | |
String value1 = "value1"; | |
String value2 = "value2"; | |
instance.put(key1, value1); | |
instance.put(key2, value2); | |
Set<Map.Entry<String, String>> expResult = new LinkedHashSet<>(); | |
expResult.add(new AbstractMap.SimpleEntry<String, String>(key1, value1)); | |
expResult.add(new AbstractMap.SimpleEntry<String, String>(key2, value2)); | |
assertEquals(expResult, instance.entrySet()); | |
} | |
/** | |
* Printing map should return format string of keys and values from the map | |
* @result expResult | |
* @throws Exception | |
*/ | |
@Test | |
public void printMapShouldReturnFormatStringOfKeysAndValuesOfMap() throws Exception { | |
String key1 = "key1"; | |
String key2 = "key2"; | |
String value1 = "value1"; | |
String value2 = "value2"; | |
instance.put(key1, value1); | |
instance.put(key2, value2); | |
int index1 = key1.hashCode() % 100; | |
int index2 = key2.hashCode() % 100; | |
String expResult = "{"; | |
expResult += (index1 < index2) ? key1 + "=" + value1 + ", " : key2 + "=" + value2 + ", "; | |
expResult += (index1 < index2) ? key2 + "=" + value2 : key1 + "=" + value1; | |
expResult += "}"; | |
String result = instance.toString(); | |
assertEquals(expResult, result); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment