学习Java的同学注意了!!!
学习进程中遇到甚么问题或想获得学习资源的话,欢迎加入Java学习交换群,群号码:183993990 我们1起学Java!
Java实用类库提供了1套相当完全的容器来帮助我们解决很多具体问题。由于我本身是1名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList3剑客, 平时接触使用的容器也只有ArrayList和HashMap。致使对全部Java容器体系的掌握和使用还停留在很浅的层面。省不足而思改进,那末随着我来总结1下Java容器的相干知识吧。
Java容器类库定义了两个不同概念的容器,Collection和Map
(文中Jdk源码版本无特殊说明均为jdk1.8.0_101)
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(java.util.Collection<?> c);
boolean addAll(java.util.Collection<? extends E> c);
boolean removeAll(java.util.Collection<?> c);
... //省略了其他方法
}
可以看到,java定义了Collection接口和内部集合的基本操作方法,Collection默许可以进行对集合末端添加元素,删除指定元素等操作。List、Set、Queue接口都继承自Collection并定义了各自不同的方法。
public interface Map<K,V> {
int size();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(java.util.Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<java.util.Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
...
}
boolean equals(Object o);
int hashCode();
}
Map内部接口Entry<K,V>对应着Map的键值对。
先介绍1下迭代器。迭代器本身也是1种设计模式,设计的初衷在于:容器的实现由很多种,而我们想对容器进行遍历操作的话,首先不应当关心容器实现的细节,其次遍历操作应当是轻量级的。迭代器统1了对容器的访问方式,同时创建它的代价很小。值得注意的是,Iterator只能单向移动。
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
通过容器的iterator()方法拿到容器的迭代器
迭代器的next()获得下1个元素
hasNext()判断是不是还有元素
remove()删除指定元素
ListIterator是Iterator的扩大以内,用于各种List类访问,支持双向移动。
List 许诺可以将元素保护在特定的序列中.List接口在Collection的基础上添加了大量的方法,使得可以再List中间插入和移除元素。
public interface List<E> extends Collection<E> {
...
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
java.util.List<E> subList(int fromIndex, int toIndex);
...
}
有两种类型的List,ArrayList和LinkedList
List类型 | 优点 | 缺点 | 底层实现 |
---|---|---|---|
ArrayList | 随机访问元素较快 | 中间元素的插入和删除较慢 | 数组 |
LinkedList | 中间元素的插入和删除,顺序访问的优化 | 随机访问元素较慢 | 双向链表 |
Set不保存重复的元素,通经常使用于快速查找元素。值得1提的是,Set具有与Collection完全1样的接口,没有任何额外的功能。 存入的元素必须定义equals()方法
Set类型 | 使用处景 | 底层实现 |
---|---|---|
HashSet | 快速查找,元素必须定义hashCode() | 链表 |
TreeSet | 保持次序,元素必须实现Comparable接口 | 红-黑树结构 |
LinkedHashSet | 保护次序的HashSet, 元素必须定义hashCode() | 链表 |
除并发利用,Queue唯一的两个实现是LinkedList和PriorityQueue, 其中LinkedList同时实现了List, Deque接口。它们的差异在于排序行动而不是性能。
public interface Queue<E> extends Collection<E> {
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
}
Map类型 | 使用处景 | 底层实现 |
---|---|---|
HashMap | 快速查询 | 散列表 |
LinkedHashMap | 迭代遍历具有顺序(插入顺序 or 最近最少使用) | 链表 |
TreeMap | 具有排序,唯1可以返回子树的Map(subMap()) | 红-黑树结构 |
WeakHashMap | 弱键映照,映照以外无援用的键,可以被垃圾回收 | 散列表 |
ConcurrentHashMap | 线程安全的Map | 链表 |
IdentityHashMap | 使用==代替equals()对键进行排序,专位解决特殊问题 | 链表 |
我们可以手工调剂HashMap来调剂性能,触及到如容量、初始容量、尺寸、负载因子等概念。感兴趣的话可以看1些相干资料。
这里不会讨论的太细致的实现,仅仅简单介绍1下基础知识,感兴趣的可以浏览《Java 并发编程的艺术》这本书。
Copy-On-Write简称COW,是1种用于程序设计中的优化策略。其基本思路是,从1开始大家都在同享同1个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去构成1个新的内容然后再改,这是1种延时怠惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往1个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出1个新的容器,然后新的容器里添加元素,添加完元素以后,再将原容器的援用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,由于当前容器不会添加任何元素。所以CopyOnWrite容器也是1种读写分离的思想,读和写不同的容器。
CopyOnWrite容器只能保证数据的终究1致性,不能保证数据的实时1致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
在并发编程中,有时候需要使用线程安全的队列或列表。通常实现线程安全有两种方式,1种是使用阻塞算法,1种是使用非阻塞算法。非阻塞算法实现基础为循环CAS(Compare and Swipe 比较和交换)。
ConcurrentLinkedQueue技术上的实现与CopyOnWriteArrayList与Copy类似,但是容器只有部份内容而不是全部容器可以被复制和修改。ConcurrentLinkedQueue有head节点和tail节点组成,每一个节点由节点元素(item)和指向下1个结点(next)的援用组成。节点之间通过next关联起来,构成1张链表结构的队列。
ConcurrentHashMap是线程安全且高效的HashMap。多线程环境下,使用非线程安全的HashMap会致使死循环,而如文章中建议的那样,HashTable这类过时容器效力低下(使用synchronized来保证线程安全)。ConcurrentHashMap使用锁分段技术,大大提高了并发使用的效力。
锁分段技术: 假定容器有多把锁,每把锁用于锁容器其中1部份数据,当多线程访问容器不同数据段数据时,线程间就不存在锁竞争,从而提高并发访问效力。
JDK7 提供了7个阻塞队列,实现原理都是基于生产-消费模式的等待通知机制。
阻塞队列类型 | 特点 |
---|---|
ArrayBlockingQueue | 由数组结构组成的有界阻塞队列 |
LinkedBlockingQueue | 由链表结构组成的有界阻塞队列 |
PriorityBlockingQueue | 支持优先级排序的无界阻塞队列 |
DelayQueue | 使用优先级队列实现的无界阻塞队列 |
SynchronousQueue | 不贮存元素的阻塞队列 |
LinkedTransferQueue | 由链表结构组成的无界阻塞队列 |
LinkedBlockingQueue | 由链表结构组成的双向阻塞队列 |
感谢浏览~
学习Java的同学注意了!!!
学习进程中遇到甚么问题或想获得学习资源的话,欢迎加入Java学习交换群,群号码:183993990 我们1起学Java!