1.List:是有序的Collection。使用此接口能够精确地控制每一个元素插入的位置。用户能够使用索引(元素在list中的位置,类似于数组下标)来访问list中的元素。和Set不同,List允许有相同的元素。常见的List的实现类:
①.LinkedList:是以元素列表的形式存储它的数据,每一个元素都和它的前一个元素和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(N)。提供了额外的get、remove、insert方法在linkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack)、队列(queue)或双向队列(deque).此方法非同步。
②.ArrayList:它的底层是数组,实现了可变大小的数组,它可以以O(1)时间复杂度对元素进行随机访问。每个ArrayList实例都有一个容量,表示存储元素的数组大小,这个容量可随着不断添加新元素而自动增加。此方法非同步。
对比:相对于LinkedList, ArrayList的插入,添加,删除操作速度慢一些,因为当元素被添加到集合任意位置的时候,需要像数组那样重新计算大小或者是更新索引。
但LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向后一个元素。
2.Set:它具有和Collection完全一样的接口,没有任何额外的功能。它是一种不包含重复元素的Collection。Set最多有一个null元素。public interface Set<E> extends Collection<E>, 常用的Set的实现类有:
①.HashSet:内部使用Map保存数据,即将HashSet的数据作为Map的数据作为Map的key值保存,这也是HashSet中元素不能重复的原因。而Map中保存key值前,会先判断当前Map对象,内部是通过key的hashcodes确定有相同的hashcode之后,再通过equal方法判断是否相同。此实现不是同步的。(无序) 时间复杂度是 O(1).
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{....}
②.LinkedHashSet:基于可预知迭代顺序的set接口的哈希表和链接列表实现。与HashSet的不同点:LinkedHashSet它维护着一个运动于所有条目的双重链接列表。
③.TreeSet是由一个树形的结构来实现的,底层是红黑树。它里面的元素是有序的。因此add(),remove(),contain()方法的时间复杂度是O(logn);
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{...}
3.Map:没有继承Collection接口,他提供key到value的映射。一个map中不能包含相同的key.map接口提供了3种集合的试图,map的内容可以被当作一组key集合、一组value集合,或者一组key-value映射。常用的map的实现类:
①.HashMap:底层是数组加链表实现的哈希表。允许null作为键,null作为值。线程不安全。(查找快,插入,删除也快),为什么用数组+链表实现吗?
i>利用拉链法解决冲突:把所有的同义词用单链表连接起来。该方法下,哈希表每一个单元中存放的不再是元素本身,而是相应同义词单链表的头指针。
ii>HashMap维护了一个Entry数组,Entry内部类有key,value,hash,next四个字段,其中next也是一个Entry类型,可以将Entry数组理解为一个个的散列桶。每一个桶实际上是一个单链表。当执行put操作时,会根据key的HashCode定位到相应的桶。
iii>遍历单链表检查该key是否已经存在,如果存在,覆盖该value,反之,新建一个新的Entry,并放在单链表的头部。
当通过传递key调用get方法时,它再次使用key.hashCode()来查找相应的散列桶,然后再使用key.equal()方法找出单链表中正确的Entry,然后返回它的值。
②.HashMap和HashTable的区别:(都实现了Map接口)
i> HashMap是非线程安全的,HashTable是线程安全的。(Hasptable的方法是synchronize的,而HashMap不是,在多个线程访问HashTable时,不需要自己为他的方法实现同步,而HashMap就必须为之提供外同步)。
ii>HashTable不允许键和值出现null值。
HashMap的键和值都允许有null值存在,null可以作为键,但是这样的键只有一个,可以有一个键或多个键的值为null,当get()方法返回null值时,即可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键而应该用containsKey()方法来判断。
iii>因为线程安全问题,HashMap效率比HashTable高。
iv>两个遍历方式的内部实现上不同。(Hashtable、HashMap都使用了Iterator.而由于历史原因,Hashtable还使用了Enumeration的方式)。
v>哈希值的使用不同,HashTable直接使用对象的hashCode.而HashMap重新计算hash值。
vi>Hashtable和HashMap他们两个内部实现方式的数组的初始大小和扩容方式。HashTable中hash数组默认大小是11,增加的方式是old*2+1.HashMap中hash数组的默认大小为16,而且一定是2的指数。
vii>HashTable继承自Dictionary类,而HashMap是继承了AbstractMap类。下面是源码:
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
........
}
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
.......
}