Search code examples
javadata-structureslinked-listequalslinkedhashmap

Java Linkedhashmap equals method custom implementation


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.


Solution

  • 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));
    }