Как HashMap.values () и HashMap.keySet() возвращают значения и ключи?
Исходный код HashMap.values()
показан следующим образом
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
Как вы можете видеть, при первом вызове метода values()
он просто возвращает объект Values
. Объект Values
является подклассом AbstractCollection
без конструктора и, конечно, не содержит элемента. Но когда я вызвал метод, он быстро возвращал коллекцию
Collection<String> values = map.values();
System.out.println(values);
Это так странно. Не только values()
, но и методы keySet()
и entrySet()
возвращают такие пустые объекты. Итак, вот мой вопрос, когда и как эти методы возвращают объекты с элементами, которые нам нужны?
Ответы
Ответ 1
Это неправильное представление о том, что класс Values
"конечно пуст". Просто потому, что в нем нет метода, и его конструктор не имеет никаких аргументов, это не значит, что коллекция пуста.
Класс Values
- это "внутренний класс" (нестатический вложенный класс) HashMap
, что означает, что он имеет неявную ссылку на объект HashMap
, который его создал. Поэтому он может обращаться ко всем элементам HashMap
, либо явно, используя ссылку HashMap.this
, либо просто обращаясь к членам напрямую. Поскольку это внутренний класс, ему даже разрешен доступ к частным членам HashMap
.
Вы можете увидеть это, например, в реализации метода Values
класса Values
:
public int size() {
return size;
}
Класс Values
не имеет члена size
, поэтому size
относится к размеру HashMap
. Это эквивалентно:
public int size() {
return HashMap.this.size;
}
РЕДАКТИРОВАТЬ: Обратите внимание, что это также означает, что получаемая вами коллекция не является копией, но все же относится к исходному содержимому HashMap
и поэтому изменяется при обновлении HashMap
:
// Getting the entry set (not a copy!)
Set<Entry<String, String>> entries = map.entrySet();
// Add elements to the map afterwards
map.put("abc", "def");
// Check out the entries in the collection
// (magically containing the elements added after getting the collection)
System.out.println(entries); // "[abc=def]"
Ответ 2
Класс Values
реализует Collection
, который поддерживается HashMap
. Как отметил мастов, Values
является внутренним классом HashMap
, который дает ему доступ к членам экземпляра HashMap
, с которым он связан. Поэтому он не пуст. Его размер - это размер HashMap
, и когда вы перебираете его, вы повторяете записи HashMap
.
Когда вы вызываете System.out.println(values);
, вы вызываете метод toString
AbstractCollection
, который использует Iterator
для итерации по значениям и получения их представления String
. Iterator
фактически выполняет итерации над записями HashMap
и возвращает их значения.
То же самое относится к Set
, возвращаемому keySet
и entrySet
.
Ответ 3
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
Эти две строки отвечают на ваш вопрос.
values
является внутренней коллекцией (Унаследовано от AbstractMap
). Если это не null
, то оно будет возвращено.
Если оно null
, то оно будет инициализировано и будет возвращено новое.
Теперь, я думаю, основной вопрос вашего вопроса:
, когда и как эти методы возвращают объекты с элементами, которые нам нужны?
Технически values
всегда возвращает этот инициализированный объект. Затем, как мы получаем наши значения ввода из этого объекта.?
Давайте немного углубимся:
values () фактически возвращают объект класса values
, который является классом Inner HashMap
и расширяет AbstractCollection
private final class Values extends AbstractCollection<V>
Как вы изучили исходный код. Затем вы найдете внутри класса values
public Iterator<V> iterator() {
return newValueIterator();
}
этот newValueIterator()
делает трюк.
Если вы пойдете глубже, вы обнаружите, что newValueIterator()
возвращает объект ValueIterator
, который является подклассом HashIterator
HashIterator
реализует основные функциональные возможности для итерации, которые на самом деле повторяются над table
, поддерживаемыми HashMap
.