Implement Map based on array or linked list

IDEA plugins commonly used by programmers:https://github.com/silently9527/ToolsetIdeaPlugin

WeChat public account: Beta learns Java

Preface

The Map in JAVA mainly associates a key with a value. Although many Map implementations have been provided in JAVA, in order to learn and master commonly used data structures, I will implement the function of Map myself from the beginning of this article. This article is mainly implemented in two ways, arrays and linked lists, and then provides binary trees, red Black tree, the version of the hash table is implemented. By handwriting each version of Map, you can master the advantages and disadvantages of each data structure, and you can choose a suitable Map according to your needs in actual work.

Definition of Map API

Before we start, we need to define the interface definition of Map, and subsequent versions will be implemented based on this interface

public interface Map<K, V> {
    void put(K key, V value);

    V get(K key);

    void delete(K key);

    int size();

    Iterable<K> keys();

    default boolean contains(K key) {
        return get(key) != null;
    }

    default boolean isEmpty() {
        return size() == 0;
    }
}

This interface is the simplest Map definition, I believe these methods will not be unfamiliar to Java programmers;

Realize Map based on linked list

  1. Based on the linked list implementation, first we need to define a Node node to indicate the key and vlaue we need to store
class Node {
    K key;
    V value;
    Node next;

    public Node(K key, V value, Node next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }

}
  1. The realization idea of ​​the get method is to traverse the linked list, and then compare whether the keys in each Node are equal, if they are equal, return value, otherwise return null
@Override
public V get(K key) {
    return searchNode(key).map(node -> node.value).orElse(null);
}

public Optional<Node> searchNode(K key) {
    for (Node node = root; node != null; node = node.next) {
        if (node.key.equals(key)) {
            return Optional.of(node);
        }
    }
    return Optional.empty();
}
  1. The idea of ​​implementing the put method is to traverse the linked list, and then compare whether the key value of each Node is equal. If it is equal, the value is overwritten. If no node with the same key is found, then a new Node is created and placed at the beginning of the linked list.
@Override
public void put(K key, V value) {
    Optional<Node> optionalNode = searchNode(key);

    if (optionalNode.isPresent()) {
        optionalNode.get().value = value;
        return;
    }
    this.root = new Node(key, value, root);
}
  1. The delete method also needs to traverse the linked list, because ours is a singly linked list. There are two ways to delete a node. The first one is to record the previous node of the current node when traversing the linked list, and put the next node of the previous node. Point to the current node next; the second type, when traversing to the node that needs to be deleted, copy the key and value of the next node that needs to be deleted to the node that needs to be deleted, and point the next pointer to next.next, such as: first -> A -> B -> C -> D -> E -> F -> G -> NULL, to delete the C node, copy the D node completely to c, then C -> E, and delete C in disguise
@Override
public void delete(K key) {
// 第一种实现:
//        for (Node node = first, preNode = null; node != null; preNode = node, node = node.next) {
//            if (node.key.equals(key)) {
//                if (Objects.isNull(preNode)) {
//                    first = first.next;
//                } else {
//                    preNode.next = node.next;
//                }
//            }
//        }

// 第二中实现:
    for (Node node = first; node != null; node = node.next) {
        if (node.key.equals(key)) {
            Node next = node.next;
            node.key = next.key;
            node.value =next.value;
            node.next = next.next;
        }
    }
}

Analyze the map implemented based on the linked list above, each time put, get, delete needs to traverse the entire linked list, which is very inefficient, unable to process a large amount of data, and the time complexity is O(N)

Implement Map based on array

The implementation based on the linked list is very inefficient, because each operation needs to traverse the linked list. If our data is in order, then we can use the binary search method when looking up, then the get method will speed up a lot

In order to reflect that our Map is ordered, we need to redefine an ordered Map

public interface SortedMap<K extends Comparable<K>, V> extends Map<K, V> {
    int rank(K key);
}

This definition requires that the key must implement the interface Comparable. The rank method returns the subscript corresponding to the array if the key value exists, and returns the number less than the key key if it does not exist.

  • In the array-based implementation, we will define two array variables to store the keys and values;
  • The implementation of the rank method: Since our entire array is in order, we can find the binary search method (see "It's Time for Brother to Review the Data Structure and Algorithm"), if it exists, return the following table of the array, if Returns 0 if it doesn't exist
@Override
public int rank(K key) {
    int lo = 0, hi = size - 1;
    while (lo <= hi) {
        int mid = (hi - lo) / 2 + lo;
        int compare = key.compareTo(keys[mid]);
        if (compare > 0) {
            lo = mid + 1;
        } else if (compare < 0) {
            hi = mid - 1;
        } else {
            return mid;
        }
    }
    return lo;
}
  • Get method implementation: Based on the rank method, the returned keys[index] is judged to be compared with the key, if they are equal, values[index] are returned, and if they are not equal, null is returned.
@Override
public V get(K key) {
    int index = this.rank(key);
    if (index < size && key.compareTo(keys[index]) == 0) {
        return values[index];
    }
    return null;
}
  • Put method implementation: Based on the rank method, judge the returned keys[index] and compare it with the key. If they are equal, directly modify the value of values[index]. If they are not equal, the key does not exist, and the array needs to be inserted and moved.
@Override
public void put(K key, V value) {
    int index = this.rank(key);
    if (index < size && key.compareTo(keys[index]) == 0) {
        values[index] = value;
        return;
    }

    for (int j = size; j > index; j--) {
        this.keys[j] = this.keys[j--];
        this.values[j] = this.values[j--];
    }
    keys[index] = key;
    values[index] = value;
    size++;
}
  • The delete method is implemented: judge whether the key exists through the rank method, and return directly if it does not exist, and move the array if it exists
@Override
public void delete(K key) {
    int index = this.rank(key);
    if (Objects.isNull(keys[index]) || key.compareTo(keys[index]) != 0) {
        return;
    }
    for (int j = index; j < size - 1; j++) {
        keys[j] = keys[j + 1];
        values[j] = values[j + 1];
    }
    keys[size - 1] = null;
    values[size - 1] = null;
    size--;
}

Based on the array implementation of Map, although the binary search method used by the get method is very fast O(logN), the efficiency is still very low when processing large amounts of data, because the put method is still too slow; we will implement it based on a binary tree in the next article Map, continue to improve and improve efficiency


All the source code in the article has been put into the github warehousehttps://github.com/silently9527/JavaCore

Finally (point attention, don’t get lost)

There may be more or less deficiencies and mistakes in the article. If you have suggestions or comments, you are welcome to comment and exchange.

Finally, writing is not easy, please don’t prostitute me in vain , I hope friends can like , comment and follow Sanlian, because these are all the sources of motivation for my sharing.

Guess you like

Origin blog.51cto.com/15049004/2667918