java集合框架

1. 简单讲一下集合框架的组成

  1. 集合框架根源上有两个接口,分别是collection和map
  2. 先说collection接口,collection 的子接口有list,set和queue
  3. 接下来从几个维度上综合说一下这几个接口实现类的区别
  4. 从元素的存储的区别上来讲,可以分为数组和链表、数组+链表、数组+红黑树四种形式
  5. 从容器是否有界上区别为有界、无界、可选边界三种
  6. 从线程安全上分别 线程安全和非安全两种
  7. 我对整个集合框架的理解就是各个不同的容器大多数都是从这几个维度来来设计的

2.ArrayList和LinkedList的区别

  1. 从元素储存的角度上看,前者是动态扩容的数组,后者是链表。
  2. 在读取元素上,数组比链表快。
  3. 在移除元素时,arrayList的内部实现会使用Arrays.Copy方法元素的移动,链表只需要移动指针
  4. 在增加元素时,ArrayList扩容的resize的方法会新创建出一个数组,每次扩容1.5倍(原容量+原容量右移一位)
  5. 从内存使用情况上,链表除了需要储存元素本身,还有维护指针,而数组不需要。
  6. arraylist实现了randomAccess接口,查找的速度很快
  7. 两者都不是线程安全的

3.hashMap 的结构

  1. 从数据结构上讲,hashmap 是由数组+链表储存数据的,当链表长度大于等于8时,将进行树化变成一颗红黑树。

4.hashmap 内容重要的成员变量

  1. 从内部实现上,有几个关键变量,分别是 capacity,threshold,loadfactor,size,modcount,node类型的数组table。分别说一下:
  2. capacity 指的是数组table的长度,capatity一定是2的n次幂。这是因为槽算法,后边讲槽算法。
  3. sizie 指的是当前hashmap中所有链表上所有元素的数量之和
  4. threshold指的是 当size大于threshold时,table数组将调用resize方法进行扩容。每次扩容为原容量的2倍。
  5. loadFactor 用来计算threshold值,即:threshold=loadfactor*capacity,默认为0.75
  6. modcount 代表结构被改变的次数.结构被修改包括长度被改变,或者遍历过程中可能产生不相同结果的其他方式。

5.hashmap 容量的确定

  1. 即使我们创建了一个hashmap并指定了容量时,实际hashmap的构造函数中并没有对table数组进行初始化,而是根据我们传入的容量进行计算,找出大于该容量的最小的2的n次幂对应的数,并将这个数赋值给threshold,当使用put方法添加元素时,调用resize方法,其内部将容量改为threshold。

6.为什么hashmap的容量必须是2的n次幂

  1. 因为我们要做取余操作,而一个数对2的n次方取余时等价于 这个数和2的n次方-1做 与操作(&),而二进制的与操作效率比取余操作(%)效率要高。

7.hashmap 桶算法

  1. 第一步调用hash()这个方法,原因是因为 我们要根据hashcode和容量进行取余,而且容量默认是16,而且大部分情况是容量都不会超过2的16次方,补充一下,hashcode算出来的值是32位的,所有取余操作(就是&操作)到导致高16的值无法参与计算,为了降低hash碰撞的几率,让高位也参加进来计算,使得数据分布更加均匀,所以需要扰动。该操作是指:将hashcode自身右移16位之后与自己做异或运算(高低位异或)。进行了两次扰动。

8.hashmap 添加元素的过程

  1. 说一下put操作,选择槽后,先看这个槽的头节点是否有元素,就是说数组的这个位置上是不是等于空,如果是空的,就创建一个node对象,将计算后的hash,key,val都放到这个node里,如果不能空的话,判断 hash是否一致,如果hash一致的话,防止hash冲突,再判断一下key值是不是相等的。如果是相等的,就直接替换value值,如果不同的话,就判断这个槽 是链表还是树,是树就加入树,是链表就加入链表。当然在put之前判断size是否大于了threshold,如果大于了,就扩容.

