mapの並列処理化
こんなんができた。スレッドプールを使いまわす関係上、staticなメソッドじゃなくなったことが大きな違い。また、各要素に対してFunctionを並列処理で動かすためのCallableインスタンスを作るために1回コレクションを走査し、各要素の計算結果を受け取るためにもう1回コレクションを走査する関係上、計算が軽いコレクションに対しては、あんまり有効じゃないかもしれません。しくしく。
スレッドプールのスレッド数はCPUコア数+2。*2くらいでもよい気がする。
一部省略したものの、ソース。
/** * High-order functions for Java collections. */ public class CollectionUtil { private static CollectionUtil instance; private ExecutorService service; private CollectionUtil() { service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 2); } public static CollectionUtil getInstance() { if (instance == null) { synchronized (CollectionUtil.class) { if (instance == null) { instance = new CollectionUtil(); } } } return instance; } public static <T0, T1> List<T1> map(Collection<T0> collection, Function<T0, T1> function) { List<T1> result = new ArrayList<T1>(collection.size()); for (T0 element : collection) { result.add(function.apply(element)); } return result; } public <T0, T1> List<T1> map2(Collection<T0> collection, Function<T0, T1> function) throws InterruptedException, ExecutionException { List<Future<T1>> futureList = new ArrayList<Future<T1>>(collection.size()); //各要素を、スレッドプールに計算させる。 //ElementRunnerがCallableインタフェースの実装クラスなので、 //service内のスレッドプールが計算してくれます。 for (T0 element : collection) { futureList.add(service.submit(new ElementRunner(element, function))); } //各要素の計算結果を取得する。 List<T1> result = new ArrayList<T1>(collection.size()); for(Future<T1> element : futureList) { result.add(element.get()); } return result; } } class ElementRunner<T0, T1> implements Callable<T1> { private T0 t; private Function<T0, T1> fn; public ElementRunner(T0 t, Function<T0, T1> fn) { this.t = t; this.fn = fn; } public T1 call() throws Exception { return fn.apply(t); } }
ツッコミ歓迎。