《Java Generics and Collections》笔记-Lists/Maps

本文详细探讨了Java中的Lists和Maps实现,包括LinkedList、CopyOnWriteArrayList、HashMap、LinkedHashMap、ConcurrentHashMap等,分析了不同实现的性能比较,并重点介绍了并发集合的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Lists

前面主要是在介绍用法,省去不写。
List<E> subList(int fromIndex, int toIndex)// return a view of a portion of the list
The returned list has no separate existence. it is just a view of part of the list from which it was obtained,so changes in it are reflected in the original list. There is an important difference from subSet, though; changes you make to the sublist write through to the backing list,but the reverse is not always true. If elements have been inserted into or removed from the backing list by directly calling one of its "structure changing" methods (Section 12.1), any subsequent attempts to use the sublist will result in a ConcurrentModificationException.
ListIterator positions
The current position of a ListIterator always lies between two elements, so in a list of length n, there are n+1 valid list iterator positions, from 0 (before the first element) to n (after the last one).
如果当前在位置0处,previousIndex返回-1,nextIndex返回n。而它的previous和next方法,抛出NoSuchElementException。

Implementing List
The performance of ArrayList reflects array performance for "random-access" operations: set and get take constant time. The downside of an array implementation is in inserting or removing elements at arbitrary positions, because that may require adjusting the position of other elements. (We have already met this problem with the remove method of the iterators of array-based queues for example, ArrayBlockingQueue (see Section 14.3.2). But the performance of positional add and remove methods are much more important for lists than iterator.remove is for queues.)

LinkedList

CopyOnWriteArrayList

In Section 13.1 we met CopyOnWriteArraySet, a set implementation designed toprovide thread safety together with very fast read access. CopyOnWriteArrayList is a List implementation with the same design aims. This combination of thread safety with fast read access is useful in some concurrent programs, especially when a collection of observer objects needs to receive frequent event notifications. The cost is that the array which backs the collection has to be treated as immutable, so a new copy is created whenever any changes are made to the collection.This cost may not be too high to pay if changes in the set of observers occur only rarely.

The class CopyOnWriteArraySet in fact delegates all of its operations to an instance of CopyOnWriteArrayList, taking advantage of the atomic operations addIfAbsent and addAllAbsent provided by the latter to enable the Set methods add and addAll to avoid introducing duplicates to the set. In addition to the two standard constructors (see Section 12.3), CopyOnWriteArrayList has an extra one that allows it to be created using the elements of a supplied array as its initial contents. Its iterators are snapshot iterators, reflecting the state of the list at the time of their creation.

Comparing List Implementations


For most list applications the choice is between ArrayList and LinkedList, synchronized or not. Once again, your decision will depend on how the list is used in practice. Ifset and get predominate, or element insertion and removal is mainly at the end of the list, then ArrayList will be the best choice. If, instead, your application needs to frequentlyinsert and remove elements near the start of the list as part of a process that uses iteration, LinkedList may be better. If you are in doubt, test the performance with each implementation. A Java 6 alternative for single-threaded code that may be worth considering in the last case if the insertions and removals are actually at the start of the list is to write to the Deque interface, taking advantage of its very efficient ArrayDeque implementation. For relatively infrequent random access, use an iterator, or copy the ArrayDeque elements into an array using toArray.
It is possible that, in a future release, ArrayDeque will be retrofitted to implement the List interface; if that happens, it will become the implementation of choice for both Queue and List in single-threaded environments
.

2.Maps


The Map interface is the last of the major Collections Framework interfaces, and the only one that does not inherit from Collection.
看一下需要注意的方法
Providing Collection Views of the Keys, Values, or Associations:
Set<Map.Entry<K, V>> entrySet() // return a Set view of the associations
Set<K> keySet()                 // return a Set view of the keys
Collection<V> values()          // return a Collection view of the values

The collections returned by these methods are backed by the map, soany changes to them are reflected in the map itself, and vice versa. In fact, only limited changes can be made via the view:elements can be removed, either directly or via an iterator over the view,but cannot be added; you will get an UnsupportedOperationException if you try.Removing a key removes the single corresponding key-value association; removing a value, on the other hand, removes only one of the associations mapping to it; the value may still be present as part of an association with a different key. An iterator over the view will become undefined if the backing map is concurrently modified.

