Java集合框架官方教程(4):Set/List/Map/Queue/Deque实现
2016-07-29 00:00
531 查看
Lesson: Implementations
Implementations are the data objects used to store collections, which implement the interfaces described inthe Interfaces section. This lesson describes the following kinds of implementations:General-purpose implementations are the most commonly used implementations, designed for everyday use. They are summarized in the table titled General-purpose-implementations.Special-purpose implementations are designed for use in special situations and display nonstandard performance characteristics, usage restrictions, or behavior.Concurrent implementations are designed to support high concurrency, typically at the expense of single-threaded performance. These implementations are part of thejava.util.concurrentpackage.Wrapper implementations are used in combination with other types of implementations, often the general-purpose ones, to provide added or restricted functionality.Convenience implementations are mini-implementations, typically made available via static factory methods, that provide convenient, efficient alternatives to general-purpose implementations for special collections (for example, singleton sets).Abstract implementations are skeletal implementations that facilitate the construction of custom implementations — described later in theCustom Collection Implementations section. An advanced topic, it's not particularly difficult, but relatively few people will need to do it.The general-purpose implementations are summarized in the following table.General-purpose Implementations
Interfaces | Hash table Implementations | Resizable array Implementations | Tree Implementations | Linked list Implementations | Hash table + Linked list Implementations |
---|---|---|---|---|---|
Set | HashSet | TreeSet | LinkedHashSet | ||
List | ArrayList | LinkedList | |||
Queue | |||||
Deque | ArrayDeque | LinkedList | |||
Map | HashMap | TreeMap | LinkedHashMap |
Set,
List, and
Mapinterfaces. In each case, one implementation —
HashSet,
ArrayList, and
HashMap— is clearly the one to use for most applications, all other things being equal. Note that the
SortedSetand the
SortedMapinterfaces do not have rows in the table. Each of those interfaces has one implementation(
TreeSetand
TreeMap) and is listed in the
Setand the
Maprows. There are two general-purpose
Queueimplementations —
LinkedList, which is also a
Listimplementation, and
PriorityQueue, which is omitted from the table. These two implementations provide very different semantics:
LinkedListprovides FIFO semantics, while
PriorityQueueorders its elements according to their values.Each of the general-purpose implementations provides all optional operations contained in its interface. All permit
nullelements, keys, and values. None are synchronized (thread-safe). All havefail-fast iterators, which detect illegal concurrent modification during iteration and fail quickly and cleanly rather than risking arbitrary, nondeterministic behavior at an undetermined time in the future.All are
Serializableand all support a public
clonemethod.The fact that these implementations are unsynchronized represents a break with the past: The legacy collections
Vectorand
Hashtableare synchronized. The present approach was taken because collections are frequently used when the synchronization is of no benefit. Such uses include single-threaded use, read-only use, and use as part of a larger data object that does its own synchronization. In general, it is good API design practice not to make users pay for a feature they don't use. Furthermore, unnecessary synchronization can result in deadlock under certain circumstances.If you need thread-safe collections, the synchronization wrappers, described in theWrapper Implementations section, allow any collection to be transformed into a synchronized collection. Thus, synchronization is optional for general-purpose implementations, whereas it is mandatory for legacy implementations. Moreover, the
java.util.concurrentpackage provides concurrent implementations of the
BlockingQueueinterface, which extends
Queue, and of the
ConcurrentMapinterface, which extends
Map. These implementations offer much higher concurrency than mere synchronized implementations.As a rule, you should be thinking about the interfaces, not the implementations. That is why there are no programming examples in this section. For the most part, the choice of implementation affects only performance. The preferred style, as mentioned in the Interfaces section, is to choose an implementation when a
Collectionis created and to immediately assign the new collection to a variable of the corresponding interface type (or to pass the collection to a method expecting an argument of the interface type). In this way, the program does not become dependent on any added methods in a given implementation, leaving the programmer free to change implementations anytime that it is warranted by performance concerns or behavioral details.The sections that follow briefly discuss the implementations. The performance of the implementations is described using words such asconstant-time,log, linear, n log(n), and quadratic to refer to the asymptotic upper-bound on the time complexity of performing the operation. All this is quite a mouthful, and it doesn't matter much if you don't know what it means. If you're interested in knowing more, refer to any good algorithms textbook. One thing to keep in mind is that this sort of performance metric has its limitations. Sometimes, the nominally slower implementation may be faster. When in doubt, measure the performance!
Set Implementations
TheSetimplementations are grouped into general-purpose and special-purpose implementations.
General-Purpose Set Implementations
There are three general-purposeSetimplementations —
HashSet,
TreeSet, and
LinkedHashSet. Which of these three to use is generally straightforward.
HashSetis much faster than
TreeSet(constant-time versus log-time for most operations) but offers no ordering guarantees. If you need to use the operations in the
SortedSetinterface, or if value-ordered iteration is required, use
TreeSet; otherwise, use
HashSet. It's a fair bet that you'll end up using
HashSetmost of the time.
LinkedHashSetis in some sense intermediate between
HashSetand
TreeSet. Implemented as a hash table with a linked list running through it, it providesinsertion-ordered iteration (least recently inserted to most recently) and runs nearly as fast as
HashSet. The
LinkedHashSetimplementation spares its clients from the unspecified, generally chaotic ordering provided by
HashSetwithout incurring the increased cost associated with
TreeSet.One thing worth keeping in mind about
HashSetis that iteration is linear in the sum of the number of entries and the number of buckets (thecapacity). Thus, choosing an initial capacity that's too high can waste both space and time. On the other hand, choosing an initial capacity that's too low wastes time by copying the data structure each time it's forced to increase its capacity. If you don't specify an initial capacity, the default is 16. In the past, there was some advantage to choosing a prime number as the initial capacity. This is no longer true.Internally, the capacity is always rounded up to a power of two. The initial capacity is specified by using the
intconstructor. The following line of code allocates a
HashSetwhose initial capacity is 64.
Set<String> s = newHashSet<String>(64);The
HashSetclass has one other tuning parameter called the load factor. If you care a lot about the space consumption of your
HashSet, read the
HashSetdocumentation for more information. Otherwise, just accept the default; it's almost always the right thing to do.If you accept the default load factor but want to specify an initial capacity, pick a number that's about twice the size to which you expect the set to grow. If your guess is way off, you may waste a bit of space, time, or both, but it's unlikely to be a big problem.
LinkedHashSethas the same tuning parameters as
HashSet, but iteration time is not affected by capacity.
TreeSethas no tuning parameters.
Special-Purpose Set Implementations
There are two special-purposeSetimplementations —
EnumSetand
CopyOnWriteArraySet.
EnumSetis a high-performance
Setimplementation for enum types. All of the members of an enum set must be of the same enum type. Internally, it is represented by a bit-vector, typically a single
long. Enum sets support iteration over ranges of enum types. For example, given the enum declaration for the days of the week, you can iterate over the weekdays. The
EnumSetclass provides a static factory that makes it easy.
for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY)) System.out.println(d);Enum sets also provide a rich, typesafe replacement for traditional bit flags.
EnumSet.of(Style.BOLD, Style.ITALIC)
CopyOnWriteArraySetis a
Setimplementation backed up by a copy-on-write array. All mutative operations, such as
add,
set, and
remove, are implemented by making a new copy of the array; no locking is ever required. Even iteration may safely proceed concurrently with element insertion and deletion. Unlike most
Setimplementations, the
add,
remove, and
containsmethods require time proportional to the size of the set.This implementation is only appropriate for sets that are rarely modified but frequently iterated. It is well suited to maintaining event-handler lists that must prevent duplicates.
List Implementations
Listimplementations are grouped into general-purpose and special-purpose implementations.
General-Purpose List Implementations
There are two general-purposeListimplementations —
ArrayListand
LinkedList. Most of the time, you'll probably use
ArrayList, which offers constant-time positional access and is just plain fast. It does not have to allocate a node object for each element in the
List, and it can take advantage of
System.arraycopywhen it has to move multiple elements at the same time. Think of
ArrayListas
Vectorwithout the synchronization overhead.If you frequently add elements to the beginning of the
Listor iterate over the
Listto delete elements from its interior, you should consider using
LinkedList. These operations require constant-time in a
LinkedListand linear-time in an
ArrayList. But you pay a big price in performance. Positional access requires linear-time in a
LinkedListand constant-time in an
ArrayList. Furthermore, the constant factor for
LinkedListis much worse. If you think you want to use a
LinkedList, measure the performance of your application with both
LinkedListand
ArrayListbefore making your choice;
ArrayListis usually faster.
ArrayListhas one tuning parameter — the initial capacity, which refers to the number of elements the
ArrayListcan hold before it has to grow.
LinkedListhas no tuning parameters and seven optional operations, one of which is
clone. The other six are
addFirst,
getFirst,
removeFirst,
addLast,
getLast, and
removeLast.
LinkedListalso implements the
Queueinterface.
Special-Purpose List Implementations
CopyOnWriteArrayListis a
Listimplementation backed up by a copy-on-write array. This implementation is similar in nature to
CopyOnWriteArraySet.No synchronization is necessary, even during iteration, and iterators are guaranteed never to throw
ConcurrentModificationException. This implementation is well suited to maintaining event-handler lists, in which change is infrequent, and traversal is frequent and potentially time-consuming.If you need synchronization, a
Vectorwill be slightly faster than an
ArrayListsynchronized with
Collections.synchronizedList. But
Vectorhas loads of legacy operations, so be careful to always manipulate the
Vectorwith the
Listinterface or else you won't be able to replace the implementation at a later time.If your
Listis fixed in size — that is, you'll never use
remove,
add, or any of the bulk operations other than
containsAll— you have a third option that's definitely worth considering. See
Arrays.asListin theConvenience Implementations section for more information.
Map Implementations
Mapimplementations are grouped into general-purpose, special-purpose, and concurrent implementations.
General-Purpose Map Implementations
The three general-purposeMapimplementations are
HashMap,
TreeMapand
LinkedHashMap. If you need
SortedMapoperations or key-ordered
Collection-view iteration, use
TreeMap; if you want maximum speed and don't care about iteration order, use
HashMap; if you want near-
HashMapperformance and insertion-order iteration, use
LinkedHashMap. In this respect, the situation for
Mapis analogous to
Set. Likewise, everything else in theSet Implementations section also applies to
Mapimplementations.
LinkedHashMapprovides two capabilities that are not available with
LinkedHashSet. When you create a
LinkedHashMap, you can order it based on key access rather than insertion. In other words, merely looking up the value associated with a key brings that key to the end of the map. Also,
LinkedHashMapprovides the
removeEldestEntrymethod, which may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map. This makes it very easy to implement a custom cache.For example, this override will allow the map to grow up to as many as 100 entries and then it will delete the eldest entry each time a new entry is added, maintaining a steady state of 100 entries.
private static final int MAX_ENTRIES = 100;protected booleanremoveEldestEntry(Map.Entry eldest) {return size() > MAX_ENTRIES;}
Special-Purpose Map Implementations
There are three special-purpose Map implementations —EnumMap,
WeakHashMapand
IdentityHashMap.
EnumMap, which is internally implemented as an
array, is a high-performance
Mapimplementation for use with enum keys. This implementation combines the richness and safety of the
Mapinterface with a speed approaching that of an array.If you want to map an enum to a value, you should always use an
EnumMapin preference to an array.
WeakHashMapis an implementation of the
Mapinterface that stores only weak references to its keys. Storing only weak references allows a key-value pair to be garbage-collected when its key is no longer referenced outside of the
WeakHashMap. This class provides the easiest way to harness the power of weak references.It is useful for implementing "registry-like" data structures, where the utility of an entry vanishes when its key is no longer reachable by any thread.
IdentityHashMapis an identity-based
Mapimplementation based on a hash table.This class is useful for topology-preserving object graph transformations, such as serialization or deep-copying. To perform such transformations, you need to maintain an identity-based "node table" that keeps track of which objects have already been seen.Identity-based maps are also used to maintain object-to-meta-information mappings in dynamic debuggers and similar systems. Finally, identity-based maps are useful in thwarting "spoof attacks" that are a result of intentionally perverse
equalsmethods because
IdentityHashMapnever invokes the
equalsmethod on its keys. An added benefit of this implementation is that it is fast.
Concurrent Map Implementations
Thejava.util.concurrentpackage contains the
ConcurrentMapinterface, which extends
Mapwith atomic
putIfAbsent,
remove, and
replacemethods, and the
ConcurrentHashMapimplementation of that interface.
ConcurrentHashMapis a highly concurrent, high-performance implementation backed up by a hash table.This implementation never blocks when performing retrievals and allows the client to select the concurrency level for updates. It is intended as a drop-in replacement for
Hashtable: in addition to implementing
ConcurrentMap, it supports all the legacy methods peculiar to
Hashtable. Again, if you don't need the legacy operations, be careful to manipulate it with the
ConcurrentMapinterface.
Queue Implementations
TheQueueimplementations are grouped into general-purpose and concurrent implementations.
General-Purpose Queue Implementations
As mentioned in the previous section,LinkedListimplements the
Queueinterface, providing first in, first out (FIFO) queue operations for
add,
poll, and so on.The
PriorityQueueclass is a priority queue based on the heap data structure.This queue orders elements according to the order specified at construction time, which can be the elements' natural ordering or the ordering imposed by an explicit
Comparator.The queue retrieval operations —
poll,
remove,
peek, and
element— access the element at the head of the queue. Thehead of the queue is the least element with respect to the specified ordering. If multiple elements are tied for least value, the head is one of those elements; ties are broken arbitrarily.
PriorityQueueand its iterator implement all of the optional methods of the
Collectionand
Iteratorinterfaces. The iterator provided in method
iteratoris not guaranteed to traverse the elements of the
PriorityQueuein any particular order. For ordered traversal, consider using
Arrays.sort(pq.toArray()).
Concurrent Queue Implementations
Thejava.util.concurrentpackage contains a set of synchronized
Queueinterfaces and classes.
BlockingQueueextends
Queuewith operations that wait for the queue to become nonempty when retrieving an element and for space to become available in the queue when storing an element. This interface is implemented by the following classes:
LinkedBlockingQueue— an optionally bounded FIFO blocking queue backed by linked nodes
ArrayBlockingQueue— a bounded FIFO blocking queue backed by an array
PriorityBlockingQueue— an unbounded blocking priority queue backed by a heap
DelayQueue— a time-based scheduling queue backed by a heap
SynchronousQueue— a simple rendezvous mechanism that uses the
BlockingQueueinterfaceIn JDK 7,
TransferQueueis a specialized
BlockingQueuein which code that adds an element to the queue has the option of waiting (blocking) for code in another thread to retrieve the element.
TransferQueuehas a single implementation:
LinkedTransferQueue— an unbounded
TransferQueuebased on linked nodes
Deque Implementations
TheDequeinterface, pronounced as "deck", represents a double-ended queue. The
Dequeinterface can be implemented as various types of
Collections. The
Dequeinterface implementations are grouped into general-purpose and concurrent implementations.
General-Purpose Deque Implementations
The general-purpose implementations includeLinkedListand
ArrayDequeclasses. The
Dequeinterface supports insertion, removal and retrieval of elements at both ends. The
ArrayDequeclass is the resizable array implementation of the
Dequeinterface, whereas the
LinkedListclass is the list implementation.The basic insertion, removal and retieval operations in the
Dequeinterface
addFirst,
addLast,
removeFirst,
removeLast,
getFirstand
getLast. The method
addFirstadds an element at the head whereas
addLastadds an element at the tail of the
Dequeinstance.The
LinkedListimplementation is more flexible than the
ArrayDequeimplementation.
LinkedListimplements all optional list operations.
nullelements are allowed in the
LinkedListimplementation but not in the
ArrayDequeimplementation. In terms of efficiency,
ArrayDequeis more efficient than the
LinkedListfor add andremove operation at both ends. The best operation in a
LinkedListimplementation is removing the current element during the iteration.
LinkedListimplementations are not ideal structures to iterate.The
LinkedListimplementation consumes more memory than the
ArrayDequeimplementation. For the
ArrayDequeinstance traversal use any of the following:
foreach
Theforeachis fast and can be used for all kinds of lists.
ArrayDeque<String> aDeque = newArrayDeque<String>();. . .for (String str : aDeque) {System.out.println(str);}
Iterator
TheIteratorcan be used for the forward traversal on all kinds of lists for all kinds of data.
ArrayDeque<String> aDeque = newArrayDeque<String>();. . .for (Iterator<String> iter = aDeque.iterator(); iter.hasNext(); ) {System.out.println(iter.next());}The
ArrayDequeclass is used in this tutorial to implement the
Dequeinterface. The complete code of the example used in this tutorial is available in
ArrayDequeSample.Both the
LinkedListand
ArrayDequeclasses do not support concurrent access by multiple threads.
Concurrent Deque Implementations
TheLinkedBlockingDequeclass is the concurrent implementation of the
Dequeinterface. If the deque is empty then methods such as
takeFirstand
takeLastwait until the element becomes available, and then retrieves andremoves the same element.Original: http://docs.oracle.com/javase/tutorial/collections/implementations/index.html
相关文章推荐
- 混沌数学之Chua's circuit(蔡氏电路)
- 奇怪吸引子---WimolBanlue
- 奇怪吸引子---WimolBanlue
- 数学图形之Kuen Surface
- 数学图形之Kuen Surface
- 使用addScala将SQLQuery自定义查询映射到pojo中
- svn 指令整理 附 ->Svn Quick Reference Card<- pdf
- 解决CVS This compilation unit is not on the build 问题
- 使用OOP的方式扩展ExtJS UI组建 - 更新版
- 使用KeyNav为UI Component添加键盘事件
- GXT中TextArea的getSelectedTex()方法在IE下报错的解决方案。
- 使用addScala将SQLQuery自定义查询映射到pojo中
- svn 指令整理 附 ->Svn Quick Reference Card<- pdf
- 解决CVS This compilation unit is not on the build 问题
- 使用OOP的方式扩展ExtJS UI组建 - 更新版
- 使用KeyNav为UI Component添加键盘事件
- GXT中TextArea的getSelectedTex()方法在IE下报错的解决方案。
- Druid 监控配置
- select value check
- 解决Invalid derived query! No property found。。。