容器类与数组:数组在初始化时就指定了大小,以后就不可以调整大小,而容器类可以自动调整大小。数组通过下标访问里面的元素,而容器里则可以通过迭代器访问。
下面贴上容器类的类继承关系图:
1. Collection
Collection的实现类包括List
Set
Queue
。
泛型接口如下:
boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()
移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)
比较此 collection 与指定对象是否相等。
int hashCode()
返回此 collection 的哈希码值。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size()
返回此 collection 中的元素数。
Object[] toArray()
返回包含此 collection 中所有元素的数组。
<T> T[]
toArray(T[] a)
1.1 List
元素可重复,List的实现类包括ArrayList
LinkedList
Vector
。
ArrayList
: 底层实现基于数组,查询快,增删慢,效率高,线程不安全。当容量不足时,自增长度的一半。
LinkedList
: 底层实现基于链表,查询慢,增删快,效率高,线程不安全。
Vector
: 底层实现基于数组,查询快,增删慢,效率低,线程安全。当容量不足时,自增长度的一倍。
1.2 Set
元素唯一,Set的实现类包括HashSet
TreeSet
。
HashSet
: 底层实现基于HashMap,线程不安全。通过对象的hashCode方法与equals方法来保证插入元素的唯一性,无序(存储顺序和取出顺序不一致)。
LinkedHashSet
: 是HashSet的子类,底层数据结构由哈希表和链表组成,线程不安全。哈希表保证元素的唯一性,链表保证元素有序。(存储和取出是一致)
TreeSet
: 底层实现基于TreeMap 的 NavigableSet,线程不安全。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
1.3 Queue
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将是最后被删除的元素,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。
Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果要使用前端而不移出该元素,使用element()或者peek()方法。
LinkedList
: 它实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
ConcurrentLinkedQueue
: 线程安全的非阻塞队列。
ArrayBlockingQueue
: 线程安全的阻塞队列,基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,队列初始化时需要指定数组大小。
LinkedBlockingQueue
: 基于链表的阻塞队列,因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。 但值得注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
PriorityBlockingQueue
: PriorityBlockingQueue里面存储的对象必须是实现Comparable接口。队列通过这个接口的compare方法确定对象的priority。
规则是:当前和其他对象比较,如果compare方法返回负数,那么在队列里面的优先级就比较高。 如果将PriorityBlockingQueue队列中的全部元素循环打印出来,你会发现里面的元素并不是全部按优先级排序的,但是队列头部元素的优先级肯定是最高的。
ArrayBlockingQueue与LinkedBlockingQueue的不同之处:
1、锁的实现不同
ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁,由此也意味着两者无法真正并行运行。
LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock。
2、内部元素操作不同
ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的,即不会产生任何额外的对象实例。
LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node进行插入或移除,这在长时间内需要高效并发地处理大批量数据的系统中,对GC和性能会有一定影响。
3、 队列初始化方式不同
ArrayBlockingQueue实现的队列中必须指定队列的大小。
LinkedBlockingQueue实现的队列中可以不指定队列的大小,默认是Integer.MAX_VALUE。
参考
java阻塞队列:http://ifeve.com/java-blocking-queue/
java各个数据结构区别:https://www.cnblogs.com/diegodu/p/6119701.html