Java集合框架官方教程(5):集合类的同步包装器/不可变包装器
2016-07-29 00:00
579 查看
Wrapper Implementations
Wrapper implementations delegate all their real work to a specified collection but add extra functionality on top of what this collection offers. For design pattern fans, this is an example of thedecorator pattern. Although it may seem a bit exotic, it's really pretty straightforward.These implementations are anonymous; rather than providing a public class, the library provides a static factory method.All these implementations are found in the
Collectionsclass, which consists solely of static methods.
Synchronization Wrappers
The synchronization wrappers add automatic synchronization (thread-safety) to an arbitrary collection. Each of the six core collection interfaces —Collection,
Set,
List,
Map,
SortedSet, and
SortedMap— has one static factory method.
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); public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s); public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);Each of these methods returns a synchronized (thread-safe)
Collectionbacked up by the specified collection. To guarantee serial access,
all access to the backing collection must be accomplished through the returned collection. The easy way to guarantee this is not to keep a reference to the backing collection. Create the synchronized collection with the following trick.
List<Type> list =Collections.synchronizedList(new ArrayList<Type>());
A collection created in this fashion is every bit as thread-safe as a normally synchronized collection, such as a
Vector.
In the face of concurrent access, it is imperative that the user manually synchronize on the returned collection when iterating over it. The reason is that iteration is accomplished via multiple calls into the collection, which must be composed into a single atomic operation. The following is the idiom to iterate over a wrapper-synchronized collection.
Collection<Type> c =Collections.synchronizedCollection(myCollection);If an explicit iterator is used, the
synchronized(c) {
for (Type e : c)
foo(e);
}
iteratormethod must be called from within the
synchronizedblock. Failure to follow this advice may result in nondeterministic behavior. The idiom for iterating over a
Collectionview of a synchronized
Mapis similar.
It is imperative that the user synchronize on the synchronized
Mapwhen iterating over any of its
Collectionviews rather than synchronizing on the
Collectionview itself, as shown in the following example.
Map<KeyType, ValType> m =Collections.synchronizedMap(new HashMap<KeyType, ValType>());
...
Set<KeyType> s = m.keySet();
...
// Synchronizing on m, not s!
synchronized(m) {
while (KeyType k : s)
foo(k);
}
One minor downside of using wrapper implementations is that you do not have the ability to execute anynoninterface operations of a wrapped implementation. So, for instance, in the preceding
Listexample, you cannot call
ArrayList's
ensureCapacityoperation on the wrapped
ArrayList.
Unmodifiable Wrappers
Unlike synchronization wrappers, which add functionality to the wrapped collection, the unmodifiable wrappers take functionality away. In particular, they take away the ability to modify the collection by intercepting all the operations that would modify the collection and throwing anUnsupportedOperationException. Unmodifiable wrappers have two main uses, as follows:
To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability.
To allow certain clients read-only access to your data structures. You keep a reference to the backing collection but hand out a reference to the wrapper. In this way, clients can look but not modify, while you maintain full access.
Like synchronization wrappers, each of the six core
Collectioninterfaces has one static factory method.
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);
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s);
public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);
Checked Interface Wrappers
TheCollections.checkedinterface wrappers are provided for use with generic collections. These implementations return adynamically type-safe view of the specified collection, which throws a
ClassCastExceptionif a client attempts to add an element of the wrong type. The generics mechanism in the language provides compile-time (static) type-checking, but it is possible to defeat this mechanism. Dynamically type-safe views eliminate this possibility entirely.
Convenience Implementations
This section describes several mini-implementations that can be more convenient and more efficient than general-purpose implementations when you don't need their full power.All the implementations in this section are made available via static factory methods rather thanpublicclasses.
List View of an Array
TheArrays.asListmethod returns a
Listview of its array argument. Changes to the
Listwrite through to the array and vice versa.The size of the collection is that of the array and cannot be changed. If the
addor the
removemethod is called on the
List, an
UnsupportedOperationExceptionwill result.
The normal use of this implementation is as a bridge between array-based and collection-based APIs. It allows you to pass an array to a method expecting a
Collectionor a
List. However, this implementation also has another use. If you need a fixed-size
List, it's more efficient than any general-purpose
Listimplementation. This is the idiom.
List<String> list = Arrays.asList(new String[size]);
Note that a reference to the backing array is not retained.
Immutable Multiple-Copy List
Occasionally you'll need an immutableListconsisting of multiple copies of the same element. The
Collections.nCopiesmethod returns such a list. This implementation has two main uses.The first is to initialize a newly created
List; for example, suppose you want an
ArrayListinitially consisting of 1,000
nullelements. The following incantation does the trick.
List<Type> list = new ArrayList<Type>(Collections.nCopies(1000, (Type)null);Of course, the initial value of each element need not be
null.
The second main use is to grow an existing
List. For example, suppose you want to add 69 copies of the string
"fruit bat"to the end of a
List<String>. It's not clear why you'd want to do such a thing, but let's just suppose you did. The following is how you'd do it.
lovablePets.addAll(Collections.nCopies(69, "fruit bat"));
By using the form of
addAllthat takes both an index and a
Collection, you can add the new elements to the middle of a
Listinstead of to the end of it.
Immutable Singleton Set
Sometimes you'll need an immutable singletonSet, which consists of a single, specified element. The
Collections.singletonmethod returns such a
Set. One use of this implementation is to remove all occurrences of a specified element from a
Collection.
c.removeAll(Collections.singleton(e));A related idiom removes all elements that map to a specified value from a
Map. For example, suppose you have a
Map—
job— that maps people to their line of work and suppose you want to eliminate all the lawyers. The following one-liner will do the deed.
job.values().removeAll(Collections.singleton(LAWYER));
One more use of this implementation is to provide a single input value to a method that is written to accept a collection of values.
Empty Set, List, and Map Constants
TheCollectionsclass provides methods to return the empty
Set,
List, and
Map—
emptySet,
emptyList, and
emptyMap. The main use of these constants is as input to methods that take a
Collectionof values when you don't want to provide any values at all, as in this example.
tourist.declarePurchases(Collections.emptySet());
Summary of Implementations
Implementations are the data objects used to store collections, which implement the interfaces described in theInterfaces lesson.The JavaCollections Framework provides several general-purpose implementations of the core interfaces:
For the
Setinterface,
HashSetis the most commonly used implementation.
For the
Listinterface,
ArrayListis the most commonly used implementation.
For the
Mapinterface,
HashMapis the most commonly used implementation.
For the
Queueinterface,
LinkedListis the most commonly used implementation.
For the
Dequeinterface,
ArrayDequeis the most commonly used implementation.
Each of the general-purpose implementations provides all optional operations contained in its interface.
The JavaCollections Framework also provides several special-purpose implementations for situations that require nonstandard performance, usage restrictions, or other unusual behavior.
The
java.util.concurrentpackage contains several collections implementations, which are thread-safe but not governed by a single exclusion lock.
The
Collectionsclass (as opposed to the
Collectioninterface), provides static methods that operate on or return collections, which are known as Wrapper implementations.
Finally, there are several Convenience implementations, which can be more efficient than general-purpose implementations when you don't need their full power. The Convenience implementations are made available through static factory methods.
Answers to Questions and Exercises:
Questions
Question: You plan to write a program that uses several basic collection interfaces:Set,
List,
Queue, and
Map. You're not sure which implementations will work best, so you decide to use general-purpose implementations until you get a better idea how your program will work in the real world. Which implementations are these?
Answer:
Set:
HashSet
List:
ArrayList
Queue:
LinkedList
Map:
HashMap
Question: If you need a
Setimplementation that provides value-ordered iteration, which class should you use?
Answer:
TreeSetguarantees that the sorted set is in ascending element order, sorted according to the natural order of the elements or by the
Comparatorprovided.
Question: Which class do you use to access wrapper implementations?
Answer:
You use the
Collectionsclass, which provides static methods that operate on or return collections.
Exercises
Exercise: Write a program that reads a text file, specified by the first command line argument, into aList. The program should then print random lines from the file, the number of lines printed to be specified by the second command line argument. Write the program so that a correctly-sized collection is allocated all at once, instead of being gradually expanded as the file is read in. Hint: To determine the number of lines in the file, use
java.io.File.lengthto obtain the size of the file, then divide by an assumed size of an average line.
Answer:
Since we are accessing the
Listrandomly, we will use
ArrayList. We estimate the number of lines by taking the file size and dividing by 50. We then double that figure, since it is more efficient to overestimate than to underestmate.
import java.util.*;This program actually spends most of its time reading in the file, so pre-allocating the
import java.io.*;
public class FileList {
public static void main(String[] args) {
final int assumedLineLength = 50;
File file = new File(args[0]);
List<String> fileList =
new ArrayList<String>((int)(file.length() / assumedLineLength) * 2);
BufferedReader reader =null;
int lineCount = 0;
try {
reader = new BufferedReader(new FileReader(file));
for (String line = reader.readLine(); line !=null;
line = reader.readLine()) {
fileList.add(line);
lineCount++;
}
} catch (IOException e) {
System.err.format("Could not read %s: %s%n", file, e);
System.exit(1);
} finally {
if (reader !=null) {
try {
reader.close();
} catch (IOException e) {}
}
}
int repeats = Integer.parseInt(args[1]);
Random random = new Random();
for (int i = 0; i < repeats; i++) {
System.out.format("%d: %s%n", i,
fileList.get(random.nextInt(lineCount - 1)));
}
}
}
ArrayListhas little affect on its performance. Specifying an initial capacity in advance is more likely to be useful when your program repeatly creates large
ArrayListobjects without intervening I/O.
Original:
http://docs.oracle.com/javase/tutorial/collections/implementations/wrapper.html
相关文章推荐
- 深入理解JavaBean(1):JavaBean的内省与BeanUtils库
- 深入理解JavaBean(1):JavaBean的内省与BeanUtils库
- Mockito:一个强大的用于Java开发的模拟测试框架
- Java垃圾回收精粹
- 深入理解Java反射:候捷谈Java反射机制
- 深入理解JavaBean(2):属性编辑器PropertyEditor
- 第1部分:Spring框架概述
- 成为Java GC专家(5):Java应用性能调优的原则
- 深入理解Java类加载器(2):线程上下文类加载器
- 面向GC的Java编程
- Mockito:一个强大的用于Java开发的模拟测试框架
- Java垃圾回收精粹
- 深入理解Java反射:候捷谈Java反射机制
- 深入理解JavaBean(2):属性编辑器PropertyEditor
- 第1部分:Spring框架概述
- Java动态代理与Cglib库
- 成为Java GC专家(5):Java应用性能调优的原则
- Java中的泛型
- 深入理解Java类加载器(2):线程上下文类加载器
- 面向GC的Java编程