1-java-java集合

  1. vector相当于ArrayList有什么优势,为什么会有这个类的存在。
  2. 为什么Listvector/stack的父类,不是queue的父类。
  3. VectorStackHashTable为什么被废除?

 

 

集合罗列

  1. 不重复集合:HashSet, LinkedHashSet(按插入顺序迭代), TreeSet(排序集合),ConcurrentSkipListSet, CopyOnWriteArraySet(线程安全set)
  2. 列表:ArrayList(底层使用数组), LinkedList(底层使用链表), CopyOnWriteArrayList(线程安全), Vector,Stack(后进先出)
  3. Map: HashMap, Hashtable, LinkedHashMap, TreeMap ,ConcurrentHashMap, ConcurrentSkipListMap
  4. 队列queue:ArrayBlockingQueue , ConcurrentLinkedQueue, DelayQueue, LinkedBlockingDeque,, PriorityBlockingQueue, PriorityQueue, SynchronousQueue
  5. 双端队列deque:LinkedList,ArrayDeque
  6. 栈:stack

总结

  1. 备注:Vector、Stack和HashTable是legacy,已经不建议使用。
  2. 集合的功能分类方式:底层使用数组/链表,是不是同步,是不是有序。

ArrayList实现

  1. 底层使用Object[]。
  2. 数组默认大小10
  3. 超出容量的add,remove,add(int index, E element)都是使用的数组copy实现的。 每次扩容大小(jdk1.8):是原数组大小的1.5倍或者需要的最小值(可能1.5倍不能满足扩容需求)。

HashMap实现

简介

  1. key和value是绑定在一起的,构成一个entry。HashMap就是一个entry类型的数组,entry除了有用户存入的key和value,还有一个entry类型的指针,指向其他entry。对key的hashCode进行hash,可以得到数组角标;所有hashCode相同的object在数组的同一位置,使用链表连接。
  2. 向hashMap中添加一个key\value的流程:

key的hashCode不会直接用来求角标的位置,hashMap中会有一个int hash(int hashCode)方法,key的hashCode经过hash之后的值才会被用来计算数组角标。

  1. hashmap的相关参数
    1. initial_capacity = 16jdk1.7/1.8
    2. loadfactor = 0.75jdk1.7/1.8 : map.size/entryArr.size>loadfactor时会引发hashmap扩容。因为当桶的数量远小于数据的数量时,大多数元素会存在于一条链表上,导致查询效率降低。
    3. 每次扩容大小:resize(2 * table.length)1.7
  2. hashhashCode是两个值,hashCode是写在Object中的hashCode()方法的返回值,hashhashmap中对hashCode扰动后的结果值。在同一个桶位上的对象的判断语句是(k = e.key) == key || (key != null && key.equals(k))),即是同一个对象,或者equals相等的对象。对于1.8版本优化后的红黑树,对于同一桶位的不同entry,红黑树的排序会首先根据hashCode,如果hashCode相同,则map希望key实现Comparable接口,如果没有实现这个接口则不能起到优化的结果。
  3. end

 

hashmap的结构,1.7和1.8有哪些区别

https://blog.csdn.net/qq_36520235/article/details/82417949

https://blog.csdn.net/qq_27093465/article/details/52270519

  1. 8版本会在链表的大小超过8时改用红黑树。
  2. hashmap1.7版本在resize时会有逆序死锁的问题,1.8不会。
  3. hash(hashCode)方法做了改变。在计算hash值的时候,JDK1.7用了9次扰动处理=4次位运算+5次异或,而JDK1.8只用了2次扰动处理=1次位运算+1次异或。
  4. 扩容时的rehash算法改变:resize的基本算法是hash&length-1,结果分为两种一部分会待在原始位置,还有一部分的数组位置是(原始位置+旧容量大小)。
    1. 对于原来的key 1.7版本时会再算一次hashCode不知道1.7为什么会重新计算?),1.8不会。
    2. 1.7计算hash&length-11.8根据resize时的规律只判断length-1的最高位就可以得到结果。
    3. 对于同一bucket内的不同entry1.7是单个进行处理,1.8是先将entry分成;两个链表,然后再将两个链表放到不同的bucket中去。
    4. 1.7版本在resize完之后需要再添加newVal1.8版本resize完成的同时新值将被添加完毕。
  5. resize时hashMap1.8版本的红黑树怎么处理?

1.7中节点只有entry类型,1.8就有了Node(implements Map.Entry)和TreeNode(extends LinkedHashMap.entry(extends HashMap.Node<K,V>))两种类型,所以红黑树节点是链表节点的子类,所以只要使用在逻辑中使用公共的接口,没有再单独写逻辑。但是在putVal()时还是有单独的逻辑的。

hashmap1.7的resize死锁问题原因:

https://www.cnblogs.com/kxdblog/p/4323892.html

假设旧数组有A、C节点,新数组有B、D节点:

  1. thread1:执行至e.next=newTable[i],改变了next指针,A指向了B
  2. thread2:执行至最后一行,将旧数组的头指针改为了错的next(B)
  3. 这时如果完再执行t1,则旧数组的头指针会被还原为正确的C。但是如果t1还没有执行,t2又执行了一轮,就会出现B.next=B;或者t1只又执行了一句newTable[i]=e,这时候t2开始执行,就会形成A和B的循环。
  4. 总结:正常的流程下新链表应该是C->A->B->D,但是在线程2的正常执行的时候,线程1通过扰乱导致a.next=b,也就是线程2将b当成了原先的c,就出现了B(原先应该是C)->A->B,循环!

HashSet的实现

HashSet的底层实现使用的是HashMap<E,Object>,hashSet.add(e)内部调用的是hashMap.put(e, PRESENT), PRESENT是一个static的引用,指向一个Object类型的对象。

BitSet

https://blog.csdn.net/kongmin_123/article/details/82225172

bitset通常用于对海量数据的处理。

从表面上看可以认为是一个boolean[ ],每个对象对应一个角标,角标为true代表对象存在,角标为false代表不存在。

但是如果底层实现也是boolean的话,Boolean是1byte(8bit),一个标志其实是只需要1bit就可以了,所以会有存储的浪费。java中又没有bit类型,所以bitset的内部实现是long[ ]。通过位操作可以得到相应的值。bitset并不继承自java.util.collection,所以bitset并没有提供集合的接口,bitset最常用的就是set\get。实现布隆过滤器时通常会使用bitset( https://blog.csdn.net/iam333/article/details/38084137 )。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值