java总结

map接口

哈希表就是一种以键-值(key-indexed) 存储数据的结构,只要输入待查找的值

  • 即key,即可查找到其对应的值。
  • 哈希的思路很简单,如果所有的键hashCode都是整数,那么就可以使用一个简单
  • 的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任
  • 意键的值。
  • 它提供了一组键值的映射。其中存储的每个数据对象都有一个相应的键key,键决
  • 定了值对象在Map中的存储位置。键应该是唯一的,不允许重复,每个key只能映
  • 射一个value。

哈希冲突

  • 当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,就把它叫做碰撞(哈希碰撞)。哈希
  • 碰撞的解决方案为开放地址法和链地址法。
  • 以key/value的方式存储数据,采用拉链法综合了数组和链表结构。如果key已知则存取效率较高,但是删除
  • 慢,如果不知道key存取则慢,对存储空间使用不充分。最典型的实现是HashMap。
  • JDK8+对桶内数据处理从链表转换为红黑树,则查询效率从O(N)提高到O(logN)。当链表中的个数超过8个时
  • 会转换为红黑树

Map接口的定义

Map主要用于存储键key值value对,根据键得到值,因此键不允许重复,但允许值重复。

Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等

ConcurrentHashMap可以理解为HashMap的线程安全版

  • public interface Map
  • 表示存储数据时有2个部分key和value,key不允许重复
  • int size(); 获取key-value个数
  • boolean containsKey(Object key);判断集合中是否包含指定的key值
  • 要求key类型需要实现equals和hashCode发方法
  • 首先调用hashCode方法获取key对应的hash值,然后当key值和map中的
  • key值相同时才会继续调用equals进行等值判断,否则是false
  • boolean containsValue(Object value);判断集合中是否包含指定的value值
  • 要求value类型需要实现equals,对于hashCode没有要求;进行判断时是直接

遍历map集合有三种方式,map提供了三种不同的视图

  • 1、Iterator 2、.forEach(lambda) 3、for-each结构 for(Object tmp: map.keySet)
  • Set keySet();获取所有的key所组成的Set集合
  • Collection values();获取所有的value所组成的Collection集合
  • Set> entrySet();获取所有的key-value对所构成的Set集合
  • 在Map中一个key-value被封装成一个Entry的对象

HahMap

HashMap是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。

* HashMap最多只允许一条记录的键为null;允许多条记录的值为null

* HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致,在JDK1.7中

* 会出现环形链,虽然JDK1.8不会出现环形链,但是还会有rehash操作出现死循环、脏读问题、size值不准确等问题。

* 如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。

HashMap底层使用数组+链表+红黑树实现,默认初始化大小Defalut_capcity是1

// 当我们放置元素到HashMap中,创建的HashMap底层数组在一开始是没有初始化的,通过hash()这个方法,算出来目标hash值后,放置这个内容到数组中,但是数组当前没有初始化,所以调用resize方法来初始化数组,初始化后,在放置内容时,直接调用newNode方法来将数据放置在数组的桶中。

// 当元素发生Hash碰撞,HashMap使用拉链法来解决,将碰撞的元素,插在之前桶中的最后一个元素的后面。这样就形成了链表。

// 当单个链表的元素个数超过8时,可能会发生树化过程。这个过程不一定发生,因为如果当前数组的长度没有到64(HashMap定义的最小书画长度)那么就会先扩容数组,当扩容数组的时候,会产生rehash过程,可能就降低了链表的长度,也就使长度不到8了。但是如果当前数组长度已经达到64,那么就会将当前链表进化成红黑树。

HashMap是使用了哪些方法来有效解决哈希冲突的:

  1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
  2. 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
  3. 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快

容量、负载因子和树化 因为容量和负载因子决定了可用的桶的数量,空桶太多了会浪费空间,如果使用太满则会严重影响操作的性能。 扩容发生条件为【负载因子*容量 < 元素数量】,所以预先设置的容量需要满足大于【预估元素数量/负载因子】 ,同时它是大于等于预先设置的容量的2的幂数 - 如果没有特殊需求不要轻易更改,JDK自带默认负载因子是适用于通用场景需求的 - 如果确实需要修改建议不要超过0.75,因为会显著增加冲突,降低hashmap性能 - 如果使用太小的负载因子,否则可能会导致更加频繁的扩容,增加性能开销,本身访问性能也会受到影响 如果容量小于64只会进行简单扩容,如果容量大于64则会进行树化改造。树化处理可以避免哈希碰撞攻击 扩容处理 - 在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容 - 每次扩展的时候,都是扩展2倍 - 扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。 在putVal()中看到在这个函数里面使用到了2次resize()方法,resize()方法表示的在进行第一次初始化时 会对其进行扩容,或者当该数组的实际大小大于其临界值,这个时候在扩容的同时也会伴随的桶上面的元素 进行重新分发,这也是JDK1.8版本的一个优化的地方,在1.7中,扩容之后需要重新去计算其Hash值,根据 Hash值对其进行分发,但在1.8版本中,则是根据在同一个桶的位置中进行判断(e.hash & oldCap)是否为 0,重新进行hash分配后,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位 置上

Hashtable

线程安全的,不允许null的键或值;是线程安全的但是Hashtable线程安全的策略实现代

  • 价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈
  • 希表加了一把大锁。多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只
  • 能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差
  • 由于线程安全,则在非多线程环境下不建议使用

Hashtable的函数都是同步的,这意味着它是线程安全的。它的key、value都不可

  • 以为null。此外,Hashtable中的映射不是有序的;在hashmap中允许key和value
  • 为null,只是key只能有一个null,如果出现冲突后盖前;如果使用null的key和value
  • 则会出现一个运行时异常NullPonterException

TreeMap

TreeMap是一个有序的key-value集合,它是通过红黑树实现的。该映射根据其键的自然顺序进行排序

[Comparable或者Comparator]或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用

的构造方法,底层实现和hash表无关。

TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。

TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。

TreeMap 实现了Cloneable接口,意味着它能被克隆。

TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。

常用方法

- put(K key, V value)用于添加一个Entry(结点,包括key和value)到TreeMap对象,

key为与指定值将要关联的键,value为使用指定键关联的值。如果key对应的值已经存在,

则将key对应的值修改为value

- V get(Object key)方法用于获取指定key的value,key为与value相关联的键

Properties

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值