GetVersion и GetVersionEx заменялись вспомогательными функциями для различных версий . Вы хотите IsWindows10OrGreater . Они могут быть найдены в VersionHelpers.h .
IsWindows10OrGreater доступен только в последней версии SDK / Visual Studio 2015. Вы можете использовать IsWindowsVersionOrGreater в общем случае. Например, в моем ящике 7 я получаю TRUE для IsWindowsVersionOrGreater (6, 0, 0) .
Помните, что параметры, которые эта функция принимает, относятся к номеру сборки Windows и НЕму маркетинговому имени. Так что Windows 8 - это сборка 6.2. Windows 7 - 6.0 и т. Д.
Вторая реализация будет включать в себя множественные изменения размера HashMap.
Я определил это, просто запустив его в отладчике и разбив его каждый раз при изменении размера хэш-карты. Сначала я изменил код, который вы опубликовали, чтобы скомпилировать его в моей системе:
import java.util.*;
import java.util.stream.*;
class Test {
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
for(int i=0; i<100000; i++) {
list.add(new Integer(i));
}
new Test().listToMap(list);
}
Map<Integer, Object> listToMap(List<Object> objs) {
return objs.stream().collect(Collectors.toMap(Object::hashCode, obj -> obj));
}
}
Затем я скомпилировал его и запустил в отладчике, пока не нажмет listToMap
:
$ javac Test.java && jdb Test
Initializing jdb ...
> stop in Test.listToMap
Deferring breakpoint Test.listToMap.
It will be set after the class is loaded.
> run
run Test
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint Test.listToMap
Breakpoint hit: "thread=main", Test.listToMap(), line=14 bci=0
14 return objs.stream().collect(Collectors.toMap(Object::hashCode, obj -> obj));
main[1]
Затем я установил точку останова в java.util.HashMap.resize
и продолжил:
main[1] stop in java.util.HashMap.resize
Set breakpoint java.util.HashMap.resize
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1]
и cont
появился еще до тех пор, пока мне не стало скучно:
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(),
line=678 bci=0
main[1] print size
size = 3073
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] print size
size = 6145
main[1] cont
>
Breakpoint hit: "thread=main", java.util.HashMap.resize(), line=678 bci=0
main[1] print size
size = 12289
Итак, да: it наиболее определенно сохраняет изменения размера снова и снова.
Будет ли вторая реализация включать несколько изменений размера HashMap или оптимизирована для выделения достаточно памяти?
В вашем коде первый. См. https://stackoverflow.com/a/51333961/139985
Стоит отметить, что для вашей текущей реализации:
- Большая часть дополнительная память, потребляемая путем изменения размера, будет восстановлена при следующем запуске GC.
- После окончания
collect
вы все равно можете получить основной массив хэшей, который в два раза больше. Память «впустую» может составлять до 8 байтов на запись в таблице, но в среднем она будет 4 байта на запись.- Тем не менее, узлы ввода хешей будут самым большим потребителем памяти в a
HashMap
. Каждая запись потребляет примерно 32 байта ... в дополнение к пространству, используемому для представления ключа и значения.(Приведенные выше числа предполагают 64-битные ссылки.)
В качестве альтернативы, если вы используете перегрузку аргумента 4 в
toMap()
, вы можете предоставитьSupplier
для созданияMap
для заполнения. Это позволяет сделать следующее:
- Выделить
HashMap
с начальной мощностью, достаточной для того, чтобы избежать изменения размера, но не слишком большого.- Используйте (гипотетический) альтернативная реализация
Map
, которая использует меньше памяти для каждой записи, чемHashMap
.- Создайте оболочку, чтобы заполнить объект, похожий на карту, который не реализует
Map<K,V>
... для вашегоK
иV
. (Например, вы могли бы использоватьTLongObjectHashMap
в библиотеке GNU Trove.)(В последних двух случаях целью является найти
Map
или «map-like «класс, который использует меньше памяти (для ваших типовK
иV
), но все же имеет соответствующую производительность для поиска.)
Подводя итог тому, что говорили все остальные и добавив немного, вот способ сделать это, используя пользовательский Collector
. Однако вы должны иметь в виду две вещи:
Collector
, что выделяет HashMap
с предопределенным размером, будет перераспределять, если указанный Collector
используется в параллельном режиме. Поэтому Collector
, который я предлагаю, не поддерживает параллельный сбор. Сказав это, вы можете записать следующий общий Collector
s:
public class ExtraCollectors {
public static <T, K, V> Collector<T, ?, HashMap<K, V>> toSizedMap(
Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper, int size) {
return toSequentialMap(
() -> com.google.common.collect.Maps.newHashMapWithExpectedSize(size),
keyMapper, valueMapper, Collector.Characteristics.UNORDERED
);
}
public static <T, K, V, M extends Map<K, V>> Collector<T, ?, M> toSequentialMap(
Supplier<M> mapSupplier, Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper, Collector.Characteristics... characteristics) {
return Collector.of(
mapSupplier,
(map, element) -> map.merge(
keyMapper.apply(element), valueMapper.apply(element), ExtraCollectors::mergeUnsupported
),
ExtraCollectors::combineUnsupported,
characteristics
);
}
private static <T> T mergeUnsupported(T valueA, T valueB) {
throw new UnsupportedOperationException("This Collector does not support merging.");
}
private static <A> A combineUnsupported(A accumulatorA, A accumulatorB) {
throw new UnsupportedOperationException("This Collector does not support parallel streams.");
}
}
Обратите внимание, что я использовал Guava Maps.newHashMapWithExpectedSize , чтобы вы получили HashMap
с нужным вам размером (примерно то, что Andreas объяснил в своем комментарии к вашему вопросу ). Если у вас нет зависимости от Guava (и вы не хотите этого делать), вы можете просто скопировать метод Maps.capacity на вашу кодовую базу.
Используя метод ExtraCollectors.toSizedMap()
, определенный выше, ваш метод преобразования будет выглядеть следующим образом:
Map<Long, KeyedObject> listToMap(List<? extends KeyedObject> objs) {
return objs.stream().collect(ExtraCollectors.toSizedMap(KeyedObject::getKey, obj -> obj, objs.size()));
}
Тем не менее, если вам действительно нужна максимальная производительность (по цене повторного использования), вы можете пропустить API Stream
, и примените ваше решение 1, но с Maps.newHashMapWithExpectedSize
, чтобы получить размер HashMap
.