Java集合框架官方教程(5):集合类的同步包装器/不可变包装器


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 theCollections class, 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) Collection backed 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 aVector.

    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);
synchronized(c) {
    for (Type e : c)
        foo(e);
}
    If an explicit iterator is used, the iterator method must be called from within the synchronized block. Failure to follow this advice may result in nondeterministic behavior. The idiom for iterating over a Collection view of a synchronized Map is similar. It is imperative that the user synchronize on the synchronizedMap when iterating over any of itsCollection views rather than synchronizing on theCollection view 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 precedingList example, you cannot callArrayList's ensureCapacity operation 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 an UnsupportedOperationException. 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 Collection interfaces 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

    The Collections.checked interface wrappers are provided for use with generic collections. These implementations return adynamically type-safe view of the specified collection, which throws aClassCastException if 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 thanpublic classes.

List View of an Array

    The Arrays.asList method returns a List view of its array argument. Changes to theList write through to the array and vice versa. The size of the collection is that of the array and cannot be changed. If theadd or the remove method is called on the List, anUnsupportedOperationException will 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 aCollection or a List. However, this implementation also has another use. If you need a fixed-sizeList, it's more efficient than any general-purpose List implementation. 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 immutable List consisting of multiple copies of the same element. TheCollections.nCopies method 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 ArrayList initially consisting of 1,000null elements. 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 existingList. 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 addAll that takes both an index and a Collection, you can add the new elements to the middle of a List instead of to the end of it.

Immutable Singleton Set

    Sometimes you'll need an immutable singleton Set, which consists of a single, specified element. TheCollections.singleton method returns such a Set. One use of this implementation is to remove all occurrences of a specified element from aCollection.

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 Mapjob — 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

    The Collections class 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 aCollection of 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 Java Collections Framework provides several general-purpose implementations of the core interfaces:

  • For the Set interface, HashSet is the most commonly used implementation.
  • For the List interface, ArrayList is the most commonly used implementation.
  • For the Map interface, HashMap is the most commonly used implementation.
  • For the Queue interface, LinkedList is the most commonly used implementation.
  • For the Deque interface, ArrayDeque is the most commonly used implementation.

    Each of the general-purpose implementations provides all optional operations contained in its interface.

    The Java Collections Framework also provides several special-purpose implementations for situations that require nonstandard performance, usage restrictions, or other unusual behavior.

    The java.util.concurrent package contains several collections implementations, which are thread-safe but not governed by a single exclusion lock.

    The Collections class (as opposed to the Collection interface), 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

  1. 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
  2. Question: If you need a Set implementation that provides value-ordered iteration, which class should you use?
    Answer:
    TreeSet guarantees that the sorted set is in ascending element order, sorted according to the natural order of the elements or by theComparator provided.
  3. Question: Which class do you use to access wrapper implementations?
    Answer:
    You use the Collections class, 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 a List. 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.length to obtain the size of the file, then divide by an assumed size of an average line.
     Answer:
     Since we are accessing the List randomly, 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.*;
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)));
        }
    }
}
    This program actually spends most of its time reading in the file, so pre-allocating the ArrayList has little affect on its performance. Specifying an initial capacity in advance is more likely to be useful when your program repeatly creates large ArrayList objects without intervening I/O.

Original: http://docs.oracle.com/javase/tutorial/collections/implementations/wrapper.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java集合框架中的各种类可以根据它们的特点进行比较,以下是一些常见的比较: 1. List和Set List和Set都是集合接口的实现类,但它们有一些区别。List是有序的(元素按照添加的顺序排列),并且允许重复元素。Set是无序的,并且不允许重复元素。在需要保证元素唯一性的情况下,应该使用Set;在需要保留元素顺序或允许重复元素的情况下,应该使用List。 2. HashMap和TreeMap HashMap和TreeMap都是Map接口的实现类,但它们的实现方式不同。HashMap使用哈希表来存储键值对,因此它的元素是无序的。TreeMap使用红黑树来存储键值对,因此它的元素是有序的。在需要快速查找元素的情况下,应该使用HashMap;在需要有序遍历元素的情况下,应该使用TreeMap。 3. ArrayList和LinkedList ArrayList和LinkedList都是List接口的实现类,但它们的实现方式不同。ArrayList使用可数组来存储元素,因此它的访问速度比LinkedList快。LinkedList使用双向链表来存储元素,因此它的插入和删除操作比ArrayList快。在需要快速访问元素的情况下,应该使用ArrayList;在需要频繁插入和删除元素的情况下,应该使用LinkedList。 4. HashSet和TreeSet HashSet和TreeSet都是Set接口的实现类,但它们的实现方式不同。HashSet使用哈希表来存储元素,因此它的元素是无序的。TreeSet使用红黑树来存储元素,因此它的元素是有序的。在需要快速查找元素的情况下,应该使用HashSet;在需要有序遍历元素的情况下,应该使用TreeSet。 5. ConcurrentHashMap和Hashtable ConcurrentHashMap和Hashtable都是Map接口的实现类,但它们的实现方式不同。ConcurrentHashMap使用分段锁来实现并发访问,因此它的并发性能比Hashtable更好。Hashtable使用同步来实现并发访问,因此它的并发性能比ConcurrentHashMap差。在需要高并发性能的情况下,应该使用ConcurrentHashMap;在不需要高并发性能的情况下,可以使用Hashtable。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值