1、谈一下Java中的容器划分(概览)?
分为两大类,是由两大接口派生而来:一个是Collection接口,一个是Map接口;
Collection接口主要用于存放单一元素;Map接口主要用于存放键值对这种类型的数据;
—-> Collection接口下又分为:
List、Queue和Set三个接口;
List接口下主要有两个实现类:ArrayList和Vector;
Set接口下主要有HashSet、TreeSet和LinkedHashSet;
Queue下接口主要有ArrayQueue;
Map接口下主要有HashMap、HashTbale和TreeMap三个实现类;
其中,List存储的元素是有序的可重复的;
Set存储的元素是无序的,不可重复的;
Queue存储的元素按特定的排队规则来确定先后顺序,存储的元素是有序的,可重复的;
Map使用键值对来存储;
2、各个集合底层的数据结构?
List接口下:
ArrayList底层是Object[]数组;
Vector底层是Object[]数组;
LinkedList底层是双向链表;
Set接口下:
HashSet:基于HashMap实现,底层采用HashMap来保存元素;
LinkedHashSet:LinkedHashSet的子类,内部通过LinkedHashMap实现;
TreeSet:红黑树(自平衡的排序二叉树);
Queue接口下:
ArrayDeque:Object[]数组+双指针
PriortyQueue:Object[]数组来实现二叉堆
Map接口下:
HashMap:JDK8之前是数组+链表;JDK之后是数组加链表或红黑树;
LinkedHashMap:继承自HashMap,底层仍然是基于拉链式散列结构即由数组+链表或红黑树组成;
Hashtable:数组+链表;
TreeMap:红黑树(自平衡的排序二叉树)
3、ArrayList和Vector的区别?
ArrayList线程不安全;Vector线程安全;
4、Arraylist和LinkedList的区别?
ArrayList和LinkedList都是线程不同步的,都不保证线程安全;
ArrayList底层是Object[]数组,而LinkedList底层是双向链表;
ArrayList采用数组存储,所以插入和删除元素一般来说比较耗时,LinkedList如果实在头尾插入或者删除元素是比较迅速的;
ArrayList查询元素比较快,而LinkedList元素就比较慢了;
5、ArrayList的底层实现原理?
以JDK8来说明:ArrayList底层是一个动态的Object[]数组;
- 当我们初始化一个ArrayList的时候,即new ArrayList<>()时,会创建一个空的Object类型的数组;
- 当我们对创建的ArrayList实例第一次调用add()方法添加数据的时候,底层会创建长度为10的数组,并将数据放到下标0的位置;
- 后面继续添加数据,如果添加的时候,超出了数组设定好的长度,那么就会执行其扩容机制:扩容时,默认扩容为原本长度的1.5倍(如果结果有小数位就会丢掉小数),然后将原有的数据复制到新的数组中;
6、ArrayLsit的扩容机制?
参照第五个问题;更加详细的情况需要参照源码;
7、针对集合容器的无序性和不可重复性,这二者的含义是什么?
无序性:
无序性不等于随机性,无序性是指存储的数据在底层数组中,并不是按照数组索引的顺序存储的,而是根据数据的哈希值决定的;
不可重复性:
不可重复性,是指添加的元素按照equals()判断时,返回false,需要同时重写equals方法和hashode方法;
8、HashMap和Hashtable的区别?
- HashMap是线程不安全的,而Hashtable是线程安全的,因为Hashtable内部方法都经过synchronized修饰;
- HashMap的效率要比Hashtable高一些(由于线程安全问题);
- HashMap时允许null键和null值的,但是null作为键的话只能有一个,作为值可以有多个;Hashtable不允许键为null;
- 创建时,如果不指定初始容量大小,HashMap默认的初始大小为16,后续每次扩容,容量为原来的两倍;而Hashtable默认的初始大小为11,之后每次扩充,容量变为原本的2n+1;
- 创建时,如果指定了初始容量大小,那么Hashtable的大小就是指定的值;但是HashMap的大小会将指定的 值扩充为2的幂次方;
- jdk8中,HashMap底层的结构是数组+链表或红黑树;而Hashtable的底层是数组+链表,不存在链表可能会变为红黑树的机制;