集合中的常见面试题

Java中的容器就是集合。在集合中分为两大接口:分别是Collection接口Map接口

Collection接口不是顶级接口,它是Iterable接口的子接口。Iterable接口表示可迭代的。也就是说Collection接口所有子接口和实现类都是可以通过Iterator迭代器进行迭代。在Collection接口下有三个子接口,分别是Queue、List、Set

Queue接口表示队列,Queue的子接口Deque表示双向队列。Deque的具体实现类ArrayDeque,表示基于数组的队列实现。当需要使用队列数据结构时可以考虑使用ArrayDeque

List接口用来存储有序可重复数据。具体实现类有LinkedListArrayListVectorLinkedList底层是基于双向非循环链表实现的,适合用在增加、删除远远大于查询的场景。  ArrayList底层是基于数组实现的,遍历效率较高。VectorArrayList类似,使用synchronized保证线程安全,但是在不考虑线程安全的情况下,很少使用VectorVector还有一个子类Stack,用来表示。所以当考虑使用栈数据结构时,可考虑Stack

Set接口所有的实现类和子接口都和Map接口对应。包含HashSetTreeSet、子接口SortedSet

HashSet是基于HashMap实现的,把HashSet的值作为HashMapkey,存储一个Object对象作为HashMapValueHashSet还有一个子类LinkedHashSet ,一个基于LinkedHashMap的类。子接口。SortedSet表示可排序集合 SortedSet还有一个子接口NavigableSet表示可做范围查询的集合。 TreeSet即实现了Set接口,又实现了NavigableSet,底层是TreeMap

Map接口有两个实现类HashMap HashTable和一个子接口SortedMap HashMap底层是基于数组+链表+红黑树实现的,在需要快速获取容器中一个值的场景中使用频率比较高,是平时开发时非常常用的集合。  HashMap还有个子类LinkedHashMap在一些JSON数据转换时默认的转换类型就是它。

HashTableHashMap类似,但是有synchronized,每次操作会锁住整个HashTable,效率较低,目前使用较少。HashTable的子类Properties常用在属性文件读取上  SortedMap表示可对Key做排序,子接NavigableMap表示可做范围查询,具体实现类是TreeMap,底层是红黑树。

ListSet的区别?

ListSet都是Collection接口的子接口。分别表示有序可重复和无序不可重复。具 体点说:

List 接口:只要实现List接口都表示类中数据是有顺序的,存储时的顺序和添加顺序有关系。因为是有顺序的,所以里面的数据也是可以有重复数据的

Set 接口:只要实现Set接口,里面的数据都是不允许重复的。且存储顺序和添加顺序是不一致的,所以认为是无序的。但是Set接口很多实现类都是基于Map实现的,所有存储的数据会按照特定的结构和顺序进行存储。我们每次从Set里面获取到的数据都是固定顺序的

 ArrayListLinkedList的区别?

ArrayListLinkedList都是List接口的实现类。这两个类都是存储有顺序、可重复数据的容器。他们的区 别在于底层数据结构,  ArrayList层是数组,   LinkedList底层是链表。

ArrayList底层是数组,在添加数据时如果数组存放不下进行扩容。由于底层是数组,所以遍历效率较高。

LinkedList:底层是双向非循环链表。添加数据时会在链表末尾添加一个节点。由于底层是链表,新增和删除效率较高。并且LinkedList因为是链表结构所以在实现时相比ArrayList还多了addFirst() addLast()getFirst()getLast() removeFist() removeLast()等头尾删除的方法。

同时因为LinkedList1.6版本开始实现了Deque接口,  LinkedList表示基于链表实现的双向队列。还 提供了push() peek()poll()等队列操作方法。

 ArrayListVector的区别?

ArrayList:新增方法没有使用synchronized关键字修饰,  get() remove()方法也使用了synchronized 修饰,扩容方法每次扩容1/2

Vector新增方法使用synchronized关键字修饰,每次扩容1倍

ArrayList:底层每次扩容1/2,新数组长度是原数组长度的3/2。且由于不是synchronized的方法,所以 在多线程下效率更高,但不能保证线程安全性。

Vector:底层每次扩容1倍,新数组长度是原数组的2倍。方法使用synchronized关键字修饰,是线程安全的。多线程下其他线程必须等待当前线程执行完成才能执行。

HashMap底层原理

Java8开始HashMap的底层是数组+链表+红黑树。而Java 7之前HashMap底层是数组+表,没有把链表转换为红黑树这个规则。

当实例化HashMap时,在Java 7中会默认实例化一个长度为16的数组,并设置扩容因子为0.75。而从 Java 8开始只是设置了扩容因子为0.75,变成了在第一次新增时创建默认长度16的数组。

当数组需要扩容时,Java7中只会判断元素个数是否到达总容量的75%,达到后会对数组中元素扩容,并根据元素Hash值重新放置到新数组中。而从Java 8 开始数组扩容有两种情况,第一种和Java7相同,断元素个数是否到达75%,开始扩容值。第二种是数组长度小于64,但链表长度已经到达8时进行扩容。

当新增元素时,如果元素已经存在会覆盖掉之前的内容。如果元素不存在,在Java 7 中会创建一个Entry 对象,根据KeyHash值,确定对象放置位置。当发生Hash碰撞后会形成链表,且使用头插法把对象插入到链表中。而从Java 8之后,元素类型变成Entry的子类Node型对象。且不再使用头插法,而变成了尾插法如果链表长度大于等于8,且数组长度大于等64,会把链表转换为红黑树,来提升查询的性能。转化时元素类型变成了Node的子类TreeNode类型。这也是从Java 8开始HashMap底层最大的变化了。

当删除元素时,会对元素KeyHash计算后定位到数组的某个位置,然后遍历这个位置对应的链表或红黑树,找到元素后进行删除。如果这个位置是红黑树,删除元素后,红黑树中元素个数小于等6会把红黑树转换为链表  HashMap只有扩容,并没有缩容。

当查询元素时,会对KeyHash运算,定位到数组中某个位置。然后遍历链表或红黑树,直到找到这个 元素,找到后返回元素的地址。

HashMapHashTable的区别?

HashMapHashTable都是Map接口的实现类,底层都是散列表。

HashMap:方法没有使用synchronized修饰。KeyValue都允许为null。但是由于Mapkey是不允许 重复的,第二次key=null时会覆盖上次的value

HashTable方法使用synchronized修饰KeyValue都不允许为null。目前HashTable已经很少被使用了。如果希望在多线程下线程安全,推荐使用ConcurrentHashMap

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值