9.hashmap resize方法的过程

  1. 第一步。先判断当前容量,如果table为空 或者长度为0的话,就将容量设置为threshold,然后再根据loadFactor调整一下threshold
  2. 第二步。将容量扩大到原来的2倍,对每一个链表来说,都先分别两个链表,因为扩容之后一部分的数据将仍然保存在原来的槽内,另一部分将放到当前槽的索引+原容量 所对应的槽内,所以将链表上每个节点的hash值与原容量做与操作,如果为0,就留下来,如果不为0则组成一个新的链表放到 新的槽内,新槽的位置是 原槽位置+原容量。

10.hashmap 和hashtable的区别

  1. hashtable内部数据结构和hashmap一样,唯一的区别是 添加了synchronized。变成了线程安全的。但是官方已经不推荐使用hashtable。完全可以使用并发包里面的提供的线程安全的集合或者通过Collections工具类,将一个hashmap转换成一个线程安全的map。

11.为什么不建议使用hashtable了

  1. 因为hashtable内部锁的粒度有点粗,直接在各个方法上添加了synchronized,导致性能太低
  2. 而juc包中的concurrenHashMap,对整个桶数进行分割分段(segment),然后再每个分段上都用lock进行保护,锁的粒度更细,并发性能更好
  3. 额外提一点,concurrenHashMap中不允许有null值。hashmap中可以用一个

12.list和set的区别

  1. List 中元素是有序的,并且可以重复,可以存入多个null值
  2. 说起set,我们常用的就是hashset,linkedhashset等,实际上hashset内部就是一个hashmap,只是value值是一个常量值,所以无序,且只能有一个null值,元素不可以重复

13.什么是迭代器

iterator接口提供了遍历任何collection的接口,平时可以从一个collection中获取迭代器方法 获取迭代器实例,它只能单向遍历,并且可在遍历过程中使用remove方法移除元素,通过modcount 和cas操作确保遍历的过程中集合元素没有被修改。

14.iterator和listIterator的区别

  1. iterator可以遍历set、list,而listiterator只能遍历list
  2. listiterator可以双向遍历,而iterator不能
  3. listiterator继承于iterator,并且添加了一些额外的功能,比如添加、替换一个元素、或者前面、后面元素的索引位置

15.equals和==的区别

  1. ==比较的是内存地址
  2. equals方法,默认情况下,在object类的equals方法里,也是通过==来判断的,但是如果我们重写了该方法,就要分几步来判断:
    1. 先是用 ==来判断
    2. 然后看两个对象是否同一个类的实例
    3. 然后分别比较内部成员是否相等。如果是值类型、字符串比较的就是值,如果内部变量也是一个对象,就按上述的逻辑递归判断。

16.集合类的fast-fail是什么

是快速失败机制,主要是modcount这个值,记录了结构被修改的次数,通过cas操作去判断,如果发现值被改变,就会抛出concurrentModifyException

17.常用的队列都用哪些

  1. ArrayBlockQueue
  2. LinkedBlockQueue
  3. deque相关的队列
  4. synchronousQueue
  5. delayQueue

18.add、remove、offer、poll、take、put的区别

  1. 拿arrayBlockQueue内部源码实现举例
  2. add和remove在队列满了或队列内没有元素时,会抛出异常
  3. offer和poll在上述情况下不会抛出异常
  4. add和remove内部实际调用的就是offer和poll
  5. 调用offer和poll的线程只是抢锁,抢到锁之后判断 是否可以添加元素或者移出一个元素,如果发现不可能,就立即返回
  6. 而take和put在offer和poll的基础上还会继续等待,内部通过reenTrantLock的两个condition:notEmpty和notFull来互相做通知,直到队列有空间可以添加数据,或者有元素可以拿出时,线程才能完成这个任务

19.arrayBlockQueue为什么是线程安全的

  1. 因为内部维护了一把锁 ReenTrantLock,在添加、移除等方法中 都需要先获取这把锁才能继续执行。

20.线程池创建时可以指定哪些类型的队列,分别有什么特点

  1. ArrayBlockQueue ,创建时需要指定队列长度
  2. LinkedBlockQueue, 默认是integer.max_value,说是无界,其实有界
  3. synchronousQueue 同步队列,不储存元素。不存储元素怎么叫队列呢,只要有元素进来了,就必须马上出去,是一个同步的队列

21.ArrayBlockQueue和linkedBlockQueue的区别

  1. 前者是数组,后者是链表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值