1. 集合简介
2. Collection接口
Collection接口是最基本的集合接口,它不提供直接的实现,其表示一个独立元素的序列,这些元素都服从一条或多条规则。如有些按照顺序插入,有些允许重复而有些不允许。
2.1 List接口(有序、可重复)
List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。常用List实现类有:ArrayList、LinkedList、Vector。
2.1.1 ArrayList
ArrayList是一个动态数组。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。
ArrayList擅长于随机访问。同时ArrayList是非同步的。
2.1.2 LinkedList
LinkedList同样是实现了List接口的一种集合,它是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,insert方法在LinkedList的首部或尾部。
由于实现的方式不同,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这样做的好处就是可以通过较低的代价在List中进行插入和删除操作。
与ArrayList一样,LinkedList也是非同步的。
常用方法
方法名 | 作用 |
---|---|
add | 尾插入 |
peek | 查看头结点(空的话返回null ) |
poll | 头删除并返回(空的话返回null ) |
push | 头插入 |
pop | 头删除并返回(null 的话抛出NoSuchElementException 异常) |
2.1.3 Vector
与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。
2.2 Set接口(无序、不能重复)
Set是一种不包括重复元素的Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与List一样,它同样运行null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。常用Set实现类有:EnumSet、HashSet、TreeSet。
2.2.1 EnumSet
枚举的专用Set。所有的元素都是枚举类型
2.2.2 HashSet
HashSet堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序
说明: 要求添加进Set中的元素所在的类,一定要重写equals()
和hashCode()
方法。进而保证Set中元素的不可重复性
2.2.3 TreeSet
基于TreeMap,生成一个总是处于排序状态的set,内部以TreeMap来实现。它是使用元素的自然顺序对元素进行排序,或者根据创建Set 时提供的 Comparator 进行排序,具体取决于使用的构造方法
3. Map接口(键值对、键唯一、值不唯一)
Map是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。常用Map实现类有:HashMap、TreeMap、Hashtable。
3.1 HashMap
以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table
),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。
3.2 Hashtable
Hashtable是以哈希表数据结构实现的,解决冲突时与HashMap也一样也是采用了散列链表的形式,不过性能比HashMap要低
3.3 TreeMap
特点:按照添加进Map中的元素的“Key”的指定属性进行排序。要求:Key必须是同一个类的对象
键以某种排序规则排序,内部以red-black(红-黑)树数据结构实现,实现了SortedMap接口
3.4 ConcurrentHashMap
(待完善…)
4. Iterator迭代器
所有的集合类,都实现了Iterator
接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
hasNext()
:是否还有下一个元素。next()
:返回下一个元素。remove()
:删除当前元素。
5. 集合的遍历方法
5.1 for循环
for(int i = 0; i < list.size(); i++){
...
}
5.2 foreach
foreach(int n : list){
...
}
5.3 Iterator迭代器
Iterator it = list.iterator();
while(it.hasNext()){
object o = it.next();
...
}
6. 主要实现类的区别总结
6.1 HashMap 和 Hashtable 区别
线程安全
Hashtable 是线程安全的,HashMap 不是线程安全的。
Hashtable 所有的元素操作都是 synchronized
修饰的,而 HashMap 并没有
性能优劣
由于Hashtable是线程安全的,所以每个方法都要阻塞其他线程,导致Hashtable性能较差,而HashMap性能较好。
若需要线程安全又保证性能,可以使用 JUC 包下的 ConcurrentHashMap
允许NULL
Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
实现方式
这里看一下HashMap和Hashtable的类定义就清楚了:
HashMap 的继承源码:
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
Hashtable 的继承源码:
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable,java.io.Serializable
容量扩容
HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
迭代器
HashMap 中的 Iterator
迭代器是 fail-fast
的,而 Hashtable 的 Enumerator
不是 fail-fast 的。
所以,当其他线程改变了HashMap 的结构,如:增加、删除元素,将会抛出 ConcurrentModificationException
异常,而 Hashtable 则不会。
6.2 Vector 和 ArrayList 区别
线程安全
Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
扩容值
如果集合中的元素的数目大于目前集合数组的长度时,Vector增长率为目前数组长度的100%,而ArrayList增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用Vector有一定的优势。
性能
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)
所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
[参考书籍]:《Java编程思想》
参考博文:集合大家族
参考博文:java集合详解和集合面试题目