1.什么是哈希表

哈希表是结合哈希算法结合其他数据结构构成的一种数据结构。

为什么会产生这样的数据结构呢?

主要原因是在理想情况下,取出和放入元素所消耗的时间复杂度为

\( O(1) \)

比如我们看一种常见的实现方式,哈希算法+数组+链表构成的哈希表:

假设数组index从1开始,那么根据哈希算法,元素1就放在数组下标为1的地方,元素3就放在数组下标为3的地方,元素5放在数组下标为1的地方,但是因为元素1已经在数组中了,对于这种情况,我们的一种解决方式就是元素1和元素5通过链表连接,元素1仍然在数组中,新加入的元素5放在元素1后面,元素1的next节点指向元素5.

img

2. java实现

哈希表的用途很广,比如java的HashMap、HashSet。

对于哈希表,我们的操作主要是PUT和GET操作。

下面就来看看怎么实现简易版本的HashMap。

import java.util.Arrays;
import java.util.Objects;

/**
 * key and value are not permitted null
 * @author bennetty74
 * @since 2021.10.24
 */
public class HashMap <K,V> implements Map<K,V>{
    // 声明一个ENTRY数组用于存放元素
    private Entry<K,V>[] entries;
    private final double loadFactor = 0.75d;
    private int capacity = 8;
    private int size = 0;

    @SuppressWarnings("unchecked")
    public HashMap() {
        entries = new Entry[this.capacity];
    }

    @SuppressWarnings("unchecked")
    public HashMap(int capacity) {
        this.capacity = capacity;
        entries = new Entry[capacity];
    }

    @Override
    public boolean contains(K key) {
        int idx = getIndex(key);
        if (entries[idx] == null) {
            return false;
        }
        Entry<K, V> entry = entries[idx];
        while (entry != null) {
            if (entry.key.equals(key)) {
                return true;
            }
            entry = entry.next;
        }
        return false;
    }
    // put操作,不允许key和value为null
    public boolean put(K key, V value) {
        if (Objects.isNull(key) || Objects.isNull(value)) {
            throw new IllegalArgumentException("Key or value is null");
        }
        int idx = getIndex(key);
        // if null, put in entries array
        if (Objects.isNull(entries[idx])) {
            entries[idx] = new Entry<>(key, value);
        } else {
            // else put at the head of Entry list
            Entry<K, V> newEntry = new Entry<>(key, value);
            newEntry.next = entries[idx];
            entries[idx] = newEntry;
        }
        // if load factor greater than 0.75, then rehash
        if (size++ / (double) capacity > 0.75f) {
            rehash();
        }
        return true;
    }

    private void rehash() {
        capacity = 2 * capacity;
        Entry<K, V>[] oldEntries = Arrays.copyOf(this.entries, capacity);
        entries = new Entry[capacity];
        for (Entry<K, V> oldEntry : oldEntries) {
            if (oldEntry != null) {
                put(oldEntry.key, oldEntry.value);
            }
        }
    }

    public V get(K key) {
        if (Objects.isNull(key)) {
            throw new IllegalArgumentException("Key is null");
        }
        // get idx by key's hashCode
        int idx = getIndex(key);
        Entry<K, V> entry = entries[idx];
        if (!Objects.isNull(entry)) {
            while (!Objects.isNull(entry)) {
                if (key.equals(entry.key)) {
                    return entry.value;
                }
                entry = entry.next;
            }
        }
        // not found the specific key, return null
        return null;
    }
    // 此处的hash算法很简单,就是根据key的hashCode除以entry数组的容量的余数作为数组下标存放元素
    private int getIndex(K key) {
        return key.hashCode() % capacity;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("HashMap { ");
        for (int i = 0; i < capacity; i++) {
            if (entries[i] != null) {
                Entry<K, V> entry = entries[i];
                while (entry != null) {
                    sb.append(entry.key).append("=").append(entry.value).append(",");
                    entry = entry.next;
                }
            }
        }
        sb.replace(sb.length() - 1, sb.length(), "").append(" }");
        return sb.toString();
    }

    // Entry类,包含HashMap的key和value
    private static class Entry<K,V> {
        K key;
        V value;

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

        Entry<K,V> next;
    }


}

benntty74
49 声望0 粉丝

小透明