集合类是java中比较重要的类,同时也是面试过程当中经常涉及到的一类问题,在这里对常用的集合类进行一下整理总结。
集合类结构如图1所示:
图1 类结构
由图1可知,ArrayList、Vector、LinkedList类都是实现了List接口,HashSet、TreeSet实现了Set接口,而HashMap、HashTable和TreeMap都是实现了Map接口。结合上图,分别介绍下各种集合类的特点。
一、实现List接口的集合类
1、ArrayList是比较常用的一个集合类。首先,此类是非线程安全的集合类。同时,此类也是有序的且可重复的,即读取的顺序和保存的顺序是一致的。在构造一个初始的ArrayList对象时,其初始大小为10.那么,随着元素不断的增加,当初始化申请的空间存放不下的话,就要进行申请空间,具体算法如下图所示:
public void ensureCapacity(int minCapacity) {也就是当空间不足时,申请的大小为((oldCapacity * 3)/2 + 1),也就是原来大小的1.5倍。
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
2、Vector在非多线程程序中用的不多,这是由其特性决定的。Vector是线程安全的类,也就是同步类(ynchronized)。在其它方面,它是与ArrayList基本上相似的。二者除了线程安全的不同以外,那么就是ArrayList执行的效率要比Vector高,也就是说Vector牺牲了效率换取了安全性,同样的道理,ArrayList牺牲了安全性而换取了效率。其实二者无法区分哪个类比那个类更好,最终是取决于你实际的业务,合适的就是最好的。
3、LinkedList也是经常用到的一个集合类。简单的有名称就可以看出,这是一个基于链表的集合类。那么到底是不是呢?我们结合下面的源码来看:
private static class Node<E> {由代码可以发现,它存在一个前驱属性(prev)和一个后继属性(next),由此可见这是一个双向链表的结构。同样的,LinkedList也是一个非线程安全的集合类。那么它的特点在哪里呢?由于ArrayList和Vector是基于数组,而LinkedList是基于链表,那么这里就结合他们的这些本质特性来比较小两种类型的集合类的效率。
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
首先,增加一个节点数据。这里以ArrayList为例,它增加一个数据后,其后面的节点数据都需要向后移动一个位置,也就是对应的index都需要加1,而LinkedList只需要修改它节点的前驱和后继即可,所以在增加一个数据这个操作过程中,其效率是要高于ArrayList的。
其次,删除一个节点数据,原理同上,只不过是减少,同样是LinkedList的效率要高于ArrayList。
再次,查询一个节点的数据,对应ArrayList,只需要遍历至该节点即可,儿LinkedList则需要对其前驱和后继也就是地址进行操作,其效率要低于ArrayList,所以ArrayList的效率要高。
最后,修改一个节点的数据,那么,在修改之前需要先查询出将要修改的节点,由于ArrayList的查询速度要快于LinkedList,所以修改方面的效率自然是ArrayList高于LinkedList的效率。
综上所述,在增删操作方面,LinkedList的效率要高于ArrayList,而在改查操作方面,则是相反,LinkedList要低于ArrayList。当然,在数据量相对较小的情况下,二者的差距基本上是微乎其微的,但是如果设计到的数据量大的话,那么就要考虑其效率,选择合适的集合类。
二、实现Set接口的集合类
1、HashSet集合类,其实通过源码可以发现,HashSet的实现,在底层是基于HashMap实现的,如下代码所示:
public HashSet(Collection<? extends E> c) {初始化时,初始化一个负载系数为0.75的HashMap,该集合类的特点是无序、不能add重复的值,其实,这也就是由其底层特性所决定的。
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
2、TreeSet集合类,TreeSet集合类的实现是基于TreeMap实现的,TreeMap是一种有序二叉树数据结构,基于这种数据结构,数据的查找和操作则效率更高。
三、实现Map接口的集合类
1、HashMap集合类,这是一个重要的集合类,HashMap集合类的特点是继承了AbstractMap类,且k值和v值都允许为null,重要的一点是,HashMap类是非同步的,也就是说是非线程安全的类。
2、HashTable集合类与HashMap集合类相对应,但是HashTable集合类是继承了Dictionary类,与HashMap相反,它的k和v都是不允许为null,最重要的一点是,HashTable类是同步类,也就是线程安全的集合类。由于是线程安全类,效率上可能低于HashMap类。