集合系列常见面试题

1,说说常用的集合

  • ·集合从大的方向分有两个,一是Collection集合,二是Map集合。

  • ·Collection集合下有List、Set、Queue(队列)。

  • ·Map集合下有HashMap、 LinkedHashMap、TreeMap、

HashTable、ConcurrentHashMap。

  • ·List集合下有ArrayList、LinkedList、Vector、CopyOnWriteArrayList。
  • ·Set集合下有HashSet、LinkedHashSet、TreeSet、CopyOnWriteArraySet

2.说说对ArrayList的理解

  • ArrayList是List集合的一个实现类,其底层实现是数组transient Object[] elementData;
  • 效率问题:数组的结构特性是查询快,增删慢(数组内的元素都是连续的,只要给定索引下标就可以直接得出元素,但是在增删的过程中每次操作都需要移动后面的所有元素,这就使效率大大降低)
  • 扩容机制:当构造方法中没有指定数组的大小时,其默认初始容量是10。当超过这个默认值的时候,一定有一个扩容机制,其扩容机制是,当集合中元素的个数大于集合容量的时候,也就是add的时候集合放不下了,就会触发扩容机制,扩容后的新集合容量是旧集合容量的1.5倍,源码:int
    newCapacity = oldCapacity + (oldCapacity >> 1);。
  • 线程安全问题:ArrayList是线程不安全的,在add()方法的时候,首先会检查一下数组的容量是否够用,如果够用,那么就会执行elementData[size++]
    = e;方法,该语句执行了两大步,第一大步是,将e放到elementData缓冲区,第二大步是,将size的大小进行加1操作,也就是说,这个操作并非原子性操作。当在并发的情况时,就会出现问题。

3.说说对LinkedList的理解

  • LinkedList也是List的一个实现类,其底层是双向链表,
  • 效率问题: 其内部有一个next指针指向下一个节点,一个prev指针,指向上一个节点,由于是链表的数据结构,所以在查询的时候相对就比较慢了,因为当我们需要查询某个元素的时候,需要从第一个节点开始遍历,直到查询结束。而他的增删就比较快了,如果增加一个N节点,直接将后一个节点的prev指向N节点,N节点的next指向后一个节点,前一个节点的next指向N节点,N节点的prev指针指向前一个节点即可。
  • 线程问题:LinkedList也是线程不安全的,其添加元素的操作,通过linkLast方法在尾部进行添加的,添加完之后,并把size的大小加1。其他的不说,单单一个size++就不是原子性了。简单的a加1操作会执行三步:1:把a的值加载到内存、2:将内存中的值,存储到变量中、3:然后进行加1操作。

补充:ArrayList&LinkedList选择

  • 因为你的 ArrayList 存放元素大小是固定的,并且在事先已经指定并开僻了 1000 个容量的数组空间,实际上只是对数组进行了操作。
  • 而 LinkedList 是采用链表实现的,在事先无法指定容量,每添加一个数据都得去开僻新的空间。
  • 如果在添加时这样进行比较的话,对于 LinkedList 是很不公平的。
  • 对于迭代来说,ArrayList 速度远比 LinkedList 慢,因为链表迭代是很快的,如果要让 ArrayList 比
    LinkedList 快的话,可以使用下标索引。
    使用原则:
  • 1:事先能预知元素数量时,应优先选择 ArrayList,并且在构造中进行初始化
  • 2:事先不能预知元素数量时,根据不同的迭代需要选择 ArrayList 或者 LinkedList
  • 3:如果有很多的 remove 操作时,应优先选择 LinkedList
  • 4:需要顺序迭代,也就是从第一个元素开始一个一个地访问到最后一个 时,应优先选择 LinkedList
  • 5:需要随机访问,也就是使用 get(int) 方法取任意下标访问时,应优先选择 ArrayList

4.说说对Vector的理解

  • Vector也是List的一个实现类,其底层也是一个数组protected Object[] elementData;
  • 底层ArrayList差不多,也就是加了synchronized的ArrayList,线程是安全的,效率没有ArrayList高,一般不建议使用。

