HashMap

HashMap:数组+链表,结合二者优势

上一篇粗略理解了Map的各个类,这一篇详写对HashMap的认识。

HashMap特点

HashMap是基于哈希表的Map接口实现。

HashMap底层采用的是Entry数组和链表实现。

HashMap是采用key-value形式存储,其中key是可以允许为null但是只能是一个,并且key不允许重复(如果重复则新值覆盖旧值)。

HashMap是线程不安全的。

HashMap存入的顺序和遍历的顺序有可能是不一致的。

HashMap保存数据的时候通过计算key的hash值来去决定存储的位置。

HashMap的方法:

void                           clear()
Object                       clone()
boolean                     containsKey(Object key)
boolean                     containsValue(Object value)
Set<Entry<K, V>>     entrySet()
V                                get(Object key)
boolean                     isEmpty()
Set<K>                      keySet()
V                                put(K key, V value)
void                            putAll(Map<? extends K, ? extends V> map)
V                                remove(Object key)
int                               size()
Collection<V>            values()

HashMap put(key,value)代码:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

HashMap put(key,value)的算法思路:

1)对key计算hashcode(),得到index

2)如果没有发生冲突,直接放进table里

3)如果发生冲突,遍历链表,如果节点已经存在(key重复),则替换old value(从这里可以看出,hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率)

4)将节点尾插到链表

5)如果冲突导致链表过长(n>=8),链表转换为红黑树

6)如果table满,则需要resize

 

 

课后习题:

1.请讲解HashMap数据结构+put()的算法步骤

答:

HashMap数据结构:

数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。而HashMap是结合了两者优点的数据结构。

put()算法步骤:

根据传入键值的hashCode计算出index,看table[index],如果为空就直接new一个链表放入新传入的键值实值。如果不为空,遍历链表。如果有键值equals()方法返回true,  则用新的实值替换旧实值,如果不存在则在链表之后添加新的键值实值构造出的Entry对象。之后判断是否树化。

2.加载因子LoadFactor的作用

加载因子是表示Hsah表中元素的填满的程度,加载因子越大,填满的元素越多,此时空间利用率高了,但冲突的机会加大了.。HashMap默认的加载因子是0.75,最大容量是16,因此可以得出HashMap的默认容量是:0.75*16=12。

3.Threshold的作用

threshold是HashMap所能容纳的最大数据量的键值对个数,用于判断是否需要调整HashMap的容量,以达到空间利用率和Hash冲突发生机率相对平衡。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍(resize)。

4.NavigableMap的作用

可以看看NavigableMap的源码:

由于NavigableMap继承了SortedMap,因此它是有序的,以下是它的方法

//返回第一个key小于参数的Entry
Map.Entry<K,V> lowerEntry(K key);

//返回第一个key小于参数的key
K lowerKey(K key);

//返回第一个key小于等于参数的Entry
Map.Entry<K,V> floorEntry(K key);

//返回第一个key小于等于参数的key
K floorKey(K key);

//返回第一个key大于等于参数的Entry
Map.Entry<K,V> ceilingEntry(K key);

//返回第一个key大于等于参数的key
K ceilingKey(K key);

//返回第一个key大于参数的Entry
Map.Entry<K,V> higherEntry(K key);

//返回第一个key大于参数的key
K higherKey(K key);

//返回key最小的Entry
Map.Entry<K,V> firstEntry();

//返回key最大的Entry
Map.Entry<K,V> lastEntry();

//删除并返回key最小的Entry
Map.Entry<K,V> pollFirstEntry();

//删除并返回key最大的Entry
Map.Entry<K,V> pollLastEntry();

//返回key降序排列的NavigableMap
NavigableMap<K,V> descendingMap();

//返回key升序排列的NavigableSet
NavigableSet<K> navigableKeySet();

//返回key降序排列的NavigableSet
NavigableSet<K> descendingKeySet();

//返回key升序排列的子映射,设置包含标志
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive);

//按key升序排列,返回子映射,开头到toKey,设置包含标志
NavigableMap<K,V> headMap(K toKey, boolean inclusive);

//按key升序排列,返回子映射,fromKey到末尾,设置包含标志
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);

//同时也继承了SortedMap的不带包含标志的子映射方法
SortedMap<K,V> subMap(K fromKey, K toKey);
SortedMap<K,V> headMap(K toKey);
SortedMap<K,V> tailMap(K fromKey);

方法 lowerEntry、floorEntry、ceilingEntry 和 higherEntry 分别返回与小于、小于等于、大于等于、大于给定键的键关联的 Map.Entry 对象,如果不存在这样的键,则返回 null。类似地,方法 lowerKey、floorKey、ceilingKey 和 higherKey 只返回关联的键。所有这些方法是为查找条目而不是遍历条目而设计的。

       此接口还定义了 firstEntry、pollFirstEntry、lastEntry 和 pollLastEntry 方法,它们返回和/或移除最小和最大的映射关系(如果存在),否则返回 null。

      subMap(K, K)、headMap(K) 和 tailMap(K) 方法被指定为返回 SortedMap,以允许现有 SortedMap 实现能相容地改进为实现 NavigableMap,但鼓励此接口的扩展和实现重写这些方法以返回 NavigableMap。类似地,可以重写 SortedMap.keySet() 以返回 NavigableSet。

 

因此,粗略总结一下它的作用:

使用NavigableMap,除了获取最大值与最小值,我们还可以对任何一个元素,找到比它小的值和比它大的值,还可以按照按照原有的顺序倒序排序等。

5.为什么Hash类容器放入的元素必须覆写hashCode()方法和eaquls()方法?

覆写equals方法之后,可以实现对象的自定义比较,如果不覆写此方法,默认调用Object的equals方法,等同于比较地址值,是否指向同一个内存地址。如果不覆写hashCode方法,而直接调用该方法,得到的哈希值是由系统计算出的,返回的是对象对应的内存地址,返回值和对象属性之间没有直接联系。覆写hashCode方法,可以根据对象属性的不同,返回相应的哈希值。

在java的集合中,判断两个对象是否相等的规则是:

1.首先,判断两个对象的hashCode是否相等

2.如果不相等,认为两个对象也不相等

3.如果相等,则判断两个对象用equals运算是否相等

4.如果不相等,认为两个对象也不相等

5.如果相等,认为两个对象相等

因此,equals相等的两个对象,hashcode一定相等;equals不相等的两个对象,却并不能证明他们的hashcode不相等。如果不覆写,放入两个新的对象,可能会是不相等的.可能是不同的key,产生了多条记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值