将一系列整数分组到函数的答案

对于一系列整数,我想应用(“昂贵的”)操作,仅过滤掉那些有趣答案的整数,然后分组答案。

第一个片段有效,但它在代码和计算中复制了操作(“模数2”):

IntStream.range(1, 10).boxed() .filter(p -> (p % 2 != 0)) // Expensive computation .collect(Collectors.groupingBy(p -> (p % 2))); // Same expensive // computation! // {1=[1, 3, 5, 7, 9]} (Correct answer) 

我尝试先映射到答案,然后过滤,然后分组 – 但最初的整数当然会在途中丢失:

 IntStream.range(1, 10).boxed() .map(p -> p % 2) // Expensive computation .filter(p -> p != 0) .collect(Collectors.groupingBy(p -> p)); // {1=[1, 1, 1, 1, 1]} (Of course, wrong answer) 

我想映射到一个元组或类似的东西,但还没有想出一个干净的方法来做到这一点。

好吧,既然我描述了我要做的事情,并且有一些关于它的讨论,我想我应该写下我所描述的内容:

  class IntPair { final int input, result; IntPair(int i, int r) { input = i; result = r; } } Map> output = IntStream.range(1, 10) .mapToObj(i -> new IntPair(i, i % 2)) .filter(pair -> pair.result != 0) .collect(groupingBy(pair -> pair.result, mapping(pair -> pair.input, toList()))); 

请注意,辅助类可以(也可能应该)是某种嵌套类,甚至是本地类。

为字段命名的一个好处是,它确实使得更容易理解正在发生的事情。 当我最初写这篇文章时,我无意中颠倒了input的作用并result了分组操作,因此我得到了错误的结果。 重新阅读代码之后,我很容易看到我按input值进行分组而不是result值,并且它也很容易修复。 如果我必须使用arr[0]arr[1]tuple.t1tuple.t2这将更难诊断和修复。

为什么不分组昂贵的计算结果,然后过滤生成的地图?

 IntStream.range(1, 10).boxed() .collect(groupingBy(x -> x % 2)) .entrySet().stream() .filter(e -> e.getKey() != 0) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); 

如果你想通过单个流实现这个(不收集到中间地图),你可以这样做:

 IntStream.range(1, 10).boxed() .map(p -> new AbstractMap.SimpleEntry<>(p % 2, p)) .filter(entry -> entry.getKey() != 0) .collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList()))); 

如果您不介意使用第三方代码,我的StreamEx库特别针对此类任务提供语法糖:

 IntStreamEx.range(1, 10).boxed() // map to Map.Entry where keys are your expensive computation // and values are input elements. The result is EntryStream // which implements the Stream and has additional methods .mapToEntry(p -> p % 2, Function.identity()) .filterKeys(k -> k != 0) .grouping(); 

在内部,它与第一个解决方案几乎相同。

一般的解决方案是记住计算的结果。 例如,像Stuart Marks建议的那样,但如果你不想引入新类型,你可以使用int数组:

 Map> map = IntStream.range(1, 10) .mapToObj(i -> new int[]{i, i % 2}) .filter(pair -> pair[1] != 0) .collect(groupingBy(pair -> pair[1], mapping(pair -> pair[0], toList()))); 

这种特殊情况的具体解决方案是通过简单的操作i&1替换昂贵的操作i%2

 Map> map = IntStream.range(1, 10) .filter(i-> (i&1)!=0) .boxed().collect(groupingBy(i->i&1)); 

那个操作太便宜了,我不在乎它的重复。 但是,如果你这样做的话

 Map> map = IntStream.range(1, 10) .filter(i-> (i&1)!=0) .boxed().collect(groupingBy(i->1)); 

要么

 Map> map = Collections.singletonMap(1, IntStream.range(1, 10).filter(i-> (i&1)!=0) .boxed().collect(toList())); 

将解决问题。 当然,它不是一个可重用的解决方案,但lambda表达式无论如何都是一次性使用的代码片段。

一种方法是首先收集Map的数字和答案,然后对条目流进行操作:

 IntStream.range(1, 10).boxed() .collect(toMap(p -> p, p -> p % 2)) .entrySet().stream() .filter(p -> p.getValue() != 0) .collect(groupingBy(p -> p.getValue(), mapping(p -> p.getKey(), toList()))); 

尽管如此,还不确定性能影响。

所以这是我的解决方案:)。 也许不是最好的。

 import java.util.stream.IntStream; import java.util.stream.Collectors; import java.util.Map; import java.util.List; public class Test { private static final class Tuple { private final Integer t1; private final Integer t2; private Tuple(final Integer t1, final Integer t2) { this.t1 = t1; this.t2 = t2; } public Integer getT1() { return t1; } public Integer getT2() { return t2; } } private static String getValues(final List list) { final StringBuilder stringBuilder = new StringBuilder(); for(Tuple t : list) { stringBuilder.append(t.getT2()).append(", "); } return stringBuilder.toString(); } public static void main(String []args){ Map> results = IntStream.range(1, 10).boxed() .map(p -> new Tuple(p % 2, p)) // Expensive computation .filter(p -> p.getT1() != 0) .collect(Collectors.groupingBy(p -> p.getT1())); results.forEach((k, v) -> System.out.println(k + "=" + getValues(v))); } } 

输出为1 = 1,3,5,7,9;

关于表现:

 sh-4.3# java -Xmx128M -Xms16M Test Time a1: 231 Time a2: 125 sh-4.3# java -Xmx128M -Xms16M Test Time a1: 211 Time a2: 127 sh-4.3# java -Xmx128M -Xms16M Test Time a1: 172 Time a2: 100 

A1是你问题中的第一个算法,A2是我的答案。 因此即使使用辅助类也会更快。

以下是性能测量,还包括答案中的算法(如A3):

 sh-4.3# java -Xmx128M -Xms16M HelloWorld Time a1: 202 Time a2: 113 Time a2: 170 sh-4.3# java -Xmx128M -Xms16M HelloWorld Time a1: 195 Time a2: 114 Time a2: 169 

我发现你的表现超出我的表现很奇怪,认为它会或多或少相同。