So a hashmap is a hash-based implementation of a map structure in java. I've figured out how to get the hashmap put method to work, but I want to write a method that removes the key value pair, and I'm having trouble implementing it.
The only thing I can really understand right now is how to tell the function to stop in the event that the key is empty or doesn't exist.. I'd love any sort of help. An explanation as to how the method will work, or some basic pseudo-code examples would be much appreciated.
This is what I have in the delete method so far:
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
If it helps, here is my completed Map Entry class:
public class MapEntry<K, V> {
MapEntry<K, V> next;
K key;
V value;
public MapEntry(K key, V value) {
this.setKey(key);
this.setValue(value);
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setNext(MapEntry<K, V> next) {
this.next = next;
}
public MapEntry<K, V> getNext() {
return next;
}
}
Also, here's the entirety of my HashMap class if it helps.
public class HashMap<K, V> {
private int DEFAULT_CAPACITY = 10;
private MapEntry<K, V>[] Hash;
private int size;
public HashMap() {
Hash = new MapEntry[DEFAULT_CAPACITY];
}
public int getHashCode(K key) {
int bucketIndex = key.hashCode() % Hash.length;
return bucketIndex;
}
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
MapEntry<K, V> entry = Hash[getHashCode(key)];
while (entry != null && !key.equals(entry.getKey()))
entry = entry.getNext();
if (entry != null)
return entry.getValue();
else
return null;
}
/**
*
* #param key
* #param value
* The put method works by associating the specified value with
* the given key in the map.
* If the key is already in the map,
* the old value is replaced with the new one.
*/
public void put(K key, V value) {
int keyBucket = hash(key);
MapEntry<K, V> temp = Hash[keyBucket];
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
temp.value = value;
return;
}
temp = temp.next;
}
Hash[keyBucket] = new MapEntry<K, V>(key, value);
size++;
}
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
public void print(){
//Bonus Method
}
private int hash(K key) {
if (key == null) {
return 0;
} else {
return Math.abs(key.hashCode() % this.Hash.length);
}
} }
Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically, this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next; otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket.
source code
public class MyInternalMap<K, V> implements Map<K, V> {
/**
* https://stackoverflow.com/questions/16266459/implementing-a-remove-method-in-a-java-hashmap
*/
private final int initialCapacity;
private MyMapEntry<K, V>[] mapEntries;
private int size;
public MyInternalMap() {
this(16);
}
public MyInternalMap(int initialCapacity) {
this.initialCapacity = initialCapacity;
mapEntries = new MyMapEntry[initialCapacity];
}
#Override
public int size() {
return size;
}
#Override
public boolean isEmpty() {
return size == 0;
}
#Override
public boolean containsKey(Object key) {
return get(key) != null;
}
#Override
public boolean containsValue(Object value) {
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
if (containsValue(value, mapEntry)) {
return true;
}
}
return false;
}
private boolean containsValue(Object value, MyMapEntry<K, V> mapEntry) {
if (mapEntry == null) {
return false;
}
if (value == mapEntry.getValue() || mapEntry.getValue().equals(value)) {
return true;
}
return containsValue(value, mapEntry.next);
}
#Override
public V get(Object key) {
if (key == null) {
return null;
}
MyMapEntry<K, V> entry = mapEntries[getHashCode(key)];
while (entry != null) {
if (key.equals(entry.key)) {
return entry.value;
}
entry = entry.next;
}
return null;
}
#Override
public V put(K key, V value) {
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null) {
//create new head node in this bucket
mapEntries[keyBucket] = new MyMapEntry<>(key, value);
size++;
return null;
}
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
V returnValue = temp.value;
temp.value = value;
return returnValue;
}
temp = temp.next;
}
//create new node in this bucket
mapEntries[keyBucket].next = new MyMapEntry<>(key, value);
size++;
return null;
}
#Override
public V remove(Object key) {
/**
* Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically,
* this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next;
* otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket
*/
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null)
return null;
MyMapEntry<K, V> prev = temp;
while (temp != null) {
if (temp.key != null && temp.key.equals(key)) {
V valueReturn = temp.value;
if (prev == temp) { //first element?
mapEntries[keyBucket] = temp.next;
} else {
prev.next = temp.next;
}
size--;
return valueReturn;
}
prev = temp;
temp = temp.next;
}
return null;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
//TODO impl
}
#Override
public void clear() {
mapEntries = new MyMapEntry[initialCapacity];
size = 0;
}
#Override
public Set<K> keySet() {
Set<K> resultKeys = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addKeySet(mapEntry, resultKeys);
}
return resultKeys;
}
private void addKeySet(MyMapEntry<K, V> mapEntry, Set<K> resultKeys) {
if (mapEntry != null) {
resultKeys.add(mapEntry.key);
addKeySet(mapEntry.next, resultKeys);
}
}
#Override
public Collection<V> values() {
Collection<V> resultValues = new ArrayList<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addValue(mapEntry, resultValues);
}
return resultValues;
}
private void addValue(MyMapEntry<K, V> mapEntry, Collection<V> resultValues) {
if (mapEntry != null) {
resultValues.add(mapEntry.value);
addValue(mapEntry.next, resultValues);
}
}
#Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> entrySetResult = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addEntrySet(mapEntry, entrySetResult);
}
return entrySetResult;
}
private void addEntrySet(MyMapEntry<K, V> mapEntry, Set<Entry<K, V>> entrySetResult) {
if (mapEntry != null) {
entrySetResult.add(mapEntry);
addEntrySet(mapEntry.next, entrySetResult);
}
}
private int getHashCode(Object key) {
if (key == null)
return 0;
int bucketIndex = Math.abs(key.hashCode()) % initialCapacity;
return bucketIndex;
}
class MyMapEntry<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
private MyMapEntry<K, V> next;
public MyMapEntry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public K getKey() {
return key;
}
#Override
public V getValue() {
return value;
}
#Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}
}
public class MyInternalMapTest {
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapTest(Map<Integer, String> map) {
assertNull(map.get(0));
assertNull(map.get(null));
assertNull(map.remove(0));
assertNull(map.remove(null));
assertNull(map.remove(1));
assertEquals(0, map.size());
assertNull(map.put(1, "1"));
assertEquals(1, map.size());
assertEquals("1", map.put(1, "2"));
assertEquals(1, map.size());
assertEquals("2", map.get(1));
assertEquals(1, map.size());
assertNull(map.put(2, "3"));
assertEquals(2, map.size());
assertEquals("2", map.remove(1));
assertEquals(1, map.size());
assertNull(map.remove(1));
assertEquals("3", map.remove(2));
assertEquals(0, map.size());
}
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapSameHashCodeTest(Map<Integer, String> map) {
assertNull(map.put(1, "1"));
assertEquals("1", map.get(1));
assertNull(map.put(17, "2"));
assertEquals("1", map.get(1));
assertEquals("2", map.get(17));
assertEquals("1", map.get(1));
assertTrue(map.containsValue("1"));
assertTrue(map.containsValue("2"));
assertFalse(map.containsValue("3"));
assertEquals(Arrays.asList("1", "2"), map.values().stream().sorted().collect(Collectors.toList()));
}
private static Stream<Arguments> mapArgumentsProvider() {
return Stream.of(
Arguments.of(new MyInternalMap<>()),
Arguments.of(new HashMap<>())
);
}
}
Related
I am trying to implement my own LinkedHashMap using chaining strategy for deal with collisions. Below code snippet shows what I have tried so far.
CustomLinkedHashMap
public class CustomLinkedHashMap<K, V> {
private Entry<K, V>[] table;
private int capacity = 4;
private Entry<K, V> header;
private Entry<K, V> last;
#SuppressWarnings("unchecked")
public CustomLinkedHashMap() {
table = new Entry[capacity];
}
#SuppressWarnings("unchecked")
public boolean equals(Object that) {
if (!(that instanceof CustomLinkedHashMap)) {
return false;
}
CustomLinkedHashMap<K, V> other = (CustomLinkedHashMap<K, V>) that;
// if lists are empty
if (header == null) {
return other.header == null;
}
if (!header.equals(other.header)) {
return false;
}
// Just one element
if (header == last) {
return true;
}
if (!header.equals(other.last)) {
return false;
}
Entry<K, V> thisNode = header;
Entry<K, V> otherNode = other.header;
while (thisNode.next != last) {
thisNode = thisNode.next;
otherNode = otherNode.next;
if (!(thisNode.equals(otherNode))) {
return false;
}
}
return true;
}
public void put(K newKey, V data) {
if (newKey == null)
return; //nulls not allowed
int hash = hash(newKey);
Entry<K, V> newEntry = new Entry<K, V>(newKey, data, null);
maintainOrderAfterInsert(newEntry);
if (table[hash] == null) {
table[hash] = newEntry;
} else {
Entry<K, V> previous = null;
Entry<K, V> current = table[hash];
while (current != null) { // reached to the last entry of bucket.
if (current.key.equals(newKey)) {
if (previous == null) { //node has to be insert on first of bucket.
newEntry.next = current.next;
table[hash] = newEntry;
return;
} else {
newEntry.next = current.next;
previous.next = newEntry;
return;
}
}
previous = current;
current = current.next;
}
previous.next = newEntry;
}
}
public boolean isEmpty() {
return header == null;
}
public void clear() {
if (table != null && capacity > 0) {
header = null;
last = null;
capacity = 0;
}
}
public void printMap() {
Entry<K, V> currentEntry = header;
while (currentEntry != null) {
System.out.print("{" + currentEntry.key + "=" + currentEntry.value + "}" + " ");
currentEntry = currentEntry.after;
}
}
private int hash(K key) {
return Math.abs(key.hashCode()) % capacity;
}
static class Entry<K, V> {
K key;
V value;
Entry<K, V> next;
Entry<K, V> before, after;
public Entry(K key, V value, Entry<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
#SuppressWarnings("unchecked")
public boolean equals(Object object) {
if (object == this)
return true;
if (object instanceof Entry) {
Entry<K, V> e = (Entry<K, V>) object;
if (Objects.equals(key, e.key) && Objects.equals(value, e.value)) {
return true;
}
}
return false;
}
}
}
CustomLinkedHashMap class Full implementation here.
As for my implementation when I try equals method it always returns me false.
CustomLinkedHashMap<Integer, String> lhm = new CustomLinkedHashMap<>();
lhm.put(13, "John");
lhm.put(3, "Holmes");
lhm.put(19, "Jenifer");
CustomLinkedHashMap<Integer, String> lhm2 = new CustomLinkedHashMap<>();
lhm2.put(13, "John");
lhm2.put(3, "Holmes");
lhm2.put(19, "Jenifer");
System.out.println(lhm.equals(lhm2)); // Returns true when using java.util.LinkedHashMap
Any suggestions would be appreciable.
Your equals for CustomLinkedHashMap is way too complicated. All you really need is to check the entries (and their ordering):
#Override
public boolean equals(#Nullable Object thatObject) {
if (!(thatObject instanceof CustomLinkedHashMap)) {
return false;
}
CustomLinkedHashMap<?,?> thatMap =
(CustomLinkedHashMap<?,?>) thatObject;
return Arrays.equals(this.table, thatMap.table);
}
You also need a solid implementation for Entry, which checks the key and value against another:
#Override
public boolean equals(#Nullable Object thatObject) {
if (!(thatObject instanceof Entry)) {
return false;
}
Entry<?,?> thatEntry = (Entry<?,?>) thatObject;
if (!Objects.equals(this.key, thatEntry.key)) {
return false;
}
return Objects.equals(this.value, thatEntry.value));
}
I am trying to implement an ArrayMap<K,V> based on an AbstractMap<K, V> but I don't know why my entrySet() should return and I have tried to make it return something using AbstractSet but it really doesn't like my Arraylist of entries...
public class ArrayMap<K, V> extends AbstractMap<K, V> {
private Set<Entry<K, V>> entries = null;
private ArrayList<ArrayMapEntry<K, V>> list;
public ArrayMap() {
list = new ArrayList<ArrayMapEntry<K, V>>();
}
public ArrayMap(int initialCapacity) {
list = new ArrayList<ArrayMapEntry<K, V>>(initialCapacity);
}
#Override
public Set<Entry<K, V>> entrySet() {
if (entries == null) {
entries = new AbstractSet<Entry<K, V>>() {
#Override
public void clear() {
list.clear();
}
#Override
public Iterator<Entry<K, V>> iterator() {
return list.iterator();
}
#Override
public int size() {
return list.size();
}
};
}
return entries;
}
static class ArrayMapEntry<K, V> implements Map.Entry<K, V> {
protected K key;
protected V value;
public ArrayMapEntry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public K getKey() {
return key;
}
#Override
public V getValue() {
return value;
}
#Override
public V setValue(V v) {
value = v;
return v;
}
public String toString() { return key + " : " + value; }
public boolean equals(Object o) {
if (!(o instanceof ArrayMapEntry)) {
return false;
}
ArrayMapEntry e = (ArrayMapEntry) o;
return (key == null? e.getKey() == null : key.equals(e.getKey()))
&& (value == null ? e.getValue() == null : value.equals(e.getValue()));
}
public int hashCode() {
int keyHash = (key == null ? 0 : key.hashCode());
int valueHash = (value == null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
}
I'm tasked to implement a remove() method for my Sorted Binary Tree. I need to pass a set of tests which was assigned by our teacher, and I'm struggling to do so. This is the code I have currently, which was heavily borrowed from our schoolbook.
#Override
#SuppressWarnings("unchecked")
public V remove(Object key) throws NoSuchElementException {
if(!containsKey((K) key)){
throw new NoSuchElementException();
}
ReturnObject oldEntry = new ReturnObject(null);
SortedTreeMap<K, V> newRoot = removeEntry(getRoot(this), (K) key, oldEntry);
if(newRoot != null){
setRoot(newRoot.data);
setLeftChild(newRoot.leftChild);
setRightChild(newRoot.rightChild);
} else{
this.data = null;
}
if(oldEntry.get() != null){
size--;
}
return oldEntry.get();
}
/*
* Removes the entry in a given root node of a subtree.
* rootNode is the root node of the subtree.
* Returns the root node of the revised subtree.
*
* */
private SortedTreeMap<K,V> removeFromRoot(SortedTreeMap<K, V> rootNode){
if(rootNode.hasLeftChild() && rootNode.hasRightChild()){
SortedTreeMap<K,V> leftSubtreeRoot = rootNode.getLeftChild();
SortedTreeMap<K,V> largestNode = findLargest(leftSubtreeRoot);
rootNode.setRoot(largestNode.getRoot());
rootNode.setLeftChild(removeLargest(leftSubtreeRoot));
} else if(rootNode.hasRightChild()) {
rootNode = rootNode.getRightChild();
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Finds the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the node containing the largest entry in the tree.
*
* */
private SortedTreeMap<K,V> findLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
rootNode = findLargest(rootNode.getRightChild());
}
return rootNode;
}
/*
* Removes the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the root node of the revised tree.
*
* */
private SortedTreeMap<K,V> removeLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rightEntry = removeLargest(rightEntry);
rootNode.setRightChild(rightEntry);
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Removes an entry from the tree rooted at a given node.
* rootNode is a reference to the root of a tree.
* entry is the object to be removed.
* oldEntry is an object whose data field is null
* Returns the root node of the resulting tree; if entry matches
* an entry in the tree, oldEntry's data field is the entry
* that was removed from the tree; otherwise it is null.
* */
private SortedTreeMap<K,V> removeEntry(SortedTreeMap<K,V> rootNode, K entry, ReturnObject oldEntry){
if(rootNode != null){
K rootData = rootNode.data.key;
int comparison = entry.compareTo(rootData);
if(comparison == 0){
oldEntry.set(rootNode.data.value);
rootNode = removeFromRoot(rootNode);
} else if(comparison < 0){
SortedTreeMap<K,V> leftEntry = rootNode.getLeftChild();
SortedTreeMap<K,V> subtreeRoot = removeEntry(leftEntry, entry, oldEntry);
rootNode.setLeftChild(subtreeRoot);
} else{
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rootNode.setRightChild(removeEntry(rightEntry, entry, oldEntry));
}
}
return rootNode;
}
And this is the test I'm struggling to pass:
/**
* Check that entry is no longer in map after remove
*/
public Property remove_removes_entry() {
return property(isKVList,
kvs -> implies(kvs.length() > 0,
() -> property(choose(0, kvs.length()-1),
i -> {
P2<Integer, String> entry = kvs.index(i);
SortedTreeMap<Integer, String> tm = new SortedTreeMap<>(intOrd.toComparator());
kvs.foreachDoEffect(kv -> tm.add(kv._1(), kv._2()));
tm.remove(entry._1());
List<Integer> keys = fromIterator(tm.keys().iterator());
return prop(!keys.exists(key -> key.equals(entry._1())));
})
)
);
}
The error message I'm getting is this:
java.lang.Error: Falsified after 2 passed tests with arguments:
List(List((0,gzprxt),(4,lntpqj),(-5,caki),(-6,jzf)),2)
I can get in between 0 to 60 passes and I don't get which scenario the remove method fails in. I've tried to make a Junit test, but didn't succeed.
Here is some more context for my class:
public class Entry<K, V> {
public final K key;
public final V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public boolean equals(Object o) {
if (o instanceof Entry) {
Entry other = (Entry)o;
return this.key.equals(other.key) && this.value.equals(other.value);
}
return false;
}
}
public class SortedTreeMap<K extends Comparable<? super K>, V> implements ISortedTreeMap<K, V> {
private int size;
private Entry<K,V> data;
private SortedTreeMap<K,V> leftChild, rightChild, parent;
private Comparator<K> keyComparator;
private SortedTreeMap(K key, V value, Comparator<K> keyComparator, SortedTreeMap<K, V> parent){
data = new Entry<>(key, value);
this.parent = parent;
this.keyComparator = keyComparator;
}
public SortedTreeMap(Comparator<K> kComparator) {
keyComparator = kComparator;
}
private Entry<K,V> getRoot(){
return this.data;
}
private SortedTreeMap<K,V> getLeftChild(){
return this.leftChild;
}
private SortedTreeMap<K,V> getRightChild(){
return this.rightChild;
}
private void setLeftChild(SortedTreeMap<K, V> newLeftChild){
leftChild = newLeftChild;
}
private void setRightChild(SortedTreeMap<K, V> newRightChild){
rightChild = newRightChild;
}
private void setRoot(Entry<K, V> newRoot){
data = newRoot;
}
private boolean hasLeftChild(){
return leftChild != null;
}
private boolean hasRightChild(){
return rightChild != null;
}
#Override
public V add(K key, V value) {
V result = null;
if(isEmpty()) {
data = new Entry<>(key, value);
} else{
int comparison = keyComparator.compare(key, data.key);
SortedTreeMap<K,V> newChild = new SortedTreeMap<>(key, value, keyComparator, this);
if(comparison < 0){
if (hasLeftChild()) {
result = leftChild.add(key, value);
} else{
setLeftChild(newChild);
}
} else if(comparison > 0){
if (hasRightChild()) {
result = rightChild.add(key, value);
} else{
setRightChild(newChild);
}
} else{
result = data.value;
this.data = new Entry<>(key, value);
}
}
if (result == null){
size++;
}
return result;
}
#Override
public Iterable<K> keys() {
return new KeyIterator(this);
}
public class KeyIterator implements Iterable<K>, Iterator<K>{
private SortedTreeMap<K,V> next;
KeyIterator(SortedTreeMap<K, V> root){
next = root;
while(next.leftChild != null){
next = next.leftChild;
}
}
#Override
public Iterator<K> iterator() {
return this;
}
public boolean hasNext(){
return next != null && !next.isEmpty();
}
public K next(){
if(!hasNext()){
throw new NoSuchElementException();
}
SortedTreeMap<K,V> r = next;
if(next.rightChild != null){
next = next.rightChild;
while(next.leftChild != null){
next = next.leftChild;
}
return r.data.key;
}
while(true){
if(next.parent == null){
next = null;
return r.data.key;
}
if(next.parent.leftChild == next){
next = next.parent;
return r.data.key;
}
next = next.parent;
}
}
}
I'm trying to create the entrySet() method for a custom HashMap. I've attached both my entry class, called MyEntry, and my entrySet() method.
I receive the error:
The return type is incompatible with Map<K,V>.entrySet()
... and the return type is underlined in red. I don't understand what this is expressing.
#Override
public Set<HashMap<K,V>.MyEntry<K, V>> entrySet(){
if (entrySetView != null) return entrySetView;
else return entrySetView = new AbstractSet<HashMap<K,V>.MyEntry<K,V>>() {
#Override
public Iterator<HashMap<K,V>.MyEntry<K, V>> iterator() {
return new HashIterator<HashMap<K,V>.MyEntry<K, V>>() {
#Override
public HashMap<K,V>.MyEntry<K, V> next() {
return nextEntry();
}
};
}
#Override
public int size() {
return HashMap.this.size();
}
#Override
public void clear() {
HashMap.this.clear();
}
};
}
public class MyEntry<K, V> implements Map.Entry<K,V>{
private K key;
private V value;
private MyEntry<K,V> next;
private int hash;
public MyEntry() {
counter++;
}
public MyEntry(K key, V value, MyEntry<K,V> next, int h) {
this.key = key;
this.value = value;
this.next = next;
this.hash = h;
counter++;
}
public boolean equals(Object o) {
if(this == o) return true;
if (!(o instanceof MyEntry))
return false;
MyEntry entry = (MyEntry)o;
Object k1 = getKey();
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setKey(K key) {
this.key = key;
}
public Object setValue(Object val) {
V oldValue = value;
value = (V) val;
return oldValue;
}
public MyEntry<K,V> getNext(){
return next;
}
public void setNext(MyEntry<K,V> n) {
this.next = n;
}
public String toString() {
return "{" + getKey() + "," + getValue() + "}";
}
}
entrySet of the Map interface has a return type of Set<Map.Entry<K, V>>, so you can't return a Set<HashMap<K,V>.MyEntry<K, V>>.
Set<HashMap<K,V>.MyEntry<K, V>> is not a sub-interface of Set<Map.Entry<K, V>>.
I believe something like this should work (you might have to change the type of entrySetView as well):
#Override
public Set<Map.Entry<K, V>> entrySet(){
if (entrySetView != null)
return entrySetView;
else
return entrySetView = new AbstractSet<Map.Entry<K, V>>() {
#Override
public Iterator<Map.Entry<K, V>> iterator() {
return new HashIterator<Map.Entry<K, V>>() {
#Override
public Map.Entry<K, V> next() {
return nextEntry();
}
};
}
#Override
public int size() {
return HashMap.this.size();
}
#Override
public void clear() {
HashMap.this.clear();
}
};
}
I've implemented a HashMap with Linear Probing for hash collisions.
import java.util.Optional;
#SuppressWarnings("unchecked")
public abstract class HashMap<T, R> {
private static final int MIN_CAPACITY = 2;
private Entry<T, R>[] table;
private int internalSize, size;
private float fillRatio;
public HashMap() {
this(MIN_CAPACITY);
}
public HashMap(int initialCapacity) {
this(initialCapacity, .75f);
}
public HashMap(int initialCapacity, float fillRatio) {
this.table = new Entry[Math.max(MIN_CAPACITY, initialCapacity)];
this.fillRatio = fillRatio;
}
public Optional<R> put(T key, R value) {
int index = getIndex(key);
Entry<T, R> current = table[index];
table[index] = new Entry<>(key, value);
if(value == null && current != null && current.getValue() != null) {
size--;
} else if(value != null && (current == null || current.getValue() == null)){
size++;
}
if(current == null && ++internalSize >= (table.length * fillRatio)) {
resizeTable();
}
if(current != null) {
return Optional.ofNullable(current.getValue());
}
return Optional.empty();
}
public Optional<R> get(T key) {
int index = getIndex(key);
Entry<T, R> entry = table[index];
if(entry != null)
return Optional.ofNullable(entry.getValue());
return Optional.empty();
}
public boolean has(T key) {
return get(key).isPresent();
}
public int getSize() {
return size;
}
protected void resizeTable() {
internalSize = size = 0;
Entry<T, R>[] tmp = table;
table = new Entry[(int) ((table.length /fillRatio)* 2)];
for(Entry<T, R> entry : tmp){
if(entry != null) {
put(entry.getKey(), entry.getValue());
}
}
}
private int getIndex(T key) {
int hash = key.hashCode();
int index = (((hash % table.length) + table.length) % table.length);
while(table[index] != null && table[index].getKey().hashCode() != hash) {
if(++index == table.length) {
index = 0;
}
}
return index;
}
public static final class Entry <T, R> {
private final T key;
private final R value;
public Entry(T key, R value) {
this.key = key;
this.value = value;
}
public T getKey() {
return key;
}
public R getValue() {
return value;
}
}
}
it seems to work exactly as expected except for every 100,000 entries or so will return the wrong value for a hash. I can reproduce it fairly reliably with this test
java.util.HashMap<UUID, UUID> javaMap = new java.util.HashMap<>();
HashMap<UUID, UUID> map = new HashMap<>();
for (int i = 0; i < 200000; i++) {
UUID key = UUID.randomUUID(), value = UUID.randomUUID();
javaMap.put(key, value);
map.put(key, value);
}
for (java.util.HashMap.Entry<UUID, UUID> entry : javaMap.entrySet()) {
Optional<UUID> value = map.get(entry.getKey());
assertTrue(value.isPresent());
assertEquals(value.get(), entry.getValue());
}
I'm not seeing what my problem is and I'm not thinking of a good way to debug such a rare occurrence. Any thoughts on what I might be doing wrong or how to debug this without spending forever on it?
How does a Java HashMap handle different objects with the same hash code? answered my question. My HashMap implementation does not handle different objects with the same hash code. It only works correctly if hashcodes are unique to equal objects.