这里有几点需要特别留意,第一,keySet,values等方法返回的集合中进行操作会改变相应的map,更重要的是,尽管我们之前的到一个keyset集合,那么我们在之后对map进行操作时,这些改变(插入删除)会反应到keyset集合中,其他几个也是一样。第二,keyset集合不能进行插入操作。
从源码中可以很清楚的看出上面的原因,KeySet等操作其实是通过KeyInterator来实现的,它遍历的就是Map的key,因此map的变化会反应在keyset中。

Implementing Map
The implementations, eight in all, that the Collections Framework provides for Map are shown in Figure 16.2. We shall discuss HashMap, LinkedHashMap, WeakHashMap, IdentityHashMap, and EnumMap here; the interfaces NavigableMap, ConcurrentMap, and ConcurrentNavigableMap are discussed, along with their implementations, in the sections following this one.

HashMap

Iteration over a collection of keys or values requires time proportional to the capacity of the map plus the number of key-value mappings that it contains. The iterators are fail-fast.
这里要注意一点,table的每一个slot指向的是一个对象,它包含key,value还有next。对于WeakHashMap,其中的key指向WeakReference,WeakReference指向对象,value部分也通过指针指向client。 HashMap源码分析,可以参考《HashMap源码分析》。

LinkedHashMap

Unlike LinkedHashSet, however, LinkedHashMap offers a choice of iteration orders; elements can be returnedeither in the order in which they were inserted in the map, or in the order in which they were accessed (fromleast-recently to most-recently accessed). An access ordered LinkedHashMap is created by supplying an argument of true for the last parameter of the constructor:
public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder)
Supplying false will give an insertion-ordered map.
Access-ordered maps are especially useful for constructing Least Recently Used (LRU) caches.
LinkedHashMap的源码实现非常巧妙,其源码分析可以参考《LinkedHashMap源码分析》。

WeakReferenceHashMap

To look up the value associated with a supplied key, the HashMap will look for a key that matches (in the sense ofequals) the supplied one they don't have to be physically the same object.
But suppose that the objects of the key class areunique that is, object equality is the same as object identity. For example, each object might contain a unique serial number. In this case, once we no longer have a reference from outside the map to an object being used as a key, we can never look it up again, because we can never re-create it. So the map might as well get rid of the key-value pair and, in fact, there may bea strong advantage in doing so if the map is large and memory is in short supply. That is the idea that WeakHashMap implements.
A more general use is in those applications for example, caches where you don't mind information disappearing if memory is low. Here, WeakHashMap is useful whether or not the keys are unique, because you can always re-create a key if necessary to see if the corresponding value is still in the cache. WeakHashMap isn't perfect for this purpose; one of its drawbacks is that it weakly references the map's keys rather than its values, which usually occupy much more memory. So even after the garbage collector has reclaimed a key, the real benefit in terms of available memory will not be experienced until the map has removed the stale entry. A second drawback is that weak references are too weak; the garbage collector is liable to reclaim a weakly reachable objectat any time, and the programmer cannot influence this in any way.
WeakHashMap performs similarly to HashMap, though more slowly because of the overheads of the extra level of indirection for keys.The cost of clearing out unwanted key-value associations before each operation is proportional to the number of associations that need to be removed. The iterators over collections of keys and values returned by WeakHashMap are fail-fast.

IdentityHashMap

An IdentityHashMap differs from an ordinary HashMap in that two keys are considered equal only if they arephysically the same object: identity, rather than equals, is used for key comparison.
The standard implementation of IdentityHashMap handles collisions differently than the chaining method shown in Figure 13.2 and used by all the other variants of HashSet and HashMap. This implementation uses a technique called linear probing, in which the key and value references are stored directly in adjacent locations in the table itself rather than in cells referenced from it. With linear probing, collisions are handled by simply stepping along the table until the first free pair of locations is found.  if key2 and value2 were removed from the table in Figure 13.2, key3 and value3 would have to be moved along to take their place.

public IdentityHashMap(int expectedMaxSize)

The first two are the standard constructors found in every general-purpose Map implementation. The third takes the place of the two constructors that in other HashMaps allow the user to control the initial capacity of the table and the load factor at which it will be rehashed. IdentityHashMap doesn't allow this,fixing it instead (at .67 in the standard implementation) in order to protect the user from the consequences of setting the load factor inappropriately: whereas the cost of finding a key in a table using chaining is proportional to the load factor l, in a table using linear probing it is proportional to 1/(1-l). So avoiding high load factors is too important to be left to the programmer! This decision is in line with the policy, mentioned earlier, of no longer providing configuration parameters when new classes are introduced into the Framework.

