java 中提供了一系列的创建不可变集合的方法 和 创建同步集合的方法。下面进行简单的介绍。
一、UnmodifiableXXX 不可变集合
1、 实现原理
下面以 UnmodifiableList 为例来进行说明。
static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> { private static final long serialVersionUID = -283967356065247728L; final List<? extends E> list; UnmodifiableList(List<? extends E> list) { super(list); 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 set(int index, E element) { throw new UnsupportedOperationException(); } public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public int indexOf(Object o) {return list.indexOf(o);} public int lastIndexOf(Object o) {return list.lastIndexOf(o);} public boolean addAll(int index, Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public void replaceAll(UnaryOperator<E> operator) { throw new UnsupportedOperationException(); } @Override public void sort(Comparator<? super E> c) { throw new UnsupportedOperationException(); } public ListIterator<E> listIterator() {return listIterator(0);} public ListIterator<E> listIterator(final int index) { return new ListIterator<E>() { private final ListIterator<? extends E> i = list.listIterator(index); 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() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } public void add(E e) { throw new UnsupportedOperationException(); } @Override public void forEachRemaining(Consumer<? super E> action) { i.forEachRemaining(action); } }; } public List<E> subList(int fromIndex, int toIndex) { return new UnmodifiableList<>(list.subList(fromIndex, toIndex)); } /** * UnmodifiableRandomAccessList instances are serialized as * UnmodifiableList instances to allow them to be deserialized * in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList). * This method inverts the transformation. As a beneficial * side-effect, it also grafts the RandomAccess marker onto * UnmodifiableList instances that were serialized in pre-1.4 JREs. * * Note: Unfortunately, UnmodifiableRandomAccessList instances * serialized in 1.4.1 and deserialized in 1.4 will become * UnmodifiableList instances, as this method was missing in 1.4. */ private Object readResolve() { return (list instanceof RandomAccess ? new UnmodifiableRandomAccessList<>(list) : this); } }
UnmodifiableList 继承 UnmodifiableCollection类,UnmodifiableCollection 中涉及到元素改动(新增、删除、清空…..)的方法都直接抛出 UnsupportedOperationException 异常,并不改动元素;Iterator 中涉及到元素修改的方法也一样不进行元素的改动。
static class UnmodifiableCollection<E> implements Collection<E>, Serializable { private static final long serialVersionUID = 1820017752578914078L; final Collection<? extends E> c; UnmodifiableCollection(Collection<? extends E> c) { if (c==null) throw new NullPointerException(); this.c = c; } 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>() { private final Iterator<? extends E> i = c.iterator(); public boolean hasNext() {return i.hasNext();} public E next() {return i.next();} public void remove() { throw new UnsupportedOperationException(); } @Override public void forEachRemaining(Consumer<? super E> action) { // Use backing collection version i.forEachRemaining(action); } }; } 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(); } }
UnmodifiableList 中对 UnmodifiableCollection 中一些方法根据自身需求进行了覆盖,保持元素不可变的实现方式与UnmodifiableCollection一样。
2、使用
List<String> list = new ArrayList<String>(); list.add("aa"); list.add("bb"); List<String> unmodifiableList = Collections.unmodifiableList(list); System.out.println(unmodifiableList);//[aa, bb]
当我们调用 当unmodifiableList.add(“add”)时,运行代码将会出现以下异常:
Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableCollection.add(Collections.java:1018) at com.wyp.test.testFiles(test.java:152) at com.wyp.test.main(test.java:160)
然而 Collections.unmodifiableList(…) 实现的不是真正的不可变集合,当原始集合被修改后,不可变集合里面的元素也是跟着发生变化。
在上述代码的下面加入以下代码:
list.add("cc"); System.out.println(unmodifiableList);
再次打印unmodifiableList的时候,你会发现结果是[aa, bb, cc],多了一个”cc”元素
Collections.synchronizedXXX 同步集合
1、实现
先来看一下同步集合的共同父类的实现。
static class SynchronizedCollection<E> implements Collection<E>, Serializable { private static final long serialVersionUID = 3053995032091335093L; final Collection<E> c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<E> c) { this.c = Objects.requireNonNull(c); mutex = this; } SynchronizedCollection(Collection<E> c, Object mutex) { this.c = Objects.requireNonNull(c); this.mutex = Objects.requireNonNull(mutex); } public int size() { synchronized (mutex) {return c.size();} } public boolean isEmpty() { synchronized (mutex) {return c.isEmpty();} } public boolean contains(Object o) { synchronized (mutex) {return c.contains(o);} } public Object[] toArray() { synchronized (mutex) {return c.toArray();} } public <T> T[] toArray(T[] a) { synchronized (mutex) {return c.toArray(a);} } public Iterator<E> iterator() { return c.iterator(); // Must be manually synched by user! } public boolean add(E e) { synchronized (mutex) {return c.add(e);} } public boolean remove(Object o) { synchronized (mutex) {return c.remove(o);} } public boolean containsAll(Collection<?> coll) { synchronized (mutex) {return c.containsAll(coll);} } public boolean addAll(Collection<? extends E> coll) { synchronized (mutex) {return c.addAll(coll);} } public boolean removeAll(Collection<?> coll) { synchronized (mutex) {return c.removeAll(coll);} } public boolean retainAll(Collection<?> coll) { synchronized (mutex) {return c.retainAll(coll);} } public void clear() { synchronized (mutex) {c.clear();} } public String toString() { synchronized (mutex) {return c.toString();} } // Override default methods in Collection @Override public void forEach(Consumer<? super E> consumer) { synchronized (mutex) {c.forEach(consumer);} } @Override public boolean removeIf(Predicate<? super E> filter) { synchronized (mutex) {return c.removeIf(filter);} } @Override public Spliterator<E> spliterator() { return c.spliterator(); // Must be manually synched by user! } @Override public Stream<E> stream() { return c.stream(); // Must be manually synched by user! } @Override public Stream<E> parallelStream() { return c.parallelStream(); // Must be manually synched by user! } private void writeObject(ObjectOutputStream s) throws IOException { synchronized (mutex) {s.defaultWriteObject();} } }
这里在构造函数中初始化了一个对象锁 mutex ,并使用这个锁来使用同步代码块对相关操作进行同步控制。
下来再来看看 SynchronizedList 的实现(SynchronizedSet, SynchronizedMap 等的实现与之类似)
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; } 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);} } // .... }
SynchronizedList 继承SynchronizedCollection类,实现了List接口,以组合的方式包含了 final 的 List 成员变量。相关的方法中使用 父类中的 mutex 锁进行同步控制,这样就可以避免子类和父类很相互锁住对方。
需要注意的是,iterator 方法并没有使用 mutex 进行同步控制,需要客户端自行进行同步控制。
2、使用
使用方式很简单,Collections.synchronizedList(list) 这样就可以
注:同步容器在有些情况下不是线程安全的,同步容易的迭代器可能会抛出 ConcurrentModificationException 异常,这些在下一篇文章中详细介绍。