Подводя итог тому, что говорили все остальные и добавив немного, вот способ сделать это, используя пользовательский Collector
. Однако вы должны иметь в виду две вещи:
Collector
, что выделяет HashMap
с предопределенным размером, будет перераспределять, если указанный Collector
используется в параллельном режиме. Поэтому Collector
, который я предлагаю, не поддерживает параллельный сбор. Сказав это, вы можете записать следующий общий Collector
s:
public class ExtraCollectors {
public static Collector> 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 > Collector toSequentialMap(
Supplier 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 mergeUnsupported(T valueA, T valueB) {
throw new UnsupportedOperationException("This Collector does not support merging.");
}
private static 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 listToMap(List extends KeyedObject> objs) {
return objs.stream().collect(ExtraCollectors.toSizedMap(KeyedObject::getKey, obj -> obj, objs.size()));
}
Тем не менее, если вам действительно нужна максимальная производительность (по цене повторного использования), вы можете пропустить API Stream
, и примените ваше решение 1, но с Maps.newHashMapWithExpectedSize
, чтобы получить размер HashMap
.
Как насчет использования TimerTask и Timer для планирования повторного выполнения (каждые 5 секунд)? В зависимости от интервала вы выполняете либо задание, выполненное в настоящее время в handler1
, либо задание в handler2
, либо оба. Возможно, вы можете разбить эти задания на функции, которые вызываются из метода run TimerTask's
. Это позволило бы заданию handler1
выполняться до задания handler2
синхронизированным образом в том же потоке.
Поскольку handler2 выполняется чаще, чем handler1, вы можете использовать счетчик в handler2 для отслеживания того, когда следует запускать события, которые вы планируете инициировать в handler1.
handler2.postDelayed(new Runnable() {
int count = 0;
@Override
public void run() {
if (count == 0) {
// fire handler1 events
}
count = (count+1) % 6;
}
}, 5);
Чтобы убедиться, что handler1 всегда выполняется перед handler2 , вы можете использовать следующий подход:
Каждый раз, когда handler1 завершив то, что должен сделать, он [выдаст сигнал Service
, в котором оба Handler
работают, чтобы] немедленно запустить handler2 .
handler2 (или Service
) будет разрешено запланировать повторное выполнение Runnable
на следующие пять раундов. Вы можете использовать некоторый тип обратного отсчета, чтобы отслеживать оставшиеся раунды. Последний «независимо управляемый» раунд начнется примерно через 25 секунд после начального раунда. После этого раунда до handler1 может быть выдана следующая команда запуска для handler2 .