EnumMap
SortedMap and NavigableMap
Like SortedSet, the subinterface SortedMap (see Figure 16.5) adds to its contract a guarantee that its iterators will traverse the map in ascending key order.
注意是按照key的顺序。

NavigableMap
Map.Entry<K,V> pollFirstEntry()
Map.Entry<K,V> pollLastEntry()
Map.Entry<K,V> firstEntry()
Map.Entry<K,V> lastEntry()

NavigableMap<K,V> subMap(
K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
NavigableMap<K,V> headMap(K toKey, boolean inclusive)
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)


Map.Entry<K,V> ceilingEntry(K Key)
K ceilingKey(K Key)
Map.Entry<K,V> floorEntry(K Key)
K floorKey(K Key)
Map.Entry<K,V> higherEntry(K Key)
K higherKey(K Key)
Map.Entry<K,V> lo werEntry(K Key)
K lowerKey(K Key)

TreeMap

SortedMap is implemented in the Collections Framework by treeMap. We met trees as a data structure for storing elements in order when we discussed TReeSet (see Section 13.2.1). In fact, the internal representation of a treeSet is just a TReeMap in which every key is associated with the same standard value, so the explanation of the mechanism and performance of red-black trees given in Section 13.2.1 applies equally here.
treeMap has similar performance characteristics to TReeSet: the basic operations (get, put, and remove) perform in O(log n) time). The collection view iterators are fail-fast.

ConcurrentMap

This requirement cannot be met by synchronized maps such as those provided by Collections.synchronizedMap, because with full synchronization each operation needs to obtain an exclusive lock on the entire map, effectively serializing access to it. Locking only a part of the collection at a timelock striping can achieve very large gains in throughput, as we shall see shortly with ConcurrentHashMap. Butbecause there is no single lock for a client to hold to gain exclusive access, client-side locking no longer works, andclients need assistance from the collection itself to carry out atomic actions.
That is the purpose of the interface ConcurrentMap. It provides declarations for methods that perform compound operations atomically. There are four of these methods:
V putIfAbsent(K key, V value)
// associate key with value only if key is not currently present.
// return the old value (may be null) if the key was present,
// otherwise return null
boolean remove(Object key, Object value)
// remove key only if it is currently mapped to value. Returns
// true if the value was removed, false otherwise
V replace(K key, V value)
// replace entry for key only if it is currently present. Return
// the old value (may be null) if the key was present, otherwise
// return null
boolean replace(K key, V oldValue, V newValue)
// replace entry for key only if it is currently mapped to oldValue.
// return true if the value was replaced, false otherwise

ConcurrentHashMap

ConcurrentHashMap provides an implementation of ConcurrentMap and offers a highly effective solution to the problem of reconciling throughput with thread safety. It is optimized for reading, soretrievals do not block even while the table is being updated (to allow for this, the contract states that the results of retrievals will reflect the latest update operations completed before the start of the retrieval). Updates also can often proceed without blocking, because a ConcurrentHashMap consists of not one but a set of tables, called segments, each of which can be independently locked. If the number of segments is large enough relative to the number of threads accessing the table, there will often be no more than one update in progress per segment at any time.
上面括号中的文字需要说明一下,CocurrentHashMap并不能保证在table中放入一个元素后,其他线程立马可以看到它,但是可以保证当put方法完成后其他线程可以看到它。这个问题的分析需要结合java内存模型中的happen-before关系来分析。

2.如果读的时候不加锁会不会出现读一半的情况,就是前一半的内容是旧的,后一半内容是新的情况?
个人认为会出现这种情况,因为读操作是不加锁的,那么它与加锁的写操作之间不存在任何happen-before关系。所以可能会出现这种情况。

If the map is being concurrently updated, however,the size method will not succeed in obtaining a consistent result. In this situation, itobtains exclusive access to the map by locking all the segments, obtaining the entry count from each, then unlocking them again. The performance cost involved in this is a justifiable tradeoff against the highly optimized performance for common operations, especially reads. Overall, ConcurrentHashMap is indispensable in highly concurrent contexts, where it performs far better than any available alternative.
Disregarding locking overheads such as those just described, the cost of the operations of ConcurrentHashMap are similar to those of HashMap. The collection views return weakly consistent iterators.

