HashMap&&HshTable以及简单实现HashMap

我想绝大多数做Java或者Android的人都是很了解HashMap的结构的,毕竟是一个经常使用到的类,当然也可能说不出来到底是一个怎样的结构。如果不了解的话可以考虑自己来实现一个HashMap

简单实现HashMap

试着想想下面的问题?

  • HashMapput方法的时间复杂度是多少?
  • HashMapget方法的时间复杂度是多少?
  • 怎么存储不同类型数据?这个简单,范型。

思考片刻…

getput的是时间复杂度肯定不能超过O(n),HashMap第一个需要保证的就是快速的put以及get,那么如何做到putget的时间复杂度为O(1)或者接近O(1)?

对于一个List来说,如果知道了index的值确实是可以做到时间复杂度为O(1)。但是如何确定List的大小?是否可以将List里面的内容分段来放,这样就保证了时间复杂度小于O(n),同时也不至于初始化的List就过大。

那么直接来张HashMap的结构图,使用Gliffy画的,可以凑合看看。



可以看出是一个数组,数组中每一项存储的是一个链表。当里面存储的内容比较的分散的时候(数组中每一项最多只存储一个数据),那么putget的时间复杂度就是O(1)了。

那么就来简单实现一个HashMap,仅仅实现putget方法。


public class CustomHashMap<K, V> {

    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    static final int DEFAULT_SIZE = 16;

    Node<K, V>[] table; // 存储数据

    float loadFactor; // 负载系数。 threshold = loadFactor * table.length

    int threshold; // 阀值,超过就对CustomHashMap扩充

    int size; // 存储数据的数量

    public CustomHashMap() {
        loadFactor = DEFAULT_LOAD_FACTOR;
    }

    public V getValue(K key) {
        int hash = (key == null ? 0 : key.hashCode());
        if (table == null || table.length == 0) {
            return null;
        }

        int index = (hash & table.length - 1);
        Node<K, V> e = table[index];

        while (e != null) {
            if (e.key == key && e.hash == hash) {
                return e.value;
            }
        }
        return null;
    }

    public void putValue(K key, V value) {
        int hash = (key == null ? 0 : key.hashCode());

        if (table == null || table.length == 0) {
            resize();
        }

        Node<K, V> node = new Node<>(key, value, hash);

        int index = (hash & table.length - 1);
        Node<K, V> e = table[index];

        if (e == null) {
            node.next = null;
            table[index] = node;
        } else {
            while (e != null) {
                if (e.key == key && e.hash == hash) {
                    e.value = value;
                    return;
                }
                e = e.next;
            }
            node.next = table[index];
            table[index] = node;
        }

        if (++size > threshold) {
            resize();
        }
    }

    /**
     * 调整CustomHashMap的大小
     */
    private void resize() {
        int newCapacity, oldCapacity;
        int newThr, oldThr;
        if (table == null || table.length == 0) {
            oldCapacity = 0;
            oldThr = 0;
        } else {
            oldCapacity = table.length;
            oldThr = threshold;
        }

        if (oldCapacity > 0) {
            newCapacity = oldCapacity << 1; // 加一倍
            newThr = oldThr << 1; // 加一倍
        } else {
            newCapacity = DEFAULT_SIZE;
            newThr = (int) (DEFAULT_SIZE * DEFAULT_LOAD_FACTOR);
        }
        threshold = newThr;
        Node<K, V>[] oldTable = table;
        @SuppressWarnings({"rawtypes", "unchecked"})
        Node<K, V>[] newTable = (Node<K, V>[]) new Node[newCapacity];

        table = newTable;

        if (oldTable != null) {
            for (int i = 0; i < oldCapacity; i++) {
                Node<K, V> e = oldTable[i];
                if (e != null) {
                    newTable[i] = e;
                }
            }
        }
    }

    static class Node<K, V> {
        Node<K, V> next;
        K key;
        V value;
        int hash;

        Node(K key, V value, int hash) {
            this.key = key;
            this.value = value;
            this.hash = hash;
        }
    }
}

简单测试

public class CustomHashMapTest {

    public static void main(String[] args) {
        CustomHashMap<String, String> map = new CustomHashMap<>();
        map.putValue("Egos", "GG");
        System.out.println(map.getValue("Egos"));
    }
}

HashMap和HashTable区别

  • HashMap中可以存储keynull或者valuenull的数据。HashTable中不允许key以及valuenull
  • HashTable时线程安全的(putget都是使用synchronized同步),HashMap则不是线程安全的。这样导致的结果是在单线程中使用HashTable效率会比HashMap低。

一些可以思考的问题

  • hashCode的计算。怎么保证HashMap中数据尽量散开存储,即Hash冲突减少?从数组的容量以及hashCode的算法中都可以考虑。
  • 源码中会默认的保证存储的数组table最大长度是2^k,为什么呢?提示hash&(length-1)
  • 使用HashMap的结构来扩展?例如LinkedHashMap(保证便利取数据和存数据的顺序一样)。

PS:最近经常的静不下心来,所以想着复习下老知识。当然只是比较浅显的分析了一下,但是觉得Java中的一些数据结构还是蛮有意思的。除了基本的java.util包下面还有java.util.concurrent都有很多封装类,有时候可以考虑下为什么这么实现或许对自己也有一些启发。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap和ConcurrentHashMap都是Java中常用的Map实现类,它们有以下几个主要区别: 1. 线程安全性:HashMap是非线程安全的,而ConcurrentHashMap是线程安全的。在多线程环境下使用HashMap可能会导致竞态条件和数据不一致的问题,而ConcurrentHashMap通过使用锁分段技术(Segment)或CAS操作来实现线程安全。 2. 并发性能:ConcurrentHashMap在并发访问时能够提供较好的性能。它通过将数据分割成多个段(Segment),每个段都拥有自己的锁,不同的线程可以同时访问不同的段,从而提高并发性能。而HashMap在并发访问时需要手动添加同步措施,性能较低。 3. 迭代器弱一致性:ConcurrentHashMap的迭代器是弱一致的,即在迭代过程中允许其他线程对Map进行修改,但不保证迭代器一定能够反映出最新的修改。而HashMap的迭代器是快速失败的,即在迭代过程中如果有其他线程对Map进行修改,会立即抛出ConcurrentModificationException异常。 4. Null键和null值:HashMap允许使用null作为键和值,而ConcurrentHashMap不允许使用null作为键和值。在ConcurrentHashMap中,如果使用null作为键或值,可能会抛出NullPointerException异常。 综上所述,HashMap适用于单线程环境或者在多线程环境下通过手动添加同步措施保证线程安全;而ConcurrentHashMap适用于多线程环境下需要高并发性能和线程安全性的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值