JDK 工具类之 Collections 2


    /**
     * Returns a synchronized (thread-safe) map backed by the specified
     * map.  In order to guarantee serial access, it is critical that
     * all access to the backing map is accomplished
     * through the returned map.

* * It is imperative that the user manually synchronize on the returned * map when iterating over any of its collection views: *

     *  Map m = Collections.synchronizedMap(new HashMap());
     *      ...
     *  Set s = m.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not s!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * 
* Failure to follow this advice may result in non-deterministic behavior. * *

The returned map will be serializable if the specified map is * serializable. * * @param the class of the map keys * @param the class of the map values * @param m the map to be "wrapped" in a synchronized map. * @return a synchronized view of the specified map. */ public static Map synchronizedMap(Map m) { return new SynchronizedMap<>(m); } /** * @serial include */ private static class SynchronizedMap implements Map, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { this.m = Objects.requireNonNull(m); mutex = this; } SynchronizedMap(Map m, Object mutex) { this.m = m; this.mutex = mutex; } public int size() { synchronized (mutex) {return m.size();} } public boolean isEmpty() { synchronized (mutex) {return m.isEmpty();} } public boolean containsKey(Object key) { synchronized (mutex) {return m.containsKey(key);} } public boolean containsValue(Object value) { synchronized (mutex) {return m.containsValue(value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} } public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V remove(Object key) { synchronized (mutex) {return m.remove(key);} } public void putAll(Map map) { synchronized (mutex) {m.putAll(map);} } public void clear() { synchronized (mutex) {m.clear();} } private transient Set keySet; private transient Set> entrySet; private transient Collection values; public Set keySet() { synchronized (mutex) { if (keySet==null) keySet = new SynchronizedSet<>(m.keySet(), mutex); return keySet; } } public Set> entrySet() { synchronized (mutex) { if (entrySet==null) entrySet = new SynchronizedSet<>(m.entrySet(), mutex); return entrySet; } } public Collection values() { synchronized (mutex) { if (values==null) values = new SynchronizedCollection<>(m.values(), mutex); return values; } } public boolean equals(Object o) { if (this == o) return true; synchronized (mutex) {return m.equals(o);} } public int hashCode() { synchronized (mutex) {return m.hashCode();} } public String toString() { synchronized (mutex) {return m.toString();} } // Override default methods in Map @Override public V getOrDefault(Object k, V defaultValue) { synchronized (mutex) {return m.getOrDefault(k, defaultValue);} } @Override public void forEach(BiConsumer action) { synchronized (mutex) {m.forEach(action);} } @Override public void replaceAll(BiFunction function) { synchronized (mutex) {m.replaceAll(function);} } @Override public V putIfAbsent(K key, V value) { synchronized (mutex) {return m.putIfAbsent(key, value);} } @Override public boolean remove(Object key, Object value) { synchronized (mutex) {return m.remove(key, value);} } @Override public boolean replace(K key, V oldValue, V newValue) { synchronized (mutex) {return m.replace(key, oldValue, newValue);} } @Override public V replace(K key, V value) { synchronized (mutex) {return m.replace(key, value);} } @Override public V computeIfAbsent(K key, Function mappingFunction) { synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);} } @Override public V computeIfPresent(K key, BiFunction remappingFunction) { synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);} } @Override public V compute(K key, BiFunction remappingFunction) { synchronized (mutex) {return m.compute(key, remappingFunction);} } @Override public V merge(K key, V value, BiFunction remappingFunction) { synchronized (mutex) {return m.merge(key, value, remappingFunction);} } private void writeObject(ObjectOutputStream s) throws IOException { synchronized (mutex) {s.defaultWriteObject();} } } /** * Returns a synchronized (thread-safe) sorted map backed by the specified * sorted map. In order to guarantee serial access, it is critical that * all access to the backing sorted map is accomplished * through the returned sorted map (or its views).

* * It is imperative that the user manually synchronize on the returned * sorted map when iterating over any of its collection views, or the * collections views of any of its subMap, headMap or * tailMap views. *

     *  SortedMap m = Collections.synchronizedSortedMap(new TreeMap());
     *      ...
     *  Set s = m.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not s!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * 
* or: *
     *  SortedMap m = Collections.synchronizedSortedMap(new TreeMap());
     *  SortedMap m2 = m.subMap(foo, bar);
     *      ...
     *  Set s2 = m2.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not m2 or s2!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * 
* Failure to follow this advice may result in non-deterministic behavior. * *

The returned sorted map will be serializable if the specified * sorted map is serializable. * * @param the class of the map keys * @param the class of the map values * @param m the sorted map to be "wrapped" in a synchronized sorted map. * @return a synchronized view of the specified sorted map. */ public static SortedMap synchronizedSortedMap(SortedMap m) { return new SynchronizedSortedMap<>(m); } /** * @serial include */ static class SynchronizedSortedMap extends SynchronizedMap implements SortedMap { private static final long serialVersionUID = -8798146769416483793L; private final SortedMap sm; SynchronizedSortedMap(SortedMap m) { super(m); sm = m; } SynchronizedSortedMap(SortedMap m, Object mutex) { super(m, mutex); sm = m; } public Comparator comparator() { synchronized (mutex) {return sm.comparator();} } public SortedMap subMap(K fromKey, K toKey) { synchronized (mutex) { return new SynchronizedSortedMap<>( sm.subMap(fromKey, toKey), mutex); } } public SortedMap headMap(K toKey) { synchronized (mutex) { return new SynchronizedSortedMap<>(sm.headMap(toKey), mutex); } } public SortedMap tailMap(K fromKey) { synchronized (mutex) { return new SynchronizedSortedMap<>(sm.tailMap(fromKey),mutex); } } public K firstKey() { synchronized (mutex) {return sm.firstKey();} } public K lastKey() { synchronized (mutex) {return sm.lastKey();} } } /** * Returns a synchronized (thread-safe) navigable map backed by the * specified navigable map. In order to guarantee serial access, it is * critical that all access to the backing navigable map is * accomplished through the returned navigable map (or its views).

* * It is imperative that the user manually synchronize on the returned * navigable map when iterating over any of its collection views, or the * collections views of any of its {@code subMap}, {@code headMap} or * {@code tailMap} views. *

     *  NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
     *      ...
     *  Set s = m.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not s!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * 
* or: *
     *  NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
     *  NavigableMap m2 = m.subMap(foo, true, bar, false);
     *      ...
     *  Set s2 = m2.keySet();  // Needn't be in synchronized block
     *      ...
     *  synchronized (m) {  // Synchronizing on m, not m2 or s2!
     *      Iterator i = s.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * 
* Failure to follow this advice may result in non-deterministic behavior. * *

The returned navigable map will be serializable if the specified * navigable map is serializable. * * @param the class of the map keys * @param the class of the map values * @param m the navigable map to be "wrapped" in a synchronized navigable * map * @return a synchronized view of the specified navigable map. * @since 1.8 */ public static NavigableMap synchronizedNavigableMap(NavigableMap m) { return new SynchronizedNavigableMap<>(m); } /** * A synchronized NavigableMap. * * @serial include */ static class SynchronizedNavigableMap extends SynchronizedSortedMap implements NavigableMap { private static final long serialVersionUID = 699392247599746807L; private final NavigableMap nm; SynchronizedNavigableMap(NavigableMap m) { super(m); nm = m; } SynchronizedNavigableMap(NavigableMap m, Object mutex) { super(m, mutex); nm = m; } public Entry lowerEntry(K key) { synchronized (mutex) { return nm.lowerEntry(key); } } public K lowerKey(K key) { synchronized (mutex) { return nm.lowerKey(key); } } public Entry floorEntry(K key) { synchronized (mutex) { return nm.floorEntry(key); } } public K floorKey(K key) { synchronized (mutex) { return nm.floorKey(key); } } public Entry ceilingEntry(K key) { synchronized (mutex) { return nm.ceilingEntry(key); } } public K ceilingKey(K key) { synchronized (mutex) { return nm.ceilingKey(key); } } public Entry higherEntry(K key) { synchronized (mutex) { return nm.higherEntry(key); } } public K higherKey(K key) { synchronized (mutex) { return nm.higherKey(key); } } public Entry firstEntry() { synchronized (mutex) { return nm.firstEntry(); } } public Entry lastEntry() { synchronized (mutex) { return nm.lastEntry(); } } public Entry pollFirstEntry() { synchronized (mutex) { return nm.pollFirstEntry(); } } public Entry pollLastEntry() { synchronized (mutex) { return nm.pollLastEntry(); } } public NavigableMap descendingMap() { synchronized (mutex) { return new SynchronizedNavigableMap<>(nm.descendingMap(), mutex); } } public NavigableSet keySet() { return navigableKeySet(); } public NavigableSet navigableKeySet() { synchronized (mutex) { return new SynchronizedNavigableSet<>(nm.navigableKeySet(), mutex); } } public NavigableSet descendingKeySet() { synchronized (mutex) { return new SynchronizedNavigableSet<>(nm.descendingKeySet(), mutex); } } public SortedMap subMap(K fromKey, K toKey) { synchronized (mutex) { return new SynchronizedNavigableMap<>( nm.subMap(fromKey, true, toKey, false), mutex); } } public SortedMap headMap(K toKey) { synchronized (mutex) { return new SynchronizedNavigableMap<>(nm.headMap(toKey, false), mutex); } } public SortedMap tailMap(K fromKey) { synchronized (mutex) { return new SynchronizedNavigableMap<>(nm.tailMap(fromKey, true),mutex); } } public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { synchronized (mutex) { return new SynchronizedNavigableMap<>( nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); } } public NavigableMap headMap(K toKey, boolean inclusive) { synchronized (mutex) { return new SynchronizedNavigableMap<>( nm.headMap(toKey, inclusive), mutex); } } public NavigableMap tailMap(K fromKey, boolean inclusive) { synchronized (mutex) { return new SynchronizedNavigableMap<>( nm.tailMap(fromKey, inclusive), mutex); } } } // Dynamically typesafe collection wrappers /** * Returns a dynamically typesafe view of the specified collection. * Any attempt to insert an element of the wrong type will result in an * immediate {@link ClassCastException}. Assuming a collection * contains no incorrectly typed elements prior to the time a * dynamically typesafe view is generated, and that all subsequent * access to the collection takes place through the view, it is * guaranteed that the collection cannot contain an incorrectly * typed element. * *

The generics mechanism in the language provides compile-time * (static) type checking, but it is possible to defeat this mechanism * with unchecked casts. Usually this is not a problem, as the compiler * issues warnings on all such unchecked operations. There are, however, * times when static type checking alone is not sufficient. For example, * suppose a collection is passed to a third-party library and it is * imperative that the library code not corrupt the collection by * inserting an element of the wrong type. * *

Another use of dynamically typesafe views is debugging. Suppose a * program fails with a {@code ClassCastException}, indicating that an * incorrectly typed element was put into a parameterized collection. * Unfortunately, the exception can occur at any time after the erroneous * element is inserted, so it typically provides little or no information * as to the real source of the problem. If the problem is reproducible, * one can quickly determine its source by temporarily modifying the * program to wrap the collection with a dynamically typesafe view. * For example, this declaration: *

 {@code
     *     Collection c = new HashSet<>();
     * }
* may be replaced temporarily by this one: *
 {@code
     *     Collection c = Collections.checkedCollection(
     *         new HashSet<>(), String.class);
     * }
* Running the program again will cause it to fail at the point where * an incorrectly typed element is inserted into the collection, clearly * identifying the source of the problem. Once the problem is fixed, the * modified declaration may be reverted back to the original. * *

The returned collection does not pass the hashCode and equals * operations through to the backing collection, but relies on * {@code Object}'s {@code equals} and {@code hashCode} methods. This * is necessary to preserve the contracts of these operations in the case * that the backing collection is a set or a list. * *

The returned collection will be serializable if the specified * collection is serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned collection permits insertion of null elements * whenever the backing collection does. * * @param the class of the objects in the collection * @param c the collection for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code c} is permitted to hold * @return a dynamically typesafe view of the specified collection * @since 1.5 */ public static Collection checkedCollection(Collection c, Class type) { return new CheckedCollection<>(c, type); } @SuppressWarnings("unchecked") static T[] zeroLengthArray(Class type) { return (T[]) Array.newInstance(type, 0); } /** * @serial include */ static class CheckedCollection implements Collection, Serializable { private static final long serialVersionUID = 1578914078182001775L; final Collection c; final Class type; @SuppressWarnings("unchecked") E typeCheck(Object o) { if (o != null && !type.isInstance(o)) throw new ClassCastException(badElementMsg(o)); return (E) o; } private String badElementMsg(Object o) { return "Attempt to insert " + o.getClass() + " element into collection with element type " + type; } CheckedCollection(Collection c, Class type) { this.c = Objects.requireNonNull(c, "c"); this.type = Objects.requireNonNull(type, "type"); } 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[] toArray(T[] a) { return c.toArray(a); } public String toString() { return c.toString(); } public boolean remove(Object o) { return c.remove(o); } public void clear() { c.clear(); } public boolean containsAll(Collection coll) { return c.containsAll(coll); } public boolean removeAll(Collection coll) { return c.removeAll(coll); } public boolean retainAll(Collection coll) { return c.retainAll(coll); } public Iterator iterator() { // JDK-6363904 - unwrapped iterator could be typecast to // ListIterator with unsafe set() final Iterator it = c.iterator(); return new Iterator() { public boolean hasNext() { return it.hasNext(); } public E next() { return it.next(); } public void remove() { it.remove(); }}; } public boolean add(E e) { return c.add(typeCheck(e)); } private E[] zeroLengthElementArray; // Lazily initialized private E[] zeroLengthElementArray() { return zeroLengthElementArray != null ? zeroLengthElementArray : (zeroLengthElementArray = zeroLengthArray(type)); } @SuppressWarnings("unchecked") Collection checkedCopyOf(Collection coll) { Object[] a; try { E[] z = zeroLengthElementArray(); a = coll.toArray(z); // Defend against coll violating the toArray contract if (a.getClass() != z.getClass()) a = Arrays.copyOf(a, a.length, z.getClass()); } catch (ArrayStoreException ignore) { // To get better and consistent diagnostics, // we call typeCheck explicitly on each element. // We call clone() to defend against coll retaining a // reference to the returned array and storing a bad // element into it after it has been type checked. a = coll.toArray().clone(); for (Object o : a) typeCheck(o); } // A slight abuse of the type system, but safe here. return (Collection) Arrays.asList(a); } public boolean addAll(Collection coll) { // Doing things this way insulates us from concurrent changes // in the contents of coll and provides all-or-nothing // semantics (which we wouldn't get if we type-checked each // element as we added it) return c.addAll(checkedCopyOf(coll)); } // Override default methods in Collection @Override public void forEach(Consumer action) {c.forEach(action);} @Override public boolean removeIf(Predicate filter) { return c.removeIf(filter); } @Override public Spliterator spliterator() {return c.spliterator();} @Override public Stream stream() {return c.stream();} @Override public Stream parallelStream() {return c.parallelStream();} } /** * Returns a dynamically typesafe view of the specified queue. * Any attempt to insert an element of the wrong type will result in * an immediate {@link ClassCastException}. Assuming a queue contains * no incorrectly typed elements prior to the time a dynamically typesafe * view is generated, and that all subsequent access to the queue * takes place through the view, it is guaranteed that the * queue cannot contain an incorrectly typed element. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned queue will be serializable if the specified queue * is serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned queue permits insertion of {@code null} elements * whenever the backing queue does. * * @param the class of the objects in the queue * @param queue the queue for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code queue} is permitted to hold * @return a dynamically typesafe view of the specified queue * @since 1.8 */ public static Queue checkedQueue(Queue queue, Class type) { return new CheckedQueue<>(queue, type); } /** * @serial include */ static class CheckedQueue extends CheckedCollection implements Queue, Serializable { private static final long serialVersionUID = 1433151992604707767L; final Queue queue; CheckedQueue(Queue queue, Class elementType) { super(queue, elementType); this.queue = queue; } public E element() {return queue.element();} public boolean equals(Object o) {return o == this || c.equals(o);} public int hashCode() {return c.hashCode();} public E peek() {return queue.peek();} public E poll() {return queue.poll();} public E remove() {return queue.remove();} public boolean offer(E e) {return queue.offer(typeCheck(e));} } /** * Returns a dynamically typesafe view of the specified set. * Any attempt to insert an element of the wrong type will result in * an immediate {@link ClassCastException}. Assuming a set contains * no incorrectly typed elements prior to the time a dynamically typesafe * view is generated, and that all subsequent access to the set * takes place through the view, it is guaranteed that the * set cannot contain an incorrectly typed element. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned set will be serializable if the specified set is * serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned set permits insertion of null elements whenever * the backing set does. * * @param the class of the objects in the set * @param s the set for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code s} is permitted to hold * @return a dynamically typesafe view of the specified set * @since 1.5 */ public static Set checkedSet(Set s, Class type) { return new CheckedSet<>(s, type); } /** * @serial include */ static class CheckedSet extends CheckedCollection implements Set, Serializable { private static final long serialVersionUID = 4694047833775013803L; CheckedSet(Set s, Class elementType) { super(s, elementType); } public boolean equals(Object o) { return o == this || c.equals(o); } public int hashCode() { return c.hashCode(); } } /** * Returns a dynamically typesafe view of the specified sorted set. * Any attempt to insert an element of the wrong type will result in an * immediate {@link ClassCastException}. Assuming a sorted set * contains no incorrectly typed elements prior to the time a * dynamically typesafe view is generated, and that all subsequent * access to the sorted set takes place through the view, it is * guaranteed that the sorted set cannot contain an incorrectly * typed element. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned sorted set will be serializable if the specified sorted * set is serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned sorted set permits insertion of null elements * whenever the backing sorted set does. * * @param the class of the objects in the set * @param s the sorted set for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code s} is permitted to hold * @return a dynamically typesafe view of the specified sorted set * @since 1.5 */ public static SortedSet checkedSortedSet(SortedSet s, Class type) { return new CheckedSortedSet<>(s, type); } /** * @serial include */ static class CheckedSortedSet extends CheckedSet implements SortedSet, Serializable { private static final long serialVersionUID = 1599911165492914959L; private final SortedSet ss; CheckedSortedSet(SortedSet s, Class type) { super(s, type); ss = s; } public Comparator comparator() { return ss.comparator(); } public E first() { return ss.first(); } public E last() { return ss.last(); } public SortedSet subSet(E fromElement, E toElement) { return checkedSortedSet(ss.subSet(fromElement,toElement), type); } public SortedSet headSet(E toElement) { return checkedSortedSet(ss.headSet(toElement), type); } public SortedSet tailSet(E fromElement) { return checkedSortedSet(ss.tailSet(fromElement), type); } } /** * Returns a dynamically typesafe view of the specified navigable set. * Any attempt to insert an element of the wrong type will result in an * immediate {@link ClassCastException}. Assuming a navigable set * contains no incorrectly typed elements prior to the time a * dynamically typesafe view is generated, and that all subsequent * access to the navigable set takes place through the view, it is * guaranteed that the navigable set cannot contain an incorrectly * typed element. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned navigable set will be serializable if the specified * navigable set is serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned navigable set permits insertion of null elements * whenever the backing sorted set does. * * @param the class of the objects in the set * @param s the navigable set for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code s} is permitted to hold * @return a dynamically typesafe view of the specified navigable set * @since 1.8 */ public static NavigableSet checkedNavigableSet(NavigableSet s, Class type) { return new CheckedNavigableSet<>(s, type); } /** * @serial include */ static class CheckedNavigableSet extends CheckedSortedSet implements NavigableSet, Serializable { private static final long serialVersionUID = -5429120189805438922L; private final NavigableSet ns; CheckedNavigableSet(NavigableSet s, Class type) { super(s, type); ns = s; } public E lower(E e) { return ns.lower(e); } public E floor(E e) { return ns.floor(e); } public E ceiling(E e) { return ns.ceiling(e); } public E higher(E e) { return ns.higher(e); } public E pollFirst() { return ns.pollFirst(); } public E pollLast() {return ns.pollLast(); } public NavigableSet descendingSet() { return checkedNavigableSet(ns.descendingSet(), type); } public Iterator descendingIterator() {return checkedNavigableSet(ns.descendingSet(), type).iterator(); } public NavigableSet subSet(E fromElement, E toElement) { return checkedNavigableSet(ns.subSet(fromElement, true, toElement, false), type); } public NavigableSet headSet(E toElement) { return checkedNavigableSet(ns.headSet(toElement, false), type); } public NavigableSet tailSet(E fromElement) { return checkedNavigableSet(ns.tailSet(fromElement, true), type); } public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type); } public NavigableSet headSet(E toElement, boolean inclusive) { return checkedNavigableSet(ns.headSet(toElement, inclusive), type); } public NavigableSet tailSet(E fromElement, boolean inclusive) { return checkedNavigableSet(ns.tailSet(fromElement, inclusive), type); } } /** * Returns a dynamically typesafe view of the specified list. * Any attempt to insert an element of the wrong type will result in * an immediate {@link ClassCastException}. Assuming a list contains * no incorrectly typed elements prior to the time a dynamically typesafe * view is generated, and that all subsequent access to the list * takes place through the view, it is guaranteed that the * list cannot contain an incorrectly typed element. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned list will be serializable if the specified list * is serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned list permits insertion of null elements whenever * the backing list does. * * @param the class of the objects in the list * @param list the list for which a dynamically typesafe view is to be * returned * @param type the type of element that {@code list} is permitted to hold * @return a dynamically typesafe view of the specified list * @since 1.5 */ public static List checkedList(List list, Class type) { return (list instanceof RandomAccess ? new CheckedRandomAccessList<>(list, type) : new CheckedList<>(list, type)); } /** * @serial include */ static class CheckedList extends CheckedCollection implements List { private static final long serialVersionUID = 65247728283967356L; final List list; CheckedList(List list, Class 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 c) { return list.addAll(index, checkedCopyOf(c)); } public ListIterator listIterator() { return listIterator(0); } public ListIterator listIterator(final int index) { final ListIterator i = list.listIterator(index); return new ListIterator() { 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 action) { i.forEachRemaining(action); } }; } public List subList(int fromIndex, int toIndex) { return new CheckedList<>(list.subList(fromIndex, toIndex), type); } /** * {@inheritDoc} * * @throws ClassCastException if the class of an element returned by the * operator prevents it from being added to this collection. The * exception may be thrown after some elements of the list have * already been replaced. */ @Override public void replaceAll(UnaryOperator operator) { Objects.requireNonNull(operator); list.replaceAll(e -> typeCheck(operator.apply(e))); } @Override public void sort(Comparator c) { list.sort(c); } } /** * @serial include */ static class CheckedRandomAccessList extends CheckedList implements RandomAccess { private static final long serialVersionUID = 1638200125423088369L; CheckedRandomAccessList(List list, Class type) { super(list, type); } public List subList(int fromIndex, int toIndex) { return new CheckedRandomAccessList<>( list.subList(fromIndex, toIndex), type); } } /** * Returns a dynamically typesafe view of the specified map. * Any attempt to insert a mapping whose key or value have the wrong * type will result in an immediate {@link ClassCastException}. * Similarly, any attempt to modify the value currently associated with * a key will result in an immediate {@link ClassCastException}, * whether the modification is attempted directly through the map * itself, or through a {@link Map.Entry} instance obtained from the * map's {@link Map#entrySet() entry set} view. * *

Assuming a map contains no incorrectly typed keys or values * prior to the time a dynamically typesafe view is generated, and * that all subsequent access to the map takes place through the view * (or one of its collection views), it is guaranteed that the * map cannot contain an incorrectly typed key or value. * *

A discussion of the use of dynamically typesafe views may be * found in the documentation for the {@link #checkedCollection * checkedCollection} method. * *

The returned map will be serializable if the specified map is * serializable. * *

Since {@code null} is considered to be a value of any reference * type, the returned map permits insertion of null keys or values * whenever the backing map does. * * @param the class of the map keys * @param the class of the map values * @param m the map for which a dynamically typesafe view is to be * returned * @param keyType the type of key that {@code m} is permitted to hold * @param valueType the type of value that {@code m} is permitted to hold * @return a dynamically typesafe view of the specified map * @since 1.5 */ public static Map checkedMap(Map m, Class keyType, Class valueType) { return new CheckedMap<>(m, keyType, valueType); } /** * @serial include */ private static class CheckedMap implements Map, Serializable { private static final long serialVersionUID = 5742860141034234728L; private final Map m; final Class keyType; final Class valueType; private void typeCheck(Object key, Object value) { if (key != null && !keyType.isInstance(key)) throw new ClassCastException(badKeyMsg(key)); if (value != null && !valueType.isInstance(value)) throw new ClassCastException(badValueMsg(value)); } private BiFunction typeCheck( BiFunction func) { Objects.requireNonNull(func); return (k, v) -> { V newValue = func.apply(k, v); typeCheck(k, newValue); return newValue; }; } private String badKeyMsg(Object key) { return "Attempt to insert " + key.getClass() + " key into map with key type " + keyType; } private String badValueMsg(Object value) { return "Attempt to insert " + value.getClass() + " value into map with value type " + valueType; } CheckedMap(Map m, Class keyType, Class valueType) { this.m = Objects.requireNonNull(m); this.keyType = Objects.requireNonNull(keyType); this.valueType = Objects.requireNonNull(valueType); } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean containsKey(Object key) { return m.containsKey(key); } public boolean containsValue(Object v) { return m.containsValue(v); } public V get(Object key) { return m.get(key); } public V remove(Object key) { return m.remove(key); } public void clear() { m.clear(); } public Set keySet() { return m.keySet(); } public Collection values() { return m.values(); } public boolean equals(Object o) { return o == this || m.equals(o); } public int hashCode() { return m.hashCode(); } public String toString() { return m.toString(); } public V put(K key, V value) { typeCheck(key, value); return m.put(key, value); } @SuppressWarnings("unchecked") public void putAll(Map t) { // Satisfy the following goals: // - good diagnostics in case of type mismatch // - all-or-nothing semantics // - protection from malicious t // - correct behavior if t is a concurrent map Object[] entries = t.entrySet().toArray(); List> checked = new ArrayList<>(entries.length); for (Object o : entries) { Map.Entry e = (Map.Entry) o; Object k = e.getKey(); Object v = e.getValue(); typeCheck(k, v); checked.add( new AbstractMap.SimpleImmutableEntry<>((K)k, (V)v)); } for (Map.Entry e : checked) m.put(e.getKey(), e.getValue()); } private transient Set> entrySet; public Set> entrySet() { if (entrySet==null) entrySet = new CheckedEntrySet<>(m.entrySet(), valueType); return entrySet; } // Override default methods in Map @Override public void forEach(BiConsumer action) { m.forEach(action); } @Override public void replaceAll(BiFunction function) { m.replaceAll(typeCheck(function)); } @Override public V putIfAbsent(K key, V value) { typeCheck(key, value); return m.putIfAbsent(key, value); } @Override public boolean remove(Object key, Object value) { return m.remove(key, value); } @Override public boolean replace(K key, V oldValue, V newValue) { typeCheck(key, newValue); return m.replace(key, oldValue, newValue); } @Override public V replace(K key, V value) { typeCheck(key, value); return m.replace(key, value); } @Override public V computeIfAbsent(K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); return m.computeIfAbsent(key, k -> { V value = mappingFunction.apply(k); typeCheck(k, value); return value; }); } @Override public V computeIfPresent(K key, BiFunction remappingFunction) { return m.computeIfPresent(key, typeCheck(remappingFunction)); } @Override public V compute(K key, BiFunction remappingFunction) { return m.compute(key, typeCheck(remappingFunction)); } @Override public V merge(K key, V value, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); return m.merge(key, value, (v1, v2) -> { V newValue = remappingFunction.apply(v1, v2); typeCheck(null, newValue); return newValue; }); } /** * We need this class in addition to CheckedSet as Map.Entry permits * modification of the backing Map via the setValue operation. This * class is subtle: there are many possible attacks that must be * thwarted. * * @serial exclude */ static class CheckedEntrySet implements Set> { private final Set> s; private final Class valueType; CheckedEntrySet(Set> s, Class valueType) { this.s = s; this.valueType = valueType; } public int size() { return s.size(); } public boolean isEmpty() { return s.isEmpty(); } public String toString() { return s.toString(); } public int hashCode() { return s.hashCode(); } public void clear() { s.clear(); } public boolean add(Map.Entry e) { throw new UnsupportedOperationException(); } public boolean addAll(Collection> coll) { throw new UnsupportedOperationException(); } public Iterator> iterator() { final Iterator> i = s.iterator(); final Class valueType = this.valueType; return new Iterator>() { public boolean hasNext() { return i.hasNext(); } public void remove() { i.remove(); } public Map.Entry next() { return checkedEntry(i.next(), valueType); } }; } @SuppressWarnings("unchecked") public Object[] toArray() { Object[] source = s.toArray(); /* * Ensure that we don't get an ArrayStoreException even if * s.toArray returns an array of something other than Object */ Object[] dest = (CheckedEntry.class.isInstance( source.getClass().getComponentType()) ? source : new Object[source.length]); for (int i = 0; i < source.length; i++) dest[i] = checkedEntry((Map.Entry)source[i], valueType); return dest; } @SuppressWarnings("unchecked") public T[] toArray(T[] a) { // We don't pass a to s.toArray, to avoid window of // vulnerability wherein an unscrupulous multithreaded client // could get his hands on raw (unwrapped) Entries from s. T[] arr = s.toArray(a.length==0 ? a : Arrays.copyOf(a, 0)); for (int i=0; i)arr[i], valueType); if (arr.length > a.length) return arr; System.arraycopy(arr, 0, a, 0, arr.length); if (a.length > arr.length) a[arr.length] = null; return a; } /** * This method is overridden to protect the backing set against * an object with a nefarious equals function that senses * that the equality-candidate is Map.Entry and calls its * setValue method. */ public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry) o; return s.contains( (e instanceof CheckedEntry) ? e : checkedEntry(e, valueType)); } /** * The bulk collection methods are overridden to protect * against an unscrupulous collection whose contains(Object o) * method senses when o is a Map.Entry, and calls o.setValue. */ public boolean containsAll(Collection c) { for (Object o : c) if (!contains(o)) // Invokes safe contains() above return false; return true; } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; return s.remove(new AbstractMap.SimpleImmutableEntry <>((Map.Entry)o)); } public boolean removeAll(Collection c) { return batchRemove(c, false); } public boolean retainAll(Collection c) { return batchRemove(c, true); } private boolean batchRemove(Collection c, boolean complement) { Objects.requireNonNull(c); boolean modified = false; Iterator> it = iterator(); while (it.hasNext()) { if (c.contains(it.next()) != complement) { it.remove(); modified = true; } } return modified; } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Set)) return false; Set that = (Set) o; return that.size() == s.size() && containsAll(that); // Invokes safe containsAll() above } static CheckedEntry checkedEntry(Map.Entry e, Class valueType) { return new CheckedEntry<>(e, valueType); } /** * This "wrapper class" serves two purposes: it prevents * the client from modifying the backing Map, by short-circuiting * the setValue method, and it protects the backing Map against * an ill-behaved Map.Entry that attempts to modify another * Map.Entry when asked to perform an equality check. */ private static class CheckedEntry implements Map.Entry { private final Map.Entry e; private final Class valueType; CheckedEntry(Map.Entry e, Class valueType) { this.e = Objects.requireNonNull(e); this.valueType = Objects.requireNonNull(valueType); } public K getKey() { return e.getKey(); } public V getValue() { return e.getValue(); } public int hashCode() { return e.hashCode(); } public String toString() { return e.toString(); } public V setValue(V value) { if (value != null && !valueType.isInstance(value)) throw new ClassCastException(badValueMsg(value)); return e.setValue(value); } private String badValueMsg(Object value) { return "Attempt to insert " + value.getClass() + " value into map with value type " + valueType; } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map.Entry)) return false; return e.equals(new AbstractMap.SimpleImmutableEntry <>((Map.Entry)o)); } } } }

你可能感兴趣的:(JDK 工具类之 Collections 2)