装饰器,顾名思义就是用来装饰对象的。它会接受一个接口对象,并返回一个同样的接口对象,不过,新的对象可能会扩展一些新的方法或属性,扩展的方法或属性就是所谓的“装饰”,也可能会对原有的接口方法进行一些修改,达到一定的“装饰”的目的。
Collections有三组装饰器方法,它们的返回对象都没有新的方法或属性,但改变了原有接口方法的ixngzhi,经过“装饰”后,它们更为安全
具体分别是写安全、类型安全和线程安全
1. 写安全
写安全的主要方法有:
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c)
public static <T> Set<T> unmodifiableSet(Set<? extends T> s)
public static <T> List<T> unmodifiableList(List<? extends T> list)
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m)
这些unmodifiableXXX方法就是使容器对象变为只读的,写入会抛出UnsupportedOperationException。
为什么要只读呢?
典型的场景是需要传递一个容器对象给一个方法,这个方法可能是第三方提供的,为避免第三方误写,所以在传递前,变为只读
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a","b","c","d"));
thirdMethod(Collections.unmodifiableCollection(list));
System.out.println(list);
}
private static void thirdMethod(Collection<String> list) {
list.add("e");
}
这样调用会抛出UnsupportedOperationException
原理上比较简单:就是把传入的Collection赋给一个final的Collection,然后返回一个实现Collection的内部类,重写 Collection的写方法,使其抛出UnsupportedOperationException
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
return new UnmodifiableCollection<>(c);
}
// UnmodifiableCollection是一个内部类:
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 1820017752578914078L;
final Collection<? extends E> c; // 一个不可变的Collection
UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
// 读方法时返回传入Collection的读方法
public int size() {return c.size();}
public boolean isEmpty() {return c.isEmpty();}
public boolean contains(Object o) {return c.contains(o);}
public Object[] toArray() {return c.toArray();}
public <T> T[] toArray(T[] a) {return c.toArray(a);}
public String toString() {return c.toString();}
public Iterator<E> iterator() {
return new Iterator<E>() {
// 一个final的Iterator
private final Iterator<? extends E> i = c.iterator();
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public void remove() { // 重写迭代器的remove方法,使之抛出异常
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
// Use backing collection version
i.forEachRemaining(action);
}
};
}
// 下面这些写操作,同一返回UnsupportedOperationException
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
public boolean addAll(Collection<? extends E> coll) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
@Override
public Spliterator<E> spliterator() {
return (Spliterator<E>)c.spliterator();
}
@SuppressWarnings("unchecked")
@Override
public Stream<E> stream() {
return (Stream<E>)c.stream();
}
@SuppressWarnings("unchecked")
@Override
public Stream<E> parallelStream() {
return (Stream<E>)c.parallelStream();
}
}
2. 类型安全(个人感觉用处比较少,感兴趣可以看下)
所谓类型安全是指确保容器中不会保存错误类型的对象
容器怎么会保存错误类型的对象呢????
不知道小兄弟有没有见过如下代码:
List list = new ArrayList<String>();
list.add(1);
System.out.println(list);
创建了一个String类型的list对象,但是添加了int类型对象,编译和运行都不会报错;
之所有有这种情况,是因为java是通过擦除来实现泛型的,而类型蚕食是可选的。正常情况下我们加上类型参数,让泛型机制来保证类型的正确性,但java5之后才有泛型,之前的代码可能没有类型参数,而新的代码可能需要与老代码互动
Collection提供了一个方法来避免这种情况
public static <E> List<E> checkedList(List<E> list, Class<E> type) {
return (list instanceof RandomAccess ?
new CheckedRandomAccessList<>(list, type) :
new CheckedList<>(list, type));
}
// 实现是类似的。只举一个例子
//内部类
static class CheckedList<E>
extends CheckedCollection<E>
implements List<E>
{
private static final long serialVersionUID = 65247728283967356L;
final List<E> list;
CheckedList(List<E> list, Class<E> type) {
super(list, type);
this.list = list;
}
public boolean equals(Object o) { return o == this || list.equals(o); }
public int hashCode() { return list.hashCode(); }
public E get(int index) { return list.get(index); }
public E remove(int index) { return list.remove(index); }
public int indexOf(Object o) { return list.indexOf(o); }
public int lastIndexOf(Object o) { return list.lastIndexOf(o); }
public E set(int index, E element) {
return list.set(index, typeCheck(element));
}
public void add(int index, E element) {
list.add(index, typeCheck(element));
}
public boolean addAll(int index, Collection<? extends E> c) {
return list.addAll(index, checkedCopyOf(c));
}
public ListIterator<E> listIterator() { return listIterator(0); }
public ListIterator<E> listIterator(final int index) {
final ListIterator<E> i = list.listIterator(index);
return new ListIterator<E>() {
public boolean hasNext() { return i.hasNext(); }
public E next() { return i.next(); }
public boolean hasPrevious() { return i.hasPrevious(); }
public E previous() { return i.previous(); }
public int nextIndex() { return i.nextIndex(); }
public int previousIndex() { return i.previousIndex(); }
public void remove() { i.remove(); }
public void set(E e) {
i.set(typeCheck(e));
}
public void add(E e) {
i.add(typeCheck(e));
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
i.forEachRemaining(action);
}
};
}
// 父类CheckedCollection的方法typeCheck:
E typeCheck(Object o) {
if (o != null && !type.isInstance(o))
throw new ClassCastException(badElementMsg(o));
return (E) o;
}
剩下 的不想多说了,感觉现在极少有人跟5之前的代码打交道。
3. 线程安全
大多数容器类都是非线程安全的,也就是说如果多个线程同时读写一个容器对象,是不安全的。
非线程安全:
ArrayList
LinkedList
HashMap
StringBuilder
线程安全的容器类:
同步容器类:使用了synchronized
1.Vector
2.HashTable
并发容器:
3.ConcurrentHashMap:分段
4.CopyOnWriteArrayList:写时复制
5.CopyOnWriteArraySet:写时复制
Queue:
6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。
(java.util.concurrent.BlockingQueue 接口代表了线程安全的队列。)
7.ArrayBlockingQueue:基于数组的有界阻塞队列
8.LinkedBlockingQueue:基于链表的有界阻塞队列。
9.PriorityBlockingQueue:支持优先级的无界阻塞队列,即该阻塞队列中的元素可自动排序。默认情况下,元素采取自然升序排列
10.DelayQueue:一种延时获取元素的无界阻塞队列。
11.SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待一个take操作,否则不能继续添加元素。内部其实没有任何一个元素,容量是0
Deque:
(Deque接口定义了双向队列。双向队列允许在队列头和尾部进行入队出队操作。)
12.ArrayDeque:基于数组的双向非阻塞队列。
13.LinkedBlockingDeque:基于链表的双向阻塞队列。
Sorted容器:
14.ConcurrentSkipListMap:是TreeMap的线程安全版本
15.ConcurrentSkipListSet:是TreeSet的线程安全版本
引用自:https://blog.csdn.net/u010002184/article/details/74892663
Collection提供了一组方法,可以将一个容器对象变为线程安全的,比如:
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
public static <T> Set<T> synchronizedSet(Set<T> s)
public static <T> List<T> synchronizedList(List<T> list)
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
需要说明的是,这些方法都是通过给所有容器方法加锁来实现的,这种实现并不是最优的,java提供了很多专门针对并发访问的容器类:最如常用的ConcurrentHashMap
我们看一个同步容器方法:
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
// 这里看下内部类SynchronizedList
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
// 给所有的容器方法都加了synchronized,来保证线程安全
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {
synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (mutex) {return list.addAll(index, c);}
}
public ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user
}
public List<E> subList(int fromIndex, int toIndex) {
synchronized (mutex) {
return new SynchronizedList<>(list.subList(fromIndex, toIndex),
mutex);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
synchronized (mutex) {list.replaceAll(operator);}
}
@Override
public void sort(Comparator<? super E> c) {
synchronized (mutex) {list.sort(c);}
}
/**
* SynchronizedRandomAccessList instances are serialized as
* SynchronizedList instances to allow them to be deserialized
* in pre-1.4 JREs (which do not have SynchronizedRandomAccessList).
* This method inverts the transformation. As a beneficial
* side-effect, it also grafts the RandomAccess marker onto
* SynchronizedList instances that were serialized in pre-1.4 JREs.
*
* Note: Unfortunately, SynchronizedRandomAccessList instances
* serialized in 1.4.1 and deserialized in 1.4 will become
* SynchronizedList instances, as this method was missing in 1.4.
*/
private Object readResolve() {
return (list instanceof RandomAccess
? new SynchronizedRandomAccessList<>(list)
: this);
}
}
给所有容器方法加锁会有几个问题:
- 每个方法都需要同步,支持的并发度比较低
- 对于迭代和复合操作,需要调用方法锁,使用比较麻烦,且容易忘记。