5.如果不使用Vector来解决ArrayList的线程安全问题,还有其他的解决方案吗?

  • 既然不建议使用Vector,还有一个包java.util.concurrent(JUC)包,它下面有一个类CopyOnWriteArrayList也是线程安全的。CopyOnWriteArrayList也是List的一个实现类。
  • add方法用Lock锁来解决并发问题,其中在进行添加数据的时候,用了copyOf方法,也就是复制了一份,然后再set进去。
    CopyOnWriteArrayList底层也是用的数组,但是它的数组是用volatile修饰了,主要是保证了数据的可见性。get操作时,并没有加锁,因为volatile保证了数据的可见性,当数据被修改的时候,读操作能立刻知道。

6.说说List和Set的区别

  • List的存储顺序是按照存入的顺序来的,而Set是根据哈希值来的
  • List可以存储相同的元素,Set不可以存储相同的元素

7.说说对HashSet的理解

  • HashSet是Set集合的一个实现类,其底层实现是HashMap的key,初始化容量是16,负载因子是0.75,扩容机制,是变为原来的2倍。
  • HashSet存储元素的顺序并不是按照存入时的顺序(和List不同)而是按照哈希值来存的所以取数据也是按照哈希值取得。元素的哈希值是通过元素的hashcode方法来获取的,
    HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equals结果为true
    ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。
    哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。

8.说说对LinkedHashSet的理解

  • LinkedHashSet是对在HashSet的基础上维护了一个双向链表,使得LinkedHashSet存取有序。

9.说说对TreeSet的理解

  • TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增加一个对象都会进行排序,将对象插入的二叉树指定的位置。

10.如果想要对HashSet进行线程安全处理,应该怎么办?

  • 可以通过CopyOnWriteArraySet来实现。

11.queue是什么呢

  • queue是一个队列,先进先出(FIFO)的数据结构

12.聊聊对HashMap的理解(重要)

底层是JDK1.7是通过数组+链表JDK1.8是通过数组+链表+红黑树组成。所有的数据都是通过一个Node节点进行封装,其中Node节点中封装了hash值,key,value,和next指针。hash是通过key计算出的hashCode值进行对数组容量减一求余得到的(官方的求余方式是通过&运算进行的)。不同的key计算出来的hash值可能相同,解决冲突是通过拉链法(链表和红黑树)进行处理。正是因为这种存储形势,所以HashMap的存取顺序是无序的。
懒加载机制,在put值的时候会判断数组是否为空,如果是就初始化数组,而不是new的时候就初始化。
HashMap是Map的一个实现类,其默认初始化容量大小是16。扩容机制是根据扩容因子来扩容的,当容量的使用量达到总容量的0.75时,就会触发扩容,举例说就是,当总容量是16时,使用量达到12,就会触发扩容机制。
当我们put一个值的时候,通过key来计算出hash值,计算出来的hash值做为数组的索引,Node节点中封装了hash值,key,value和next。当链表的长度小于8的时候,处理冲突的方式是链表。大于等于8的时候,就会触发红黑树方式存储。
当元素个数小于等于6的时候,会触发红黑树转化为链表的形式,为什么不是小于等于7,是因为给一个过度,也就是防止添加一个刚好为8,删除一个刚好为7,这样来回转化。
13.说说对LinkedHashMap的理解

LinkedHashMap解决了HashMap不能保证存取顺序的问题。内部增加了一个链表用于维护元素存取顺序。
14.说说对TreeMap的理解

TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序。
15.如果想要保证HashMap的线程安全,应该怎么办?

可以通过HashTable,该类的出现主要是解决了HashMap的线程安全问题,直接用了synchronized锁,所以效率上不高,不建议使用(发现JDK1.0的线程问题,解决都很暴力)。
ConcurrentHashMap是java.util.concurrent包下的,并发包下的。他就是对HashMap进行了一个扩展,也就是解决了他的线程安全问题。ConcurrentHashMap用了大量的CAS来进行优化。
16.什么是CAS呢?

CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。
17.Collection 和 Collections 有什么区别?

Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections则是集合类的一个工具类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
18.ArrayList和LinkedList 的区别是什么?

ArrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
19.ArrayList和Vector 的区别是什么?

Vector使用了synchronized来实现线程同步,是线程安全的,而ArrayList是非线程安全的。
Vector扩容每次会增加1倍,而ArrayList只会增加0.5倍。
20.HashMap和HashTable的区别?

HashMap允许空键值,HashTable不允许。
HashMap线程不安全(效率高),HashTable线程安全。

====================================================================
本文内容选自不同的文章,在原有的基础上添加了个人理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值