ConcurrentNavigableMap
ConcurrentSkipListMap

The relationship between ConcurrentSkipListMap and ConcurrentSkipListSet is like that between treeMap and TReeSet; a ConcurrentSkipListSet is implemented by a ConcurrentSkipListMap in which every key is associated with the same standard value, so the mechanism and performance of the skip list implementation given in Section 13.2.2 applies equally here: the basic operations (get, put, and remove) perform inO(log n) time); iterators over the collection views execute next in constant time. These iterators are fail-fast.

Comparative performance of different Map implementations


Some specialized situations dictate the implementation: EnumMap should always (and only) be used for mapping from enums. Problems such as the graph traversals described in Section 16.2.4 call for IdentityHashMap. For a sorted map, use treeMap where thread safety is not required, and ConcurrentSkipListMap otherwise.
That leaves the choice of implementation for general-purpose maps. For concurrent applications, ConcurrentHashMap is the only choice. Otherwise, favor LinkedHashMap over HashMap (and accept its slightly worse performance) if you need to make use of the insertion or access order of the map for example, to use it as a cache.
enum?-->并发?--->顺序(排序,插入顺序)?

第17章 Collections


我们只需要看一些比较有用的方法。

The generic algorithms fall into four major categories: changing element order in a list, changing the contents of a list, finding extreme values in a collection, and finding specific values in a list.
Changing the Order of List Elements
void reverse(List<?> list)
          // reverse the order of the elements
void rotate(List<?> list, int distance)
          // rotate the elements of the list; the element at index
          // i is moved to index (distance + i) % list.size()
void shuffle(List<?> list)
          // randomly permute the list elements
void shuffle(List<?> list, Random rnd)
          // randomly permute the list using the randomness source rnd
<T extends Comparable<? super T>> void sort(List<T> list)
          // sort the supplied list using natural ordering
<T> void sort(List<T> list, Comparator<? super T> c)
          // sort the supplied list using the supplied ordering
void swap(List<?> list, int i, int j)
          // swap the elements at the specified positions


Changing the Contents of a List
<T> void copy(List<? super T> dest, List<? extends T> src)
          // copy all of the elements from one list into another
这个方法最好还是不要用,当dest长度小于src时会抛出数组越界异常,而它的构造函数只有两个,一个空参数,另一参数是一个Collection。另外,这个也是浅拷贝。

<T> void fill(List<? super T> list, T obj)
          // replace every element of list with obj
<T> boolean replaceAll(List<T> list, T oldVal, T newVal) //测试没起作用
          // replace all occurrences of oldVal in list with newVal

Finding Extreme Values in a Collection

<T extends Object & Comparable<? super T>>
  T max(Collection<? extends T> coll)  // return the maximum element
                                       // using natural ordering
<T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
                                       // return the maximum element
                                       // using the supplied comparator
<T extends Object & Comparable<? super T>>
  T min(Collection<? extends T> coll)  // return the minimum element
                                       // using natural ordering
<T> T min(Collection<? extends T> coll, Comparator<? super T> comp)
                                       // return the minimum element
                                       // using the supplied comparator
使用这些方法时,里面不能有null值
Synchronized Collections
But there are occasions when you do need to program multiple threads to have access to the same collection, and these synchronized wrappers are provided by the Collections class for such situations.
There are six synchronized wrapper factory methods, corresponding to the six pre-Java 6 interfaces of the Collections Framework. (No synchronized wrappers were provided in Java 6 for NavigableSet or NavigableMap. If they had been provided, there would be very few situations in which you would choose them over the thread-safe collections ConcurrentSkipListSet and ConcurrentSkipListMap.)
<T> Collection<T> synchronizedCollection(Collection<T> c);
<T> Set<T> synchronizedSet(Set<T> s);
<T> List<T> synchronizedList(List<T> list);
<K, V> Map<K, V> synchronizedMap(Map<K, V> m);
<T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s);
<K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m);

The classes that provide these synchronized views are conditionally thread-safe (see Section 11.5); although each of their operations is guaranteed to be atomic, you may need to synchronizemultiple method calls in order to obtain consistent behavior. In particular,iterators must be created and used entirely within a code block synchronized on the collection; otherwise, the result will at best be failure with ConcurrentModificationException. This is very coarse-grained (粗粒度)synchronization; if your application makes heavy use of synchronized collections, its effective concurrency will be greatly reduced.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值