/* * Copyright 2015 Goldman Sachs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gs.collections.impl.parallel; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; import java.util.List; import java.util.RandomAccess; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.gs.collections.api.block.function.Function; import com.gs.collections.api.block.function.Function0; import com.gs.collections.api.block.function.Function2; import com.gs.collections.api.block.function.primitive.DoubleFunction; import com.gs.collections.api.block.function.primitive.DoubleFunction0; import com.gs.collections.api.block.function.primitive.FloatFunction; import com.gs.collections.api.block.function.primitive.IntFunction; import com.gs.collections.api.block.function.primitive.LongFunction; import com.gs.collections.api.block.predicate.Predicate; import com.gs.collections.api.block.procedure.Procedure; import com.gs.collections.api.block.procedure.Procedure2; import com.gs.collections.api.block.procedure.primitive.ObjectIntProcedure; import com.gs.collections.api.block.procedure.primitive.ObjectLongProcedure; import com.gs.collections.api.list.ListIterable; import com.gs.collections.api.map.MutableMap; import com.gs.collections.api.map.primitive.ObjectDoubleMap; import com.gs.collections.api.map.primitive.ObjectLongMap; import com.gs.collections.api.multimap.MutableMultimap; import com.gs.collections.api.tuple.primitive.DoubleDoublePair; import com.gs.collections.impl.block.factory.Functions0; import com.gs.collections.impl.block.procedure.MultimapPutProcedure; import com.gs.collections.impl.block.procedure.MutatingAggregationProcedure; import com.gs.collections.impl.block.procedure.NonMutatingAggregationProcedure; import com.gs.collections.impl.factory.Maps; import com.gs.collections.impl.list.fixed.ArrayAdapter; import com.gs.collections.impl.map.mutable.ConcurrentHashMap; import com.gs.collections.impl.map.mutable.UnifiedMap; import com.gs.collections.impl.map.mutable.primitive.ObjectDoubleHashMap; import com.gs.collections.impl.map.mutable.primitive.ObjectLongHashMap; import com.gs.collections.impl.multimap.list.SynchronizedPutFastListMultimap; import com.gs.collections.impl.tuple.primitive.PrimitiveTuples; import com.gs.collections.impl.utility.Iterate; import static com.gs.collections.impl.factory.Iterables.iList; /** * The ParallelIterate class contains several parallel algorithms that work with Collections. All of the higher * level parallel algorithms depend on the basic parallel algorithm named {@code forEach}. The forEach algorithm employs * a batching fork and join approach. * <p> * All Collections that are not either a {@link RandomAccess} or {@link List} are first converted to a Java array * using {@link Iterate#toArray(Iterable)}, and then run with one of the {@code ParallelArrayIterate.forEach} methods. * * @see ParallelArrayIterate */ public final class ParallelIterate { static final int DEFAULT_MIN_FORK_SIZE = 10000; static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors(); static final int TASK_RATIO = 2; static final int DEFAULT_PARALLEL_TASK_COUNT = ParallelIterate.getDefaultTaskCount(); static final ExecutorService EXECUTOR_SERVICE = ParallelIterate.newPooledExecutor(ParallelIterate.class.getSimpleName(), true); private ParallelIterate() { throw new AssertionError("Suppress default constructor for noninstantiability"); } static boolean isExecutorShutdown() { return ParallelIterate.EXECUTOR_SERVICE.isShutdown(); } static void shutdownExecutor() { ParallelIterate.EXECUTOR_SERVICE.shutdown(); } /** * Iterate over the collection specified, in parallel batches using default runtime parameter values. The * {@code ObjectIntProcedure} used must be stateless, or use concurrent aware objects if they are to be shared. * <p> * e.g. * <pre> * {@code final Map<Integer, Object> chm = new ConcurrentHashMap<Integer, Object>();} * ParallelIterate.<b>forEachWithIndex</b>(collection, new ObjectIntProcedure() * { * public void value(Object object, int index) * { * chm.put(index, object); * } * }); * </pre> */ public static <T> void forEachWithIndex( Iterable<T> iterable, ObjectIntProcedure<? super T> objectIntProcedure) { ParallelIterate.forEachWithIndex(iterable, objectIntProcedure, ParallelIterate.EXECUTOR_SERVICE); } /** * Iterate over the collection specified in parallel batches using the default runtime parameters. The * ObjectIntProcedure used must be stateless, or use concurrent aware objects if they are to be shared. The code * is executed against the specified executor. * <p> * <pre>e.g. * {@code final Map<Integer, Object> chm = new ConcurrentHashMap<Integer, Object>();} * ParallelIterate.<b>forEachWithIndex</b>(collection, new ObjectIntProcedure() * { * public void value(Object object, int index) * { * chm.put(index, object); * } * }, executor); * </pre> * * @param executor Use this executor for all execution. */ public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndex( Iterable<T> iterable, BT procedure, Executor executor) { ParallelIterate.forEachWithIndex( iterable, new PassThruObjectIntProcedureFactory<BT>(procedure), new PassThruCombiner<BT>(), executor); } /** * Iterate over the collection specified in parallel batches. The * ObjectIntProcedure used must be stateless, or use concurrent aware objects if they are to be shared. The * specified minimum fork size and task count are used instead of the default values. * * @param minForkSize Only run in parallel if input collection is longer than this. * @param taskCount How many parallel tasks to submit to the executor. * @see #forEachWithIndex(Iterable, ObjectIntProcedure) */ public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndex( Iterable<T> iterable, BT procedure, int minForkSize, int taskCount) { ParallelIterate.forEachWithIndex( iterable, new PassThruObjectIntProcedureFactory<BT>(procedure), new PassThruCombiner<BT>(), minForkSize, taskCount); } public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndex( Iterable<T> iterable, ObjectIntProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, Executor executor) { int taskCount = Math.max( ParallelIterate.DEFAULT_PARALLEL_TASK_COUNT, Iterate.sizeOf(iterable) / ParallelIterate.DEFAULT_MIN_FORK_SIZE); ParallelIterate.forEachWithIndex( iterable, procedureFactory, combiner, ParallelIterate.DEFAULT_MIN_FORK_SIZE, taskCount, executor); } public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndex( Iterable<T> iterable, ObjectIntProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount) { ParallelIterate.forEachWithIndex(iterable, procedureFactory, combiner, minForkSize, taskCount, ParallelIterate.EXECUTOR_SERVICE); } public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndex( Iterable<T> iterable, ObjectIntProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount, Executor executor) { if (Iterate.notEmpty(iterable)) { if (iterable instanceof RandomAccess || iterable instanceof ListIterable && iterable instanceof List) { ParallelIterate.forEachWithIndexInListOnExecutor( (List<T>) iterable, procedureFactory, combiner, minForkSize, taskCount, executor); } else { ParallelIterate.forEachWithIndexInListOnExecutor( ArrayAdapter.adapt((T[]) Iterate.toArray(iterable)), procedureFactory, combiner, minForkSize, taskCount, executor); } } } public static <T, BT extends ObjectIntProcedure<? super T>> void forEachWithIndexInListOnExecutor( List<T> list, ObjectIntProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount, Executor executor) { int size = list.size(); if (size < minForkSize) { BT procedure = procedureFactory.create(); Iterate.forEachWithIndex(list, procedure); if (combiner.useCombineOne()) { combiner.combineOne(procedure); } else { combiner.combineAll(iList(procedure)); } } else { int threadCount = Math.min(size, taskCount); ObjectIntProcedureFJTaskRunner<T, BT> runner = new ObjectIntProcedureFJTaskRunner<T, BT>(combiner, threadCount); runner.executeAndCombine(executor, procedureFactory, list); } } /** * Iterate over the collection specified in parallel batches using default runtime parameter values. The * {@code Procedure} used must be stateless, or use concurrent aware objects if they are to be shared. * <p> * e.g. * <pre> * {@code final Map<Object, Boolean> chm = new ConcurrentHashMap<Object, Boolean>();} * ParallelIterate.<b>forEach</b>(collection, new Procedure() * { * public void value(Object object) * { * chm.put(object, Boolean.TRUE); * } * }); * </pre> */ public static <T> void forEach(Iterable<T> iterable, Procedure<? super T> procedure) { ParallelIterate.forEach(iterable, procedure, ParallelIterate.EXECUTOR_SERVICE); } /** * Iterate over the collection specified in parallel batches using default runtime parameter values. The * {@code Procedure} used must be stateless, or use concurrent aware objects if they are to be shared. * <p> * e.g. * <pre> * {@code final Map<Object, Boolean> chm = new ConcurrentHashMap<Object, Boolean>();} * ParallelIterate.<b>forEachBatchSize</b>(collection, new Procedure() * { * public void value(Object object) * { * chm.put(object, Boolean.TRUE); * } * }, 100); * </pre> */ public static <T> void forEach(Iterable<T> iterable, Procedure<? super T> procedure, int batchSize) { ParallelIterate.forEach(iterable, procedure, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T> void forEach(Iterable<T> iterable, Procedure<? super T> procedure, int batchSize, Executor executor) { ParallelIterate.forEach(iterable, procedure, batchSize, ParallelIterate.calculateTaskCount(iterable, batchSize), executor); } /** * Iterate over the collection specified in parallel batches using default runtime parameter values * and the specified executor. * The {@code Procedure} used must be stateless, or use concurrent aware objects if they are to be shared. * * @param executor Use this executor for all execution. * @see #forEach(Iterable, Procedure) */ public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, BT procedure, Executor executor) { ParallelIterate.forEach( iterable, new PassThruProcedureFactory<BT>(procedure), new PassThruCombiner<BT>(), executor); } /** * Iterate over the collection specified in parallel batches using the specified minimum fork and task count sizes. * The {@code Procedure} used must be stateless, or use concurrent aware objects if they are to be shared. * * @param minForkSize Only run in parallel if input collection is longer than this. * @param taskCount How many parallel tasks to submit to the executor. * TODO: How does the taskCount relate to the number of threads in the executor? * @see #forEach(Iterable, Procedure) */ public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, BT procedure, int minForkSize, int taskCount) { ParallelIterate.forEach(iterable, procedure, minForkSize, taskCount, ParallelIterate.EXECUTOR_SERVICE); } public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, BT procedure, int minForkSize, int taskCount, Executor executor) { ParallelIterate.forEach( iterable, new PassThruProcedureFactory<BT>(procedure), new PassThruCombiner<BT>(), minForkSize, taskCount, executor); } public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, Executor executor) { ParallelIterate.forEach(iterable, procedureFactory, combiner, ParallelIterate.DEFAULT_MIN_FORK_SIZE, executor); } public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner) { ParallelIterate.forEach(iterable, procedureFactory, combiner, ParallelIterate.EXECUTOR_SERVICE); } /** * Iterate over the collection specified in parallel batches using the default values for the task size. The * ProcedureFactory can create stateful closures that will be collected and combined using the specified Combiner. * <p> * <pre>e.g. The <b>ParallelIterate.select()</b> implementation * <p> * {@code CollectionCombiner<T, SelectProcedure<T>> combiner = CollectionCombiner.forSelect(collection);} * ParallelIterate.<b>forEach</b>(collection,{@code new SelectProcedureFactory<T>(predicate, taskSize), combiner, 1000);} * </pre> */ @SuppressWarnings("JavaDoc") public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int batchSize) { ParallelIterate.forEach(iterable, procedureFactory, combiner, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int batchSize, Executor executor) { ParallelIterate.forEach(iterable, procedureFactory, combiner, batchSize, ParallelIterate.calculateTaskCount(iterable, batchSize), executor); } /** * Iterate over the collection specified in parallel batches using the default values for the task size. The * ProcedureFactory can create stateful closures that will be collected and combined using the specified Combiner. * <p> * <pre>e.g. The <b>ParallelIterate.select()</b> implementation * <p> * int taskCount = Math.max(DEFAULT_PARALLEL_TASK_COUNT, collection.size() / DEFAULT_MIN_FORK_SIZE); * final int taskSize = collection.size() / taskCount / 2; * {@code CollectionCombiner<T, SelectProcedure<T>> combiner = CollectionCombiner.forSelect(collection);} * ParallelIterate.<b>forEach</b>(collection,{@code new SelectProcedureFactory<T>(predicate, taskSize), combiner, DEFAULT_MIN_FORK_SIZE, taskCount);} * </pre> */ @SuppressWarnings("JavaDoc") public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount) { ParallelIterate.forEach(iterable, procedureFactory, combiner, minForkSize, taskCount, ParallelIterate.EXECUTOR_SERVICE); } public static <T, BT extends Procedure<? super T>> void forEach( Iterable<T> iterable, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount, Executor executor) { if (Iterate.notEmpty(iterable)) { if (iterable instanceof BatchIterable) { ParallelIterate.forEachInBatchWithExecutor( (BatchIterable<T>) iterable, procedureFactory, combiner, minForkSize, taskCount, executor); } else if ((iterable instanceof RandomAccess || iterable instanceof ListIterable) && iterable instanceof List) { ParallelIterate.forEachInListOnExecutor( (List<T>) iterable, procedureFactory, combiner, minForkSize, taskCount, executor); } else { ParallelArrayIterate.forEachOn( (T[]) Iterate.toArray(iterable), procedureFactory, combiner, minForkSize, taskCount, executor); } } } public static <T, BT extends Procedure<? super T>> void forEachInListOnExecutor( List<T> list, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount, Executor executor) { int size = list.size(); if (size < minForkSize) { BT procedure = procedureFactory.create(); Iterate.forEach(list, procedure); if (combiner.useCombineOne()) { combiner.combineOne(procedure); } else { combiner.combineAll(iList(procedure)); } } else { int threadCount = Math.min(size, taskCount); ProcedureFJTaskRunner<T, BT> runner = new ProcedureFJTaskRunner<T, BT>(combiner, threadCount); runner.executeAndCombine(executor, procedureFactory, list); } } public static <T, BT extends Procedure<? super T>> void forEachInBatchWithExecutor( BatchIterable<T> set, ProcedureFactory<BT> procedureFactory, Combiner<BT> combiner, int minForkSize, int taskCount, Executor executor) { int size = set.size(); if (size < minForkSize) { BT procedure = procedureFactory.create(); set.forEach(procedure); if (combiner.useCombineOne()) { combiner.combineOne(procedure); } else { combiner.combineAll(iList(procedure)); } } else { int threadCount = Math.min(size, Math.min(taskCount, set.getBatchCount((int) Math.ceil((double) size / (double) taskCount)))); BatchIterableProcedureFJTaskRunner<T, BT> runner = new BatchIterableProcedureFJTaskRunner<T, BT>(combiner, threadCount); runner.executeAndCombine(executor, procedureFactory, set); } } /** * Same effect as {@link Iterate#select(Iterable, Predicate)}, but executed in parallel batches. * * @return The selected elements. The Collection will be of the same type as the input (List or Set) * and will be in the same order as the input (if it is an ordered collection). * @see ParallelIterate#select(Iterable, Predicate, boolean) */ public static <T> Collection<T> select( Iterable<T> iterable, Predicate<? super T> predicate) { return ParallelIterate.select(iterable, predicate, false); } /** * Same effect as {@link Iterate#select(Iterable, Predicate)}, but executed in parallel batches, * and with a potentially reordered result. * * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The selected elements. The Collection will be of the same type (List or Set) as the input. */ public static <T> Collection<T> select( Iterable<T> iterable, Predicate<? super T> predicate, boolean allowReorderedResult) { return ParallelIterate.select(iterable, predicate, null, allowReorderedResult); } /** * Same effect as {@link Iterate#select(Iterable, Predicate)}, but executed in parallel batches, * and writing output into the specified collection. * * @param target Where to write the output. * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The 'target' collection, with the selected elements added. */ public static <T, R extends Collection<T>> R select( Iterable<T> iterable, Predicate<? super T> predicate, R target, boolean allowReorderedResult) { return ParallelIterate.select( iterable, predicate, target, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE, allowReorderedResult); } /** * Same effect as {@link Iterate#select(Iterable, Predicate)}, but executed in parallel batches, * and writing output into the specified collection. * * @param target Where to write the output. * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The 'target' collection, with the selected elements added. */ public static <T, R extends Collection<T>> R select( Iterable<T> iterable, Predicate<? super T> predicate, R target, int batchSize, Executor executor, boolean allowReorderedResult) { FastListSelectProcedureCombiner<T> combiner = new FastListSelectProcedureCombiner<T>(iterable, target, 10, allowReorderedResult); FastListSelectProcedureFactory<T> procedureFactory = new FastListSelectProcedureFactory<T>(predicate, batchSize); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, ParallelIterate.calculateTaskCount(iterable, batchSize), executor); return (R) combiner.getResult(); } private static <T> int calculateTaskCount(Iterable<T> iterable, int batchSize) { if (iterable instanceof BatchIterable<?>) { return ParallelIterate.calculateTaskCount((BatchIterable<?>) iterable, batchSize); } return ParallelIterate.calculateTaskCount(Iterate.sizeOf(iterable), batchSize); } private static <T> int calculateTaskCount(BatchIterable<T> batchIterable, int batchSize) { return Math.max(2, batchIterable.getBatchCount(batchSize)); } private static int calculateTaskCount(int size, int batchSize) { return Math.max(2, size / batchSize); } /** * Same effect as {@link Iterate#reject(Iterable, Predicate)}, but executed in parallel batches. * * @return The rejected elements. The Collection will be of the same type as the input (List or Set) * and will be in the same order as the input (if it is an ordered collection). * @see ParallelIterate#reject(Iterable, Predicate, boolean) */ public static <T> Collection<T> reject( Iterable<T> iterable, Predicate<? super T> predicate) { return ParallelIterate.reject(iterable, predicate, false); } /** * Same effect as {@link Iterate#reject(Iterable, Predicate)}, but executed in parallel batches, * and with a potentially reordered result. * * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The rejected elements. The Collection will be of the same type (List or Set) as the input. */ public static <T> Collection<T> reject( Iterable<T> iterable, Predicate<? super T> predicate, boolean allowReorderedResult) { return ParallelIterate.reject(iterable, predicate, null, allowReorderedResult); } /** * Same effect as {@link Iterate#reject(Iterable, Predicate)}, but executed in parallel batches, * and writing output into the specified collection. * * @param target Where to write the output. * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The 'target' collection, with the rejected elements added. */ public static <T, R extends Collection<T>> R reject( Iterable<T> iterable, Predicate<? super T> predicate, R target, boolean allowReorderedResult) { return ParallelIterate.reject( iterable, predicate, target, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE, allowReorderedResult); } public static <T, R extends Collection<T>> R reject( Iterable<T> iterable, Predicate<? super T> predicate, R target, int batchSize, Executor executor, boolean allowReorderedResult) { FastListRejectProcedureCombiner<T> combiner = new FastListRejectProcedureCombiner<T>(iterable, target, 10, allowReorderedResult); FastListRejectProcedureFactory<T> procedureFactory = new FastListRejectProcedureFactory<T>(predicate, batchSize); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, ParallelIterate.calculateTaskCount(iterable, batchSize), executor); return (R) combiner.getResult(); } /** * Same effect as {@link Iterate#count(Iterable, Predicate)}, but executed in parallel batches. * * @return The number of elements which satisfy the predicate. */ public static <T> int count(Iterable<T> iterable, Predicate<? super T> predicate) { return ParallelIterate.count(iterable, predicate, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); } /** * Same effect as {@link Iterate#count(Iterable, Predicate)}, but executed in parallel batches. * * @return The number of elements which satisfy the predicate. */ public static <T> int count(Iterable<T> iterable, Predicate<? super T> predicate, int batchSize, Executor executor) { CountCombiner<T> combiner = new CountCombiner<T>(); CountProcedureFactory<T> procedureFactory = new CountProcedureFactory<T>(predicate); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, executor); return combiner.getCount(); } /** * Same effect as {@link Iterate#collect(Iterable, Function)}, * but executed in parallel batches. * * @return The collected elements. The Collection will be of the same type as the input (List or Set) * and will be in the same order as the input (if it is an ordered collection). * @see ParallelIterate#collect(Iterable, Function, boolean) */ public static <T, V> Collection<V> collect( Iterable<T> iterable, Function<? super T, V> function) { return ParallelIterate.collect(iterable, function, false); } /** * Same effect as {@link Iterate#collect(Iterable, Function)}, but executed in parallel batches, * and with potentially reordered result. * * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The collected elements. The Collection will be of the same type * (List or Set) as the input. */ public static <T, V> Collection<V> collect( Iterable<T> iterable, Function<? super T, V> function, boolean allowReorderedResult) { return ParallelIterate.collect(iterable, function, null, allowReorderedResult); } /** * Same effect as {@link Iterate#collect(Iterable, Function)}, but executed in parallel batches, * and writing output into the specified collection. * * @param target Where to write the output. * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The 'target' collection, with the collected elements added. */ public static <T, V, R extends Collection<V>> R collect( Iterable<T> iterable, Function<? super T, V> function, R target, boolean allowReorderedResult) { return ParallelIterate.collect( iterable, function, target, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE, allowReorderedResult); } public static <T, V, R extends Collection<V>> R collect( Iterable<T> iterable, Function<? super T, V> function, R target, int batchSize, Executor executor, boolean allowReorderedResult) { int size = Iterate.sizeOf(iterable); FastListCollectProcedureCombiner<T, V> combiner = new FastListCollectProcedureCombiner<T, V>(iterable, target, size, allowReorderedResult); int taskCount = ParallelIterate.calculateTaskCount(iterable, batchSize); FastListCollectProcedureFactory<T, V> procedureFactory = new FastListCollectProcedureFactory<T, V>(function, size / taskCount); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, taskCount, executor); return (R) combiner.getResult(); } public static <T, V> Collection<V> flatCollect( Iterable<T> iterable, Function<? super T, Collection<V>> function) { return ParallelIterate.flatCollect(iterable, function, false); } public static <T, V> Collection<V> flatCollect( Iterable<T> iterable, Function<? super T, Collection<V>> function, boolean allowReorderedResult) { return ParallelIterate.flatCollect(iterable, function, null, allowReorderedResult); } public static <T, V, R extends Collection<V>> R flatCollect( Iterable<T> iterable, Function<? super T, Collection<V>> function, R target, boolean allowReorderedResult) { return ParallelIterate.flatCollect( iterable, function, target, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE, allowReorderedResult); } public static <T, V, R extends Collection<V>> R flatCollect( Iterable<T> iterable, Function<? super T, Collection<V>> function, R target, int batchSize, Executor executor, boolean allowReorderedResult) { int size = Iterate.sizeOf(iterable); int taskCount = ParallelIterate.calculateTaskCount(iterable, batchSize); int taskSize = size / taskCount; FlatCollectProcedureCombiner<T, V> combiner = new FlatCollectProcedureCombiner<T, V>(iterable, target, size, allowReorderedResult); FlatCollectProcedureFactory<T, V> procedureFactory = new FlatCollectProcedureFactory<T, V>(function, taskSize); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, taskCount, executor); return (R) combiner.getResult(); } /** * Same effect as {@link Iterate#collectIf(Iterable, Predicate, Function)}, * but executed in parallel batches. * * @return The collected elements. The Collection will be of the same type as the input (List or Set) * and will be in the same order as the input (if it is an ordered collection). * @see ParallelIterate#collectIf(Iterable, Predicate, Function, boolean) */ public static <T, V> Collection<V> collectIf( Iterable<T> iterable, Predicate<? super T> predicate, Function<? super T, V> function) { return ParallelIterate.collectIf(iterable, predicate, function, false); } /** * Same effect as {@link Iterate#collectIf(Iterable, Predicate, Function)}, * but executed in parallel batches, and with potentially reordered results. * * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The collected elements. The Collection will be of the same type * as the input (List or Set) */ public static <T, V> Collection<V> collectIf( Iterable<T> iterable, Predicate<? super T> predicate, Function<? super T, V> function, boolean allowReorderedResult) { return ParallelIterate.collectIf(iterable, predicate, function, null, allowReorderedResult); } /** * Same effect as {@link Iterate#collectIf(Iterable, Predicate, Function)}, * but executed in parallel batches, and writing output into the specified collection. * * @param target Where to write the output. * @param allowReorderedResult If the result can be in a different order. * Allowing reordering may yield faster execution. * @return The 'target' collection, with the collected elements added. */ public static <T, V, R extends Collection<V>> R collectIf( Iterable<T> iterable, Predicate<? super T> predicate, Function<? super T, V> function, R target, boolean allowReorderedResult) { return ParallelIterate.collectIf( iterable, predicate, function, target, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE, allowReorderedResult); } public static <T, V, R extends Collection<V>> R collectIf( Iterable<T> iterable, Predicate<? super T> predicate, Function<? super T, V> function, R target, int batchSize, Executor executor, boolean allowReorderedResult) { FastListCollectIfProcedureCombiner<T, V> combiner = new FastListCollectIfProcedureCombiner<T, V>(iterable, target, 10, allowReorderedResult); FastListCollectIfProcedureFactory<T, V> procedureFactory = new FastListCollectIfProcedureFactory<T, V>(function, predicate, batchSize); ParallelIterate.forEach( iterable, procedureFactory, combiner, batchSize, ParallelIterate.calculateTaskCount(iterable, batchSize), executor); return (R) combiner.getResult(); } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function) { return ParallelIterate.groupBy(iterable, function, ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); } public static <T, K, V> MutableMap<K, V> aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator) { return ParallelIterate.aggregateBy( iterable, groupBy, zeroValueFactory, nonMutatingAggregator, ParallelIterate.DEFAULT_MIN_FORK_SIZE); } public static <T, K, V, R extends MutableMap<K, V>> R aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator, R mutableMap) { return ParallelIterate.aggregateBy( iterable, groupBy, zeroValueFactory, nonMutatingAggregator, mutableMap, ParallelIterate.DEFAULT_MIN_FORK_SIZE); } public static <T, K, V> MutableMap<K, V> aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator, int batchSize) { return ParallelIterate.aggregateBy( iterable, groupBy, zeroValueFactory, nonMutatingAggregator, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T, K, V, R extends MutableMap<K, V>> R aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator, R mutableMap, int batchSize) { return ParallelIterate.aggregateBy( iterable, groupBy, zeroValueFactory, nonMutatingAggregator, mutableMap, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T, K, V> MutableMap<K, V> aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator, int batchSize, Executor executor) { return ParallelIterate.aggregateBy( iterable, groupBy, zeroValueFactory, nonMutatingAggregator, ConcurrentHashMap.<K, V>newMap(), batchSize, executor); } public static <T, K, V, R extends MutableMap<K, V>> R aggregateBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Function2<? super V, ? super T, ? extends V> nonMutatingAggregator, R mutableMap, int batchSize, Executor executor) { NonMutatingAggregationProcedure<T, K, V> nonMutatingAggregationProcedure = new NonMutatingAggregationProcedure<T, K, V>(mutableMap, groupBy, zeroValueFactory, nonMutatingAggregator); ParallelIterate.forEach( iterable, new PassThruProcedureFactory<Procedure<T>>(nonMutatingAggregationProcedure), Combiners.<Procedure<T>>passThru(), batchSize, executor); return mutableMap; } public static <T, K, V> MutableMap<K, V> aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator) { return ParallelIterate.aggregateInPlaceBy( iterable, groupBy, zeroValueFactory, mutatingAggregator, ParallelIterate.DEFAULT_MIN_FORK_SIZE); } public static <T, K, V, R extends MutableMap<K, V>> R aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator, R mutableMap) { return ParallelIterate.aggregateInPlaceBy( iterable, groupBy, zeroValueFactory, mutatingAggregator, mutableMap, ParallelIterate.DEFAULT_MIN_FORK_SIZE); } public static <T, K, V> MutableMap<K, V> aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator, int batchSize) { return ParallelIterate.aggregateInPlaceBy( iterable, groupBy, zeroValueFactory, mutatingAggregator, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T, K, V, R extends MutableMap<K, V>> R aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator, R mutableMap, int batchSize) { return ParallelIterate.aggregateInPlaceBy( iterable, groupBy, zeroValueFactory, mutatingAggregator, mutableMap, batchSize, ParallelIterate.EXECUTOR_SERVICE); } public static <T, K, V> MutableMap<K, V> aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator, int batchSize, Executor executor) { MutableMap<K, V> map = ConcurrentHashMap.newMap(); MutatingAggregationProcedure<T, K, V> mutatingAggregationProcedure = new MutatingAggregationProcedure<T, K, V>(map, groupBy, zeroValueFactory, mutatingAggregator); ParallelIterate.forEach( iterable, new PassThruProcedureFactory<Procedure<T>>(mutatingAggregationProcedure), Combiners.<Procedure<T>>passThru(), batchSize, executor); return map; } public static <T, K, V, R extends MutableMap<K, V>> R aggregateInPlaceBy( Iterable<T> iterable, Function<? super T, ? extends K> groupBy, Function0<? extends V> zeroValueFactory, Procedure2<? super V, ? super T> mutatingAggregator, R mutableMap, int batchSize, Executor executor) { MutatingAggregationProcedure<T, K, V> mutatingAggregationProcedure = new MutatingAggregationProcedure<T, K, V>(mutableMap, groupBy, zeroValueFactory, mutatingAggregator); ParallelIterate.forEach( iterable, new PassThruProcedureFactory<Procedure<T>>(mutatingAggregationProcedure), Combiners.<Procedure<T>>passThru(), batchSize, executor); return mutableMap; } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V, R extends MutableMultimap<K, V>> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function, R concurrentMultimap) { return ParallelIterate.groupBy(iterable, function, concurrentMultimap, ParallelIterate.DEFAULT_MIN_FORK_SIZE); } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V, R extends MutableMultimap<K, V>> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function, R concurrentMultimap, int batchSize) { return ParallelIterate.groupBy(iterable, function, concurrentMultimap, batchSize, ParallelIterate.EXECUTOR_SERVICE); } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function, int batchSize) { return ParallelIterate.groupBy(iterable, function, batchSize, ParallelIterate.EXECUTOR_SERVICE); } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function, int batchSize, Executor executor) { return ParallelIterate.groupBy(iterable, function, SynchronizedPutFastListMultimap.<K, V>newMultimap(), batchSize, executor); } /** * Same effect as {@link Iterate#groupBy(Iterable, Function)}, * but executed in parallel batches, and writing output into a SynchronizedPutFastListMultimap. */ public static <K, V, R extends MutableMultimap<K, V>> MutableMultimap<K, V> groupBy( Iterable<V> iterable, Function<? super V, ? extends K> function, R concurrentMultimap, int batchSize, Executor executor) { ParallelIterate.forEach( iterable, new PassThruProcedureFactory<Procedure<V>>(new MultimapPutProcedure<K, V>(concurrentMultimap, function)), Combiners.<Procedure<V>>passThru(), batchSize, executor); return concurrentMultimap; } public static <T, V> ObjectDoubleMap<V> sumByDouble( Iterable<T> iterable, Function<T, V> groupBy, DoubleFunction<? super T> function) { ObjectDoubleHashMap<V> result = ObjectDoubleHashMap.newMap(); ParallelIterate.forEach( iterable, new SumByDoubleProcedure<T, V>(groupBy, function), new SumByDoubleCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } public static <T, V> ObjectDoubleMap<V> sumByFloat( Iterable<T> iterable, Function<T, V> groupBy, FloatFunction<? super T> function) { ObjectDoubleHashMap<V> result = ObjectDoubleHashMap.newMap(); ParallelIterate.forEach( iterable, new SumByFloatProcedure<T, V>(groupBy, function), new SumByFloatCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } public static <T, V> ObjectLongMap<V> sumByLong( Iterable<T> iterable, Function<T, V> groupBy, LongFunction<? super T> function) { ObjectLongHashMap<V> result = ObjectLongHashMap.newMap(); ParallelIterate.forEach( iterable, new SumByLongProcedure<T, V>(groupBy, function), new SumByLongCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } public static <T, V> ObjectLongMap<V> sumByInt( Iterable<T> iterable, Function<T, V> groupBy, IntFunction<? super T> function) { ObjectLongHashMap<V> result = ObjectLongHashMap.newMap(); ParallelIterate.forEach( iterable, new SumByIntProcedure<T, V>(groupBy, function), new SumByIntCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } /** * @since 6.0 */ public static <V, T> MutableMap<V, BigDecimal> sumByBigDecimal(Iterable<T> iterable, Function<T, V> groupBy, Function<? super T, BigDecimal> function) { MutableMap<V, BigDecimal> result = UnifiedMap.newMap(); ParallelIterate.forEach( iterable, new SumByBigDecimalProcedure<T, V>(groupBy, function), new SumByBigDecimalCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } /** * @since 6.0 */ public static <V, T> MutableMap<V, BigInteger> sumByBigInteger(Iterable<T> iterable, Function<T, V> groupBy, Function<? super T, BigInteger> function) { MutableMap<V, BigInteger> result = UnifiedMap.newMap(); ParallelIterate.forEach( iterable, new SumByBigIntegerProcedure<T, V>(groupBy, function), new SumByBigIntegerCombiner<T, V>(result), ParallelIterate.DEFAULT_MIN_FORK_SIZE, ParallelIterate.EXECUTOR_SERVICE); return result; } /** * Returns a brand new ExecutorService using the specified poolName with the specified maximum thread pool size. The * same poolName may be used more than once resulting in multiple pools with the same name. * <p> * The pool will be initialised with newPoolSize threads. If that number of threads are in use and another thread * is requested, the pool will reject execution and the submitting thread will execute the task. */ public static ExecutorService newPooledExecutor(int newPoolSize, String poolName, boolean useDaemonThreads) { return new ThreadPoolExecutor( newPoolSize, newPoolSize, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new CollectionsThreadFactory(poolName, useDaemonThreads), new ThreadPoolExecutor.CallerRunsPolicy()); } /** * Returns a brand new ExecutorService using the specified poolName and uses the optional property named * to set the maximum thread pool size. The same poolName may be used more than * once resulting in multiple pools with the same name. */ public static ExecutorService newPooledExecutor(String poolName, boolean useDaemonThreads) { return ParallelIterate.newPooledExecutor(ParallelIterate.getDefaultMaxThreadPoolSize(), poolName, useDaemonThreads); } public static int getDefaultTaskCount() { return ParallelIterate.getDefaultMaxThreadPoolSize() * ParallelIterate.getTaskRatio(); } public static int getDefaultMaxThreadPoolSize() { return Math.min(AVAILABLE_PROCESSORS + 1, 100); } public static int getTaskRatio() { return TASK_RATIO; } private static final class SumByDoubleProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByDoubleProcedure<T, V>> { private final MutableMap<V, DoubleDoublePair> map = Maps.mutable.of(); private final Function<T, V> groupBy; private final DoubleFunction<? super T> function; private SumByDoubleProcedure(Function<T, V> groupBy, DoubleFunction<? super T> function) { this.groupBy = groupBy; this.function = function; } public void value(T each) { V groupKey = this.groupBy.valueOf(each); DoubleDoublePair sumCompensation = this.map.getIfAbsentPut(groupKey, new Function0<DoubleDoublePair>() { public DoubleDoublePair value() { return PrimitiveTuples.pair(0.0d, 0.0d); } }); double sum = sumCompensation.getOne(); double compensation = sumCompensation.getTwo(); double adjustedValue = this.function.doubleValueOf(each) - compensation; double nextSum = sum + adjustedValue; compensation = nextSum - sum - adjustedValue; sum = nextSum; this.map.put(groupKey, PrimitiveTuples.pair(sum, compensation)); } public MutableMap<V, DoubleDoublePair> getResult() { return this.map; } public SumByDoubleProcedure<T, V> create() { return new SumByDoubleProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByDoubleCombiner<T, V> extends AbstractProcedureCombiner<SumByDoubleProcedure<T, V>> { private final ObjectDoubleHashMap<V> result; private final ObjectDoubleHashMap<V> compensation = ObjectDoubleHashMap.newMap(); private SumByDoubleCombiner(ObjectDoubleHashMap<V> result) { super(true); this.result = result; } public void combineOne(SumByDoubleProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, DoubleDoublePair>() { public void value(V each, DoubleDoublePair sumCompensation) { SumByDoubleCombiner.this.result.put(each, sumCompensation.getOne()); SumByDoubleCombiner.this.compensation.put(each, sumCompensation.getTwo()); } }); } else { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, DoubleDoublePair>() { public void value(V each, DoubleDoublePair sumCompensation) { double sum = SumByDoubleCombiner.this.result.get(each); double currentCompensation = SumByDoubleCombiner.this.compensation.getIfAbsentPut(each, new DoubleFunction0() { public double value() { return 0.0d; } }) + sumCompensation.getTwo(); double adjustedValue = sumCompensation.getOne() - currentCompensation; double nextSum = sum + adjustedValue; SumByDoubleCombiner.this.compensation.put(each, nextSum - sum - adjustedValue); SumByDoubleCombiner.this.result.put(each, nextSum); } }); } } } private static final class SumByFloatProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByFloatProcedure<T, V>> { private final MutableMap<V, DoubleDoublePair> map = Maps.mutable.of(); private final Function<T, V> groupBy; private final FloatFunction<? super T> function; private SumByFloatProcedure(Function<T, V> groupBy, FloatFunction<? super T> function) { this.groupBy = groupBy; this.function = function; } public void value(T each) { V groupKey = this.groupBy.valueOf(each); DoubleDoublePair sumCompensation = this.map.getIfAbsentPut(groupKey, new Function0<DoubleDoublePair>() { public DoubleDoublePair value() { return PrimitiveTuples.pair(0.0d, 0.0d); } }); double sum = sumCompensation.getOne(); double compensation = sumCompensation.getTwo(); double adjustedValue = this.function.floatValueOf(each) - compensation; double nextSum = sum + adjustedValue; compensation = nextSum - sum - adjustedValue; sum = nextSum; this.map.put(groupKey, PrimitiveTuples.pair(sum, compensation)); } public MutableMap<V, DoubleDoublePair> getResult() { return this.map; } public SumByFloatProcedure<T, V> create() { return new SumByFloatProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByFloatCombiner<T, V> extends AbstractProcedureCombiner<SumByFloatProcedure<T, V>> { private final ObjectDoubleHashMap<V> result; private final ObjectDoubleHashMap<V> compensation = ObjectDoubleHashMap.newMap(); private SumByFloatCombiner(ObjectDoubleHashMap<V> result) { super(true); this.result = result; } public void combineOne(SumByFloatProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, DoubleDoublePair>() { public void value(V each, DoubleDoublePair sumCompensation) { SumByFloatCombiner.this.result.put(each, sumCompensation.getOne()); SumByFloatCombiner.this.compensation.put(each, sumCompensation.getTwo()); } }); } else { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, DoubleDoublePair>() { public void value(V each, DoubleDoublePair sumCompensation) { double sum = SumByFloatCombiner.this.result.get(each); double currentCompensation = SumByFloatCombiner.this.compensation.getIfAbsentPut(each, new DoubleFunction0() { public double value() { return 0.0d; } }) + sumCompensation.getTwo(); double adjustedValue = sumCompensation.getOne() - currentCompensation; double nextSum = sum + adjustedValue; SumByFloatCombiner.this.compensation.put(each, nextSum - sum - adjustedValue); SumByFloatCombiner.this.result.put(each, nextSum); } }); } } } private static final class SumByLongProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByLongProcedure<T, V>> { private final ObjectLongHashMap<V> map = ObjectLongHashMap.newMap(); private final Function<T, V> groupBy; private final LongFunction<? super T> function; private SumByLongProcedure(Function<T, V> groupBy, LongFunction<? super T> function) { this.groupBy = groupBy; this.function = function; } public void value(T each) { this.map.addToValue(this.groupBy.valueOf(each), this.function.longValueOf(each)); } public ObjectLongHashMap<V> getResult() { return this.map; } public SumByLongProcedure<T, V> create() { return new SumByLongProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByLongCombiner<T, V> extends AbstractProcedureCombiner<SumByLongProcedure<T, V>> { private final ObjectLongHashMap<V> result; private SumByLongCombiner(ObjectLongHashMap<V> result) { super(true); this.result = result; } public void combineOne(SumByLongProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { this.result.putAll(thingToCombine.getResult()); } else { thingToCombine.getResult().forEachKeyValue(new ObjectLongProcedure<V>() { public void value(V each, long value) { SumByLongCombiner.this.result.addToValue(each, value); } }); } } } private static final class SumByIntProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByIntProcedure<T, V>> { private final ObjectLongHashMap<V> map = ObjectLongHashMap.newMap(); private final Function<T, V> groupBy; private final IntFunction<? super T> function; private SumByIntProcedure(Function<T, V> groupBy, IntFunction<? super T> function) { this.groupBy = groupBy; this.function = function; } public void value(T each) { this.map.addToValue(this.groupBy.valueOf(each), (long) this.function.intValueOf(each)); } public ObjectLongHashMap<V> getResult() { return this.map; } public SumByIntProcedure<T, V> create() { return new SumByIntProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByIntCombiner<T, V> extends AbstractProcedureCombiner<SumByIntProcedure<T, V>> { private final ObjectLongHashMap<V> result; private SumByIntCombiner(ObjectLongHashMap<V> result) { super(true); this.result = result; } public void combineOne(SumByIntProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { this.result.putAll(thingToCombine.getResult()); } else { thingToCombine.getResult().forEachKeyValue(new ObjectLongProcedure<V>() { public void value(V each, long value) { SumByIntCombiner.this.result.addToValue(each, value); } }); } } } private static final class SumByBigDecimalProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByBigDecimalProcedure<T, V>> { private final MutableMap<V, BigDecimal> map = UnifiedMap.newMap(); private final Function<T, V> groupBy; private final Function<? super T, BigDecimal> function; private SumByBigDecimalProcedure(Function<T, V> groupBy, Function<? super T, BigDecimal> function) { this.groupBy = groupBy; this.function = function; } public void value(final T each) { this.map.updateValue(this.groupBy.valueOf(each), Functions0.zeroBigDecimal(), new Function<BigDecimal, BigDecimal>() { public BigDecimal valueOf(BigDecimal original) { return original.add(SumByBigDecimalProcedure.this.function.valueOf(each)); } }); } public MutableMap<V, BigDecimal> getResult() { return this.map; } public SumByBigDecimalProcedure<T, V> create() { return new SumByBigDecimalProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByBigDecimalCombiner<T, V> extends AbstractProcedureCombiner<SumByBigDecimalProcedure<T, V>> { private final MutableMap<V, BigDecimal> result; private SumByBigDecimalCombiner(MutableMap<V, BigDecimal> result) { super(true); this.result = result; } public void combineOne(SumByBigDecimalProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { this.result.putAll(thingToCombine.getResult()); } else { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, BigDecimal>() { public void value(V key, final BigDecimal value) { SumByBigDecimalCombiner.this.result.updateValue(key, Functions0.zeroBigDecimal(), new Function<BigDecimal, BigDecimal>() { public BigDecimal valueOf(BigDecimal original) { return original.add(value); } }); } }); } } } private static final class SumByBigIntegerProcedure<T, V> implements Procedure<T>, ProcedureFactory<SumByBigIntegerProcedure<T, V>> { private final MutableMap<V, BigInteger> map = UnifiedMap.newMap(); private final Function<T, V> groupBy; private final Function<? super T, BigInteger> function; private SumByBigIntegerProcedure(Function<T, V> groupBy, Function<? super T, BigInteger> function) { this.groupBy = groupBy; this.function = function; } public void value(final T each) { this.map.updateValue(this.groupBy.valueOf(each), Functions0.zeroBigInteger(), new Function<BigInteger, BigInteger>() { public BigInteger valueOf(BigInteger original) { return original.add(SumByBigIntegerProcedure.this.function.valueOf(each)); } }); } public MutableMap<V, BigInteger> getResult() { return this.map; } public SumByBigIntegerProcedure<T, V> create() { return new SumByBigIntegerProcedure<T, V>(this.groupBy, this.function); } } private static final class SumByBigIntegerCombiner<T, V> extends AbstractProcedureCombiner<SumByBigIntegerProcedure<T, V>> { private final MutableMap<V, BigInteger> result; private SumByBigIntegerCombiner(MutableMap<V, BigInteger> result) { super(true); this.result = result; } public void combineOne(SumByBigIntegerProcedure<T, V> thingToCombine) { if (this.result.isEmpty()) { this.result.putAll(thingToCombine.getResult()); } else { thingToCombine.getResult().forEachKeyValue(new Procedure2<V, BigInteger>() { public void value(V key, final BigInteger value) { SumByBigIntegerCombiner.this.result.updateValue(key, Functions0.zeroBigInteger(), new Function<BigInteger, BigInteger>() { public BigInteger valueOf(BigInteger original) { return original.add(value); } }); } }); } } } }