1. Объяснение
Что касается метода обхода карты в Java, во многих статьях рекомендуется использовать набор записей, который считается гораздо более эффективным, чем набор ключей. Причина в том, что метод enterSet получает набор всех ключей и значений сразу, тогда как keySet получает только набор ключей. Для каждого ключа значение нужно искать в Map еще раз, тем самым уменьшая объем. общая эффективность. Так какова реальная ситуация?
Чтобы понять реальный разрыв в производительности обхода, включая различия в различных сценариях, таких как перемещение ключ+значение, перемещение ключа, перемещение значения и т. д., я попытался провести несколько сравнительных тестов.
2. Сравнительное тестирование
Сначала был проведен только простой тест, но результаты показали, что производительность keySet была лучше, что меня озадачило. Не правда ли, все говорят, что входной набор явно лучше, чем keySet? Для дальнейшей проверки были использованы различные данные испытаний для более детального сравнительного тестирования.
2.1 Данные испытаний
2.1.1 Тестовые данные HashMap
HashMap-1, размер — 1 миллион, ключ и значение — строковые, значение ключа — 1, 2, 3...1000000:
Скопируйте код кода следующим образом:
Map<String, String> map = new HashMap<String, String>();
Строковый ключ, значение;
для (я = 1; я <= число; я++) {
ключ = "" + я;
значение = «значение»;
map.put(ключ, значение);
}
HashMap-2, размер — 1 миллион, ключ и значение — строка, значение ключа — 50, 100, 150, 200,..., 50000000:
Скопируйте код кода следующим образом:
Map<String, String> map = new HashMap<String, String>();
Строковый ключ, значение;
для (я = 1; я <= число; я++) {
ключ = "" + (я * 50);
значение = «значение»;
map.put(ключ, значение);
}
2.1.2 Тестовые данные TreeMap
TreeMap-1, размер — 1 миллион, ключ и значение — строка, значение ключа — 1, 2, 3...1000000:
Скопируйте код кода следующим образом:
Map<String, String> map = new TreeMap<String, String>();
Строковый ключ, значение;
для (я = 1; я <= число; я++) {
ключ = "" + я;
значение = «значение»;
map.put(ключ, значение);
}
TreeMap-2, размер — 1 миллион, ключ и значение — String, значения ключа — 50, 100, 150, 200,..., 50000000, более дискретно:
Скопируйте код кода следующим образом:
Map<String, String> map = new TreeMap<String, String>();
Строковый ключ, значение;
для (я = 1; я <= число; я++) {
ключ = "" + (я * 50);
значение = «значение»;
map.put(ключ, значение);
}
2.2 Сценарий тестирования
Используйте различные методы записи keySet, enterSet и значений, чтобы протестировать три сценария: перемещение ключа+значения, перемещение ключа и перемещение значения.
2.2.1 Переход ключ+значение
keySet проходит ключ+значение (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<String> iter = map.keySet().iterator();
в то время как (iter.hasNext()) {
ключ = iter.next();
значение = map.get(ключ);
}
keySet проходит ключ+значение (метод записи 2):
Скопируйте код кода следующим образом:
for (Строковый ключ: map.keySet()) {
значение = map.get(ключ);
}
enterSet проходит ключ+значение (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<Entry<String, String>> iter = map.entrySet().iterator();
Entry<String, String> запись;
в то время как (iter.hasNext()) {
запись = iter.next();
ключ = запись.getKey();
значение = запись.getValue();
}
enterSet проходит ключ+значение (метод записи 2):
Скопируйте код кода следующим образом:
for (Entry<String, String> запись: map.entrySet()) {
ключ = запись.getKey();
значение = запись.getValue();
}
2.2.2 Ключ перемещения
keySet перемещает ключ (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<String> iter = map.keySet().iterator();
в то время как (iter.hasNext()) {
ключ = iter.next();
}
keySet перемещает ключ (метод записи 2):
Скопируйте код кода следующим образом:
for (Строковый ключ: map.keySet()) {
}
enterSet перемещает ключ (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<Entry<String, String>> iter = map.entrySet().iterator();
в то время как (iter.hasNext()) {
ключ = iter.next().getKey();
}
enterSet перемещает ключ (метод записи 2):
Скопируйте код кода следующим образом:
for (Entry<String, String> запись: map.entrySet()) {
ключ = запись.getKey();
}
2.2.3 Значение траверсы
keySet перемещает значение (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<String> iter = map.keySet().iterator();
в то время как (iter.hasNext()) {
значение = map.get(iter.next());
}
keySet перемещает значение (метод записи 2):
Скопируйте код кода следующим образом:
for (Строковый ключ: map.keySet()) {
значение = map.get(ключ);
}
записьSet пересекает значение (метод записи 1):
Скопируйте код кода следующим образом:
Iterator<Entry<String, String>> iter = map.entrySet().iterator();
в то время как (iter.hasNext()) {
значение = iter.next().getValue();
}
входной набор перемещает значение (метод записи 2):
Скопируйте код кода следующим образом:
for (Entry<String, String> запись: map.entrySet()) {
значение = запись.getValue();
}
значения пересекает значение (способ записи 1):
Скопируйте код кода следующим образом:
Iterator<String> iter = map.values().iterator();
в то время как (iter.hasNext()) {
значение = iter.next();
}
значения пересекает значение (способ записи 2):
Скопируйте код кода следующим образом:
for (Строковое значение: map.values()) {
}
2.3 Результаты испытаний
2.3.1 Результаты теста HashMap
Единица: миллисекунда | ХэшМап-1 | ХэшМап-2 |
| keySet проходит ключ+значение (метод записи 1) | 39 | 93 |
| keySet проходит ключ+значение (метод записи 2) | 38 | 87 |
| записьSet пересекает ключ+значение (метод записи 1) | 43 | 86 |
| входной набор проходит ключ+значение (метод записи 2) | 43 | 85 |
Единица: миллисекунда | ХэшМап-1 | ХэшМап-2 |
| keySet перемещает ключ (метод записи 1) | 27 | 65 |
| keySet перемещает ключ (метод записи 2) | 26 | 64 |
| enterSet перемещает ключ (метод записи 1) | 35 | 75 |
| enterSet перемещает ключ (метод записи 2) | 34 | 74 |
Единица: миллисекунда | ХэшМап-1 | ХэшМап-2 |
| keySet перемещает значение (метод записи 1) | 38 | 87 |
| keySet перемещает значение (метод записи 2) | 37 | 87 |
| записьSet пересекает значение (метод записи 1) | 34 | 61 |
| записьSet пересекает значение (метод записи 2) | 32 | 62 |
| значения траверса значения (способ записи 1) | 26 | 48 |
| значения траверса значения (способ записи 2) | 26 | 48 |
2.3.2 Результаты теста TreeMap
Единица: миллисекунда | Карта Дерева-1 | Карта Дерева-2 |
| keySet проходит ключ+значение (метод записи 1) | 430 | 451 |
| keySet проходит ключ+значение (метод записи 2) | 429 | 450 |
| записьSet пересекает ключ+значение (метод записи 1) | 77 | 84 |
| входной набор проходит ключ+значение (метод записи 2) | 70 | 68 |
Единица: миллисекунда | Карта Дерева-1 | Карта Дерева-2 |
| keySet перемещает ключ (метод записи 1) | 50 | 49 |
| keySet перемещает ключ (метод записи 2) | 49 | 48 |
| enterSet перемещает ключ (метод записи 1) | 66 | 64 |
| enterSet перемещает ключ (метод записи 2) | 65 | 63 |
Единица: миллисекунда | Карта Дерева-1 | Карта Дерева-2 |
| keySet перемещает значение (метод записи 1) | 432 | 448 |
| keySet перемещает значение (метод записи 2) | 430 | 448 |
| записьSet пересекает значение (метод записи 1) | 62 | 61 |
| записьSet пересекает значение (метод записи 2) | 62 | 61 |
| значения траверса значения (способ записи 1) | 46 | 46 |
| значения траверса значения (способ записи 2) | 45 | 46 |
3. Заключение
3.1 Если вы используете HashMap
1. При одновременном перемещении ключа и значения разница в производительности между методами keySet и enterSet зависит от конкретных условий ключа, таких как сложность (сложные объекты), дискретность, частота конфликтов и т. д. Другими словами, это зависит от стоимости поиска значения в HashMap. Операция входного набора по получению всех ключей и значений одновременно имеет накладные расходы на производительность. Когда эти потери меньше, чем накладные расходы на поиск значения HashMap, будет отражено преимущество в производительности входного набора. Например, в приведенном выше сравнительном тесте, когда ключ представляет собой простейшую числовую строку, keySet может быть более эффективным, занимая на 10% меньше времени, чем enterSet. Вообще говоря, рекомендуется использовать входной набор. Поскольку, когда ключ очень простой, его производительность может быть немного ниже, чем у keySet, но им можно управлять, поскольку ключ становится более сложным, преимущества входаSet будут четко отражены; Конечно, мы можем выбирать в соответствии с реальной ситуацией.
2. При перемещении только по ключам метод keySet более подходит, поскольку входной набор также удаляет бесполезные значения, что приводит к потере производительности и пространства. В приведенных выше результатах теста keySet занимает на 23% меньше времени, чем метод enterSet.
3. При перемещении только значения лучшим выбором является использование метода vlaues, метод inputSet немного лучше, чем метод keySet.
4. Среди различных методов записи обхода рекомендуется использовать следующий метод записи, который несколько более эффективен:
Скопируйте код кода следующим образом:
for (Строковый ключ: map.keySet()) {
значение = map.get(ключ);
}
for (Entry<String, String> запись: map.entrySet()) {
ключ = запись.getKey();
значение = запись.getValue();
}
for (Строковое значение: map.values()) {
}
3.2 Если вы используете TreeMap
1. При одновременном обходе ключа и значения, в отличие от HashMap, производительность входаSet намного выше, чем у keySet. Это определяется эффективностью запросов TreeMap. Другими словами, стоимость поиска значения в TreeMap относительно велика, что значительно превышает стоимость одновременного получения всех ключей и значений из входного набора. Поэтому настоятельно рекомендуется использовать метод enterSet при обходе TreeMap.
2. При перемещении только по ключам метод keySet более подходит, поскольку входной набор также удаляет бесполезные значения, что приводит к потере производительности и пространства. В приведенных выше результатах теста keySet занимает на 24% меньше времени, чем метод enterSet.
3. При перемещении только по значению лучшим выбором является использование метода vlaues, а входной набор также явно лучше, чем метод keySet.
4. Среди различных методов записи обхода рекомендуется использовать следующий метод записи, который несколько более эффективен:
Скопируйте код кода следующим образом:
for (Строковый ключ: map.keySet()) {
значение = map.get(ключ);
}
for (Entry<String, String> запись: map.entrySet()) {
ключ = запись.getKey();
значение = запись.getValue();
}
for (Строковое значение: map.values()) {
}