I was looking at HashMap source of the time to find a problem, before this can be said to be completely without thinking about this problem, at the beginning of this point there is no doubt that there is such a grammatical detail, so I can not think of its solution, until their own hands-on experiment to rewrite the code to fully understand.
The bottom of the data stored in HashMap is an array of entry, which retains a key-value pair and a pointer to the next entry. So HashMap is a structure that combines arrays and lists. Because of this, you have 3 ways to observe the data: Keyset,values,entryset. The first is the result of a value from the key point of view. It contains a set of values for all the keys in the table, because HashMap explicitly specifies that a key can only correspond to one value, so there is no duplicate key, which is why you can use a set to load a key. The second values look at the mapping table from the value of the key-value pair, because there can be more than one key corresponding to a value, so there may be multiple identical values. (this view is similar to the view of a function) the third angle is the most basic angle, that is, from the point of view of the key-value pairs of the question. It returns a collection of key-value pairs. (Key-value pairs are equal if and only if the keys and values are equal).
The above is a general understanding. On this basis, Java source code: (Here I only use the keyset to illustrate this problem)
1 PublicSet<k>KeySet () {2Set<k> KS =KeySet;3 return(KS! =NULL? KS: (KeySet =NewKeySet ()));4 }5 6 Private Final classKeySetextendsAbstractset<k> {7 PublicIterator<k>iterator () {8 returnnewkeyiterator ();9 }Ten Public intsize () { One returnsize; A } - Public Booleancontains (Object o) { - returnContainsKey (o); the } - Public BooleanRemove (Object o) { - returnHashMap. This. Removeentryforkey (o)! =NULL; - } + Public voidClear () { -HashMap. This. Clear (); + } A}
It looked simple and clear, but I found a detail and was entangled with it for an afternoon (the syntax details are hidden deep).
In this place we can see that when calling the keyset () method to a HashMap, it returns a collection whose contents are the values of all keys. But the question is how this place is going to come true. From the code can see this place directly returned a thing called keyset. So what exactly is this thing? Press and hold the command key to see where this variable is declared:
Inside the Abstractmap.class:
1 transient volatile Set<k> null; 2 transient volatile null;
That is, this place is the two collection type inherited from HashMap's parent class Abstractmap (the first one is what I call the keyset, the second is the exact same process).
But the problem still does not solve, why this keyset can return to the current hashmap of the key worth of collection? I started with the idea of simply looking at this place, because my imagination was where it might be possible to find an obvious synchronization method, so that the value of the inside of the keyset varies with the value of the table (which is the underlying array, which stores all the key values to entry). But I found: "No."
The first time I think I may not find the right position, because generally it provides the inheritance of these classes is more complex, may not be in this place, may be implemented elsewhere, but I have to look for a long time did not find, that is to say: "There is no clear code to let keyset sync HashMap." The problem becomes bigger, in fact if you look for the following code in ABSTRACTMAP:
1 PublicSet<k>KeySet () {2 if(KeySet = =NULL) {3KeySet =NewAbstractset<k>() {4 PublicIterator<k>iterator () {5 return NewIterator<k>() {6 Privateiterator<entry<k,v>> i =EntrySet (). iterator ();7 8 Public BooleanHasnext () {9 returnI.hasnext ();Ten } One A PublicK Next () { - returnI.next (). GetKey (); - } the - Public voidRemove () { - I.remove (); - } + }; - } + A Public intsize () { at returnAbstractmap. This. Size (); - } - - Public BooleanIsEmpty () { - returnAbstractmap. This. IsEmpty (); - } in - Public voidClear () { toAbstractmap. This. Clear (); + } - the Public Booleancontains (Object k) { * returnAbstractmap. This. ContainsKey (k); $ }Panax Notoginseng }; - } the returnKeySet; +}
It doesn't look like a synchronous process at all, at least in my understanding, moving a container to another container requires a loop to move things one by one, even if only a shallow copy drops the value of the pointer. This section of code has nothing to do with "let keyset this set hold the value of the key in table." But it is true that this place has been synchronized.
Look at the following code:
1 Public classMain {2 3 Public Static voidMain (string[] args) {4 5Testiterator T =Newtestiterator ();6Set<integer> set =T.keyset ();7 System.out.println (set);8 9 }Ten } One A - classTestiterator { - PublicSet<integer>KeySet () { the - Finalarraylist<integer> result =NewArraylist<integer>(); -Result.add (1); -Result.add (2); +Result.add (3); - +Set<integer> KeySet =NewAbstractset<integer>() { A PublicIterator<integer>iterator () { at return NewIterator<integer>() { - Privateiterator<integer> i =result.iterator (); - - @Override - Public BooleanHasnext () { - returnI.hasnext (); in } - to @Override + PublicInteger Next () { - returnI.next (); the } * $ @OverridePanax Notoginseng Public voidRemove () { - I.remove (); the } + }; A } the + @Override - Public intsize () { $ return0; $ } - }; - the returnKeySet; - }Wuyi}
The result of this place is:
[1, 2, 3]
Why is it? The code for this place is rewritten in accordance with HASHMAP code, and I'll rewrite it as follows:
1 Public classMain {2 3 Public Static voidMain (string[] args) {4arraylist<integer> array =NewArraylist<integer>();5Array.add (1);6Array.add (2);7Array.add (3);8 9MySet set =NewMySet (Array.iterator ());Ten System.out.println (set); One } A - } - the classMySetextendsAbstractset<integer> { - - PrivateIterator<integer>iter; - + PublicMySet (iterator<integer>i) { -ITER =i; + } A at @Override - PublicIterator<integer>iterator () { - returniter; - } - - @Override in Public intsize () { - return0; to } + -}
is the same effect. In other words, just let a set it hold a iterrator of someone else, it will think of itself as it. The details of this place are to be continued.
I did not expect the series--HASHMAP implementation of the underlying details of the keyset,values,entryset of a bottom-level implementation details