Java集合 HashMap

HashMap实际上是一个散列表结构的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。
jdk1.8之前,是数组和链表实现
jdk1.8之后,采用数组加链表加红黑树实现,当链表的长度超过阈值8时,链表转为红黑树。
HashMap是基于哈希表的Map接口实现(Map是存储键值对的容器,底层基于哈希表实现,对于Map的键可以是任意类型的对象,但不能够有重复的键,每个键对应一个值)

哈希冲突:两个不同的元素,通过哈希函数得出相同的存储地址,即对某个元素进行哈希运算得到的存储地址被其他元素占用。
解决方法:开放地址法:继续寻找下一块存储地址
链地址法:采用数组加链表(HashMap采用链地址法)

HashMap继承关系

public class HashMap<K,V> extends AbstractMap<K,V>
       implements Map<K,V>,Cloneable,Serializable

AbstractMap<K,V>是Map接口的抽象类,可以实现Map接口的大部分方法
Cloneable接口,表示可以进行拷贝
Serializable接口表示可以将对象保存在本地。

hash运算:

public int hash(K key){
    int h;
    return (h = key.hashCode()) ^(h>>>16);
}

目的是为即将存入HashMap的元素的key计算hash值,将此hash值作为索引,放入哈希中

put方法:

public void put(K key,V value){
    int hash = hash(key);
    int index = (table.length-1) & hash;
    //判断是否存在节点
    Node<K,V> firstNode = table[index];
    //不存在   new Node  直接放在该位置
    if(firstNode == null){
        table[index] = new Node(hash,key,value);
        size++;
    }else {
        //存在  遍历链表
        //key已经存在于链表中,覆盖value
        if(firstNode.key.equals(key)){
           firstNode.value = value;
        }else {
            //遍历链表
            Node<K,V> tmpNode = firstNode;
            while (tmpNode.next !=null || !tmpNode.key.equals(key)){
                tmpNode = tmpNode.next;
            }
            if(tmpNode.next == null){
                if(tmpNode.key.equals(key)){
                    tmpNode.value = value;
                }else {
                    //key不存在于链表中
                    tmpNode.next= new Node(hash,key,value);
                    size++;
                }
            }else {
                tmpNode.value = value;
            }
        }
    }
}

put方法是存储方法,如果HashMap中不存在节点,直接插入,如果存在,遍历链表,若链表中存在相应的key值,直接覆盖掉原先的value,若不存在,在链表末尾插入。

get方法:

public V get(K key){
    int hash = hash(key);
    int index = (table.length-1) & hash;

    Node<K,V> firstNode = table[index];
    if(firstNode == null){
        return null;
    }
    if(hash == firstNode.hash && key == firstNode.key){
        return firstNode.value;
    }else {
        //遍历链表
        Node<K,V> tmpNode = firstNode.next;
        while (tmpNode != null){
            if(tmpNode.hash == hash && tmpNode.key == key){
                return tmpNode.value;
            }
            tmpNode = tmpNode.next;
        }
    }
    return null;
}

查询方法:如果链表为空,返回null,如果头结点的hash和key与查询值相同,返回value值,否则,遍历链表,寻找相同的hash和key,找到返回value,否则返回null。

在get方法中,需要比较hash的原因:

 if(hash == firstNode.hash && key == firstNode.key){
        return firstNode.value;

1、哈希表是一个有限集合,固定长度为32位,对象和hashCode的关系并不是一对一的,所以hash相等但也可能对象不同。
2、判断元素是否相等时先用hashCode判断,如果hashCode不同,则对象不同,如果HashCode相同,对象再进行比较,效率更高。

remove方法:

public boolean remove(K key){
    int hash = hash(key);
    int index = (table.length-1) & hash;

    Node<K,V> firstNode = table[index];
    if(firstNode == null){
        return false;
    }else {
        if(firstNode.next == null){
            if(firstNode.hash == hash && firstNode.key.equals(key)){
                table[index] = null;
                return true;
            }
        }else {
            if(firstNode.hash == hash && firstNode.key.equals(key)){
                table[index] = firstNode.next;
                return true;
            }else {
                //链表存在于多个节点则需要遍历链表进行比较
                while (firstNode.next != null ){
                    if(firstNode.next.hash == hash && firstNode.next.key.equals(key)){
                        firstNode.next = firstNode.next.next;
                        return true;
                    }else {
                        firstNode = firstNode.next;
                    }
                }
            }
        }
    }
    return false;
}

删除方法:如果表为空,返回false,如果仅有头结点,且头结点的值为key对应值,将表置为空,如果该表有多个节点,且对应的key值在头结点上,删除头结点,否则,遍历链表,寻找对应的key值,找到即删除,若表中没有对应的key值,返回false

HashMap和Hashtable区别和联系
1、继承的父类:Hashtable父类为Dicionary;HashMap父类为AbstractMap,都实现了Map、Cloneable、Serializable接口。
2、默认容量:Hashtable为11;HashMap为16
3、初始化机制:Hashtable为构造函数中初始化;HashMap在第一次put时初始化
4、并发操作:Hashtable使用同步机制,但在实际应用程序中,Hashtable本身的同步并不能保证程序在并发操作中的正确性,需要更高层次的保护;HashMap没有同步机制,需要使用者自己进行并发访问控制。
5、线程同步:HashMap线程不同步(效率高,安全性低);Hashtable线程同步(效率低,安全性高)
6、是否接受值为null的key或者value:Hashtable不接受;HashMap接受(但null为key值是只允许一个)
7、根据hash值计算数组下标的算法:Hashtable当数组长度较小时,并且key的hash值低位数值分散不均匀时,不同的hash值计算得到相同下标的概率较高;HashMap通过对key的hash做移位运算和位的与运算,使其能更广泛的分散到数组的不同位置。
8、数组遍历方式:Hashtable:Iterator和Enumeration;HashMap:Iterator.
9、是否支持fast-fall机制:Hashtable:用Iterator遍历时,支持,用Enumeration遍历,不支持;HashMap:支持fast-fall
10、Entry数组的长度:Hashtable:缺省初始长度是11,初始化时可指定initial capacity;HashMap:缺省初始化长度为16,长度始终保持2的N次方,初始化时可以指定initial capacity,若不是2的N次方,HashMap将选取第一个大于initial capacity的2的n次方作为其初始化长度。
11、LoadFactor负荷因子:均为0.75
12、负荷超过(LoadFactor数组长度)时,内部调整方式:Hashtable扩展数组:2原数组长度+1;HashMap扩展数组:原数组长度*2。两者都会根据key的hash值计算其在数组中的新位置,重新放置。算法相似,时间、空间效率相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值