*双列集合Map(HashMap,TreeMap)

目录

Map接口的方法:

HashMap的存储原理:

ConcurrentHashMap:

TreeMap:

HashTable:

LinkedHashMap :


Map与Collection在集合框架中属并列存在,Map存储的是键值对!

  • |——Map        双列集合根接口,具备的特点:存储的数据都是以键值对的形式存在的,键不可重复,值可以重复
    • |——HashMap       (JDK1.2)底层是依赖哈希表(数组+链表)来实现的。可以存入null键,null值
    • |——TreeMap          红黑树数据结构实现的,有序
    • |——Hashtable      (JDK1.0)底层也是依赖哈希表(数组+链表)来实现的,和HashMap一致,线程安全的,操作效率低。不可以存入null键,null值
    • |——LinkedHashMap
    • |——ConcurrentHashMap    分段的数组+链表实现,线程安全

 


Map接口的方法:

添加:

put(K  key, V  value)         如果之前没有存在该键,那么返回的是null,如果之前就已经存在该键了,那么就返回该键之前对应的值

putAll(Map<? extends K,? extends V> m)       把参数Map的元素添加到对象Map的集合中

删除:

remove(Object key)      根据键删除一条map中的数据,返回的是该键对应的值

clear()      清空集合中的所有数据

获取:

get(Object key)       根据指定的键获取对应的值

size()        获取map集合键值对个数

判断:

containsKey(Object key)       判断map集合是否包含指定的键

containsValue(Object value)       判断map集合中是否包含指定的值

isEmpty()        判断map集合是否为空元素

迭代: 

注意:Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素

 

keySet()          把Map集合中的所有键都保存到一个Set类型的集合对象中返回。   缺点: keySet方法只是返回了所有的键,没有值
values()          把所有的值存储到一个Collection集合中返回。    缺点: values方法只能返回所有的值,没有键
entrySet()         将map集合中的键和值映射关系打包为一个对象Map.Entry,将该对象存入Set集合,然后获取set的迭代器

 

-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

例:Map集合迭代。静态内部类public static interface Map.Entry<K,V>

//将Map集合打包为一个Map.Entry对象
Set<Map.Entry<Integer, String>>  es = map.entrySet();
//获取set的迭代器
Iterator<Map.Entry<Integer, String>>  it = es.iterator();
while (it.hasNext()) {
        // 返回的是封装了key和value对象的Map.Entry对象
        Map.Entry<Integer, String> en = it.next();
        // 获取Map.Entry对象中封装的key和value对象
        Integer key = en.getKey();
        String value = en.getValue();

        System.out.println("key=" + key + " value=" + value);
}

 


HashMap的存储原理:

HashMap中的变量:

table:Node<K,V>类型的数组,里面的元素是链表,用于存放HashMap元素的实体

Entry<K,V> / jdk1.8后变成Node<K,V>:链表节点,包含了key、value、hash、next指针四个元素

size:记录了当前放入HashMap的元素个数

loadFactor:负载因子

threshold:扩容的阈值,决定了HashMap何时扩容,以及扩容后的大小,一般等于,等于 table * loadFactor

HashMap可以让你将空值作为一个表的条目的key或value

HashMap添加元素的时候,首先会调用键的hashCode方法得到元素的哈希码值,然后经过运算就可以算出该元素在哈希表中的存储位置。

Hash算法
Java中任何对象都有hashcode,hash算法就是通过hashcode与自己进行向右位移16的异或运算。这样做是为了计算出来的hash值足够随机,足够分散,还有产生的数组下标足够随机

 

算出位置后分两种情况:

情况1:如果算出的位置目前没有任何元素存储,那么该元素可以直接添加到哈希表中。

情况2:哈希冲突/碰撞 如果算出的位置目前已经存在其他的元素,那么还会调用该元素的equals方法与这个位置上的元素进行比较,如果equals方法返回的是false,那么该元素允许被存储,如果equals方法返回的是true,那么该元素被视为重复元素,不允存储。(如果出现了相同键,那么后添加的数据的值会取代之前的值)

 

  • 容量(capacity):HashMap目前数组总大小
  • 初始化容量(initial capacity):创建hash表时数组的初始化容量,默认初始容量大小为16
  • 负载因子loadFactor):负载因子等于“size/capacity”。为了降低哈希冲突的概率  负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)
  • “负载极限”(threshold): 扩容的阈值, 是一个0~1的数值,默认值(0.75) 决定了hash表的最大填满程度, 负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

 

空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

碰撞时 后加的数据在前面,先加的移下,负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能

于是,在JDK1.8版本中,对数据结构做了进一步的优化,

  1. 引入了红黑树,。而当链表长度太长(TREEIFY_THRESHOLD默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能(O(logn))。当长度小于(UNTREEIFY_THRESHOLD默认为6),就会退化成链表。
  2. 发生hash碰撞时,java 1.7 会在链表的头部插入,而java 1.8会在链表的尾部插入
  3. Node替代了Entry

 


JDK1.5中,Doug Lea给我们带来了concurrent包,从此Map也有安全的了

ConcurrentHashMap:

是HashTable的替代, 它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,效率提升N倍,默认提升16倍。

由Segment数组结构和HashEntry数组结构组成, 一个Segment里包含一个HashEntry数组,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。读操作不加锁,HashEntry的value变量是 volatile的,也能保证读取到最新的值。

在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中。默认是把segments初始化为长度为16的数组

Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个Segment桶。原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

 

 

 

在Java 8 里抛弃了Segment的概念,直接用Node数组+链表+红黑树的数据结构来实现


TreeMap:

是基于红黑树(二叉树)数据结构实现的, 特点:会对元素的键进行排序存储。

TreeMap 要注意事项

  1. 往TreeMap添加元素的时候,如果元素的键具备自然顺序,那么就会按照键的自然顺序特性进行排序存储。
  2. 往TreeMap添加元素的时候,如果元素的键不具备自然顺序特性,那么键所属的类必须要实现Comparable接口,把键的比较规则定义在CompareTo方法上。
  3. 往TreeMap添加元素的时候,如果元素的键不具备自然顺序特性,而且键所属的类也没有实现Comparable接口,那么就必须在创建TreeMap对象的时候传入比较器。

HashTable:

Hashtable是基于陈旧的Dictionary类的。Hashtable是线程安全的(每个方法加synchronized)。不可以存入null键,null值

对remove,put,get进行了同步控制, 保证了Hashtable的线程安全性,


LinkedHashMap :

HashMap问题: 迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序的

LinkedHashMap 是HashMap的一个子类,加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,保存了记录的插入顺序,也就是有序的

LinkedHashMap = HashMap + LinkedList   【使用HashMap操作数据结构,使用LinkedList维护插入元素的先后顺序】

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xyc1211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值