Immutable collection system, there is a very important collection is not introduced, is immutablemap, through the UML diagram, you can see IMMUTABLEMAP structure system.
First look at Immutablebimap, because the implementation of the ordinary immutablemap depends on it. Immutablebimap on the basis of Immutablemap, add inverse () and other methods, can make the key value reversal. The construction of Immutablebimap is also based on the number of elements, using different implementations (0-->emptyimmutablbimap,1-->singletonimmutablbimap,n (n>=2)-- REGULARIMMUBTALMAP), the code looks like this:
Public Abstract classImmutablebimap<k, v>extendsImmutablemap<k, v>ImplementsBimap<k, v>{ Public Static<k, v> immutablebimap<k, v>of () {//inside the empty element, without maintaining the storage structure, the inverse () method directly returns this return(Immutablebimap<k, v>) emptyimmutablebimap.instance; } Public Static<k, v> immutablebimap<k, v>of (K K1, V v1) {//when a single element is constructed, returns this class, internally maintaining two elements K,v,inverse (), returning the V,k singletonimmutablebimap return NewSingletonimmutablebimap<k, v>(K1, v1); } Public Static<k, v> immutablebimap<k, v>of (k K1, v v1, K K2, v v2) {//multiple element constructs are, return this class, internally maintain two entry[] collections, a position with key as the Hashbucket,
Another position with value as hashbucket, used for inverse (), the reversal of Key-value return NewRegularimmutablebimap<k, v>(entryof (K1, v1), entryof (K2, v2)); }}
The CopyOf () method, implemented in Immutablecollections, is that if copyOf () is still a Immutablecollections collection, then only the assignment of the reference is made, because the collection itself is immutable.
After seeing Immutablebimap, looking back at Immutablemap is a lot simpler, just on immutablebimap basis except inverse () method, and inside for the user single array (hashbucket)
There is no need to maintain a reversed array. Call the Immutablebimap.of () and Immutablebimap.of (K,V) methods directly in the absence of elements and the construction of a single element, as shown in the following code:
Public Abstract classImmutablemap<k, v>ImplementsMap<k, v>, Serializable {/*** Returns the empty map. This map behaves and performs comparably to * {@linkCollections#emptymap}, and is preferable mainly for consistency * and maintainability of your code. */ Public Static<k, v> immutablemap<k, v>of () {returnImmutablebimap.of (); } /*** Returns An immutable map containing a single entry. This map behaves and * performs comparably to {@linkCollections#singletonmap} but would not accept * a null key or value. It is preferable mainly for consistency and * maintainability of your code. */ Public Static<k, v> immutablemap<k, v>of (K K1, V v1) {returnimmutablebimap.of (K1, v1); }}
When multiple elements are constructed, the return regularimmubtalmap is similar to the internal implementation of REGULARIMMUTABLEBIMAP, removing the maintenance of the inverse (value-key) array, and removing the inverse () method.
Finally, the simple elaboration of the implementation of the Immutablesortedmap, Immutablemap single elements and the implementation of empty elements, it is not detailed to say that interested readers can see for themselves. When multiple elements are implemented,
Immutablesortedmap implementation class is Regularimmutablesortedmap, interestingly, its internal maintenance key and value of the data structure is two list, so imagine, the sort of early in the construction of the time has been completed, And the fact is, the exact code looks like this:
@SuppressWarnings ("Unchecked"publicstaticextendsSuperof (K K1, V V1, K K2, v v2, K K3, v v3, K K4, v v4) { // the sequencer and entires incoming fromentries method return
false, 4
, entryof (K1, v1), entryof (K2, V2), entryof (K3, v3), entryof (K4, v4));}
Static<k, v> immutablesortedmap<k, v>fromentries (Comparator<?SuperK> Comparator,BooleanSamecomparator,intSize, entry<k, v>... entries) { for(inti = 0; i < size; i++) {Entry<k, v> entry =Entries[i]; Entries[i]=entryof (Entry.getkey (), Entry.getvalue ()); } if(!samecomparator) {sortentries (comparator, size, entries);//Traversing entries sortingvalidateentries (size, entries, comparator); } returnfromsortedentries (Comparator, size, entries); }
Static<k, v> immutablesortedmap<k, v>fromsortedentries (Comparator<?SuperK>Comparator,intSize,entry<k, v>[] entries) { if(Size = = 0) { returnEmptymap (Comparator); } //iterate through the sorted entries, separating key and value, respectively, to form the respective listImmutablelist.builder<k> Keybuilder =Immutablelist.builder (); Immutablelist.builder<V> Valuebuilder =Immutablelist.builder (); for(inti = 0; i < size; i++) {Entry<k, v> entry =Entries[i]; Keybuilder.add (Entry.getkey ()); Valuebuilder.add (Entry.getvalue ()); } return NewRegularimmutablesortedmap<k, v>( NewRegularimmutablesortedset<k>(Keybuilder.build (), comparator), Valuebuilder.build ());}
The entry in Immutablemap is also being re-implemented by guava, adding a bucket of computational logic, such as UML:
Abstractmapentry on the basis of the original map.entry, write operation, set as the direct throw anomaly, Immutableentry realize Getkey () and GetValue (), Immutablemapentry the calculation and maintenance methods of the buckets (linked list), which are finally reflected to nonterminalmapentry and terminalentry, for these two classes, Terminalentry is the tail node of the bucket list, so it is implemented as follows:
Static Final classTerminalentry<k, v>extendsImmutablemapentry<k, v>{terminalentry (immutablemapentry<k, v>contents) { Super(contents); } terminalentry (K key, V value) {Super(key, value); } @Override @Nullable Immutablemapentry<k, v>Getnextinkeybucket () {//tail node, so no Nuext return NULL; } @Override @Nullable Immutablemapentry<k, v>Getnextinvaluebucket () {//tail node, so no Nuext return NULL; } }
The nonterminalmapentry structure is required to pass in the next entry
Private Static Final classNonterminalmapentry<k, v>extendsImmutablemapentry<k, v> { Private FinalImmutablemapentry<k, v>Nextinkeybucket; Nonterminalmapentry (K key, V value, Immutablemapentry<k, v>nextinkeybucket) { Super(key, value); This. Nextinkeybucket =Nextinkeybucket; } nonterminalmapentry (Immutablemapentry<k, v> contents, immutablemapentry<k, v>nextinkeybucket) { Super(contents); This. Nextinkeybucket =Nextinkeybucket; } @Override immutablemapentry<k, v>Getnextinkeybucket () {//the next entry in the same bucket returnNextinkeybucket; } @Override @Nullable Immutablemapentry<k, v>Getnextinvaluebucket () {//Bimap will maintain value buckets. return NULL; } }
Then, in the construction, if a hash conflict occurs, it is with Nonterminalmapentry, the code is as follows:
Regularimmutablemap (entry<?,? >[] theentries) { intSize =theentries.length; Entries=Createentryarray (size); intTablesize =hashing.closedtablesize (size, max_load_factor); Table=Createentryarray (tablesize); Mask= TableSize-1; for(intEntryindex = 0; Entryindex < size; entryindex++) {@SuppressWarnings ("Unchecked")//All we callers carefully put in only entry<k, V>sEntry<k, v> Entry = (entry<k, v>) Theentries[entryindex]; K Key=Entry.getkey (); V value=Entry.getvalue (); Checkentrynotnull (key, value); intTableindex = Hashing.smear (Key.hashcode ()) &Mask; @Nullable Immutablemapentry<k, v> existing =Table[tableindex]; //prepend, not append, so the entries can be immutable//in the construction is, if a hash conflict arises, then the direct append to the front of the terminalImmutablemapentry<k, v> newEntry = (existing = =NULL) ?NewTerminalentry<k, v>(key, value):NewNonterminalmapentry<k, v>(key, value, existing); Table[tableindex]=NewEntry; Entries[entryindex]=NewEntry; Checknoconflictinbucket (Key, newEntry, existing); } }
Guava Source Analysis--immutable Collections (4)