简介
Collection是层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
自从JDK1.2以来,Collection接口的定义源码如下:
//继承自Iterable为实现类提供可以使用加强遍历的方法,并定义实现类必须重写以下方法
public interface Collection<E> extends Iterable<E> {
//以下为常用方法
int size();//获取大小
boolean isEmpty();//判断是否为空
boolean contains(Object o);//判断是否包含
Iterator<E> iterator();//提供加强遍历方法
Object[] toArray();//转化为数组
<T> T[] toArray(T[] a);
boolean add(E e);//增加元素
boolean remove(Object o);//移除元素
boolean containsAll(Collection<?> c);//是否包含另一个集合
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {//过滤器
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collection<?> c);//保持集合
void clear();//清空
boolean equals(Object o);//比较
int hashCode();
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
正文
先来看一张图,我大概整理了一下collection下的所有常用的实现接口和类
1.List
Java中的List是非常常用的数据类型。List是有序的Collection。Java一共三个实现类:ArrayList,LinkedList,Vector
<1.1>ArrayList(线性表)
ArrayList是最常用的List实现类,内部维护了一个可以动态扩容的数组。它允许对元素的快速随机访问(支持下标检索)。由于数组在内存中是连续的,当数组容量不能满足要求时,需要将数组复制到另一个更大的数组(动态扩容就是基于此原理),会造成很大的资源浪费。当ArrayList在插入或者删除元素的时候,几乎都需要大范围的复制、移动,代价昂贵。
小知识:
在java中,byte=1byte,short=2byte,int=4byte,long=8byte,float=4byte,double=byte,boolean=1byte,
--------------char=2byte=一个汉字
ArrayList总结
1.ArrayList排列有序,可重复;
2.底层使用数组实现;
3.查找和修改速度快,增删速度慢且代价昂贵;
4.线程不安全;
5.当容量不足时,扩容至当前容量*1.5+1;
<1.2>LinkedList(链表)
LinkedList使用链表存储数据,很适合动态的增删操作,随机访问和遍历速度慢;另外它还提供了List接口中没有定义的方法,利用头尾指针专门用于操作表头和表尾,可当作堆,栈,队列使用;
LinkedList总结
1.LinkedList排列有序,可重复;
2.底层使用双向循环链表的数据结构;
3.查询修改速度满,增删速度快;
4.线程不安全;
<1.3>Vector (同步线性表)
Vector和ArrayList大致相同,唯一不同的是他支持线程的同步,因为它所有的实现方法上都带有synchronized关键字来保证线程安全,但实现同步需要花费很高的代价,因此他的访问速度慢;
总结Vector
1.排列有序,可重复
2.底层使用数组,查找快,增删慢;
3.线程安全但效率低;
2.Set
一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
<2.1>HashSet(哈希表)
HashSet存储元素的顺序并不是按存入的顺序(和List)不同,而是按照hash值来存取的。元素的hash值通过hashcode方法来获取,HashSet首先判断两个元素的hashcode值是否相同,如果相同接着使用equals方法进行比较,如果为ture则表示两个元素相同;所以要是两个元素不同,必须重写hashcode和equals方法;
总结
1.底层由HashMap实现;
2.排列无序且不可重复;
3.存取速度快
<2.2>TreeSet(二叉树)
TreeSet基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
总结
1.TreeSet是使用二叉树的原理对对象按照指定的顺序进行排序(升序、降序),每增加一个对象都会进行排序,将对象插入到指定的位置(AVL平衡树)
2.Integer和String类型都可以机型默认的TreeSet排序,而自定义对象必须继承Compareable接口并复写compareTo方法才可以正常使用;
3.比较此对象与指定对象的顺序。如果大于、等于或小于指定对象,compareTo方法返回正数,0,负数;
<2.3>LinkedHashSet(HashSet+LinkedHashMap)
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的元素的影响;
对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。
LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法
操作上又与 HashSet 相同,因此 LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并
通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操
作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可。
总结
1.LinkedHashSet基于HashSet,底层是由LinkedHashMap(其基于HashMap)实现;
2.线程不安全;
3.Map
Map实质上是一个键值对<K,V>的映射,一个映射不能包含重复的键,每个键最多只能映射一个值;Map 接口提供三种collection 视图(注意:只是有视图关系,保证可以有方法遍历,但是Map并没有继承Collection接口) ,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。
< 3.1>HashMap(数组+链表+红黑树)
HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快
的访问速度,但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为 null,允许多条记
录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使
HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。
我们用下面这张图来介绍HashMap 的结构:
(在JDK1.8之后,HashMap的底层是由一个数组和红黑树或者链表组成,当某一点的边表长度大于8的时候就会自动转化为红黑树,当小于6的时候会自动转化成链表)
因为红黑树的平均查找长度是log(n),长度为8,查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。
总结
1.HashMap底层是由桶+红黑树/链表组成;
2.HashMap允许键=null或值=null;
3.HashMap线程不安全;
4.HashMap支持序列化;
5.不包含contains()方法;
< 3.2>HashTable
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法;
它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换。
总结
1.HashTable继承自Dictionary;
2.HashTable不允许键=null或值=null;
3.HashTable线程安全;
4.包含contains()方法;
< 3.3>TreeMap
TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,
也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。
如果使用排序的映射,建议使用 TreeMap。在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的Comparator,否则会在运行时抛出 java.lang.ClassCastException 类型的异常。
总结
1.键不可重复,值可重复;
2.底层为红黑树;