Java 将整数范围分组为函数的答案
对于一系列整数,我想应用一个(“昂贵的”)操作,只过滤出那些有有趣答案的整数,然后对答案进行分组 第一个代码段可以工作,但它在代码和计算中重复了操作(“模数2”):Java 将整数范围分组为函数的答案,java,functional-programming,java-8,java-stream,Java,Functional Programming,Java 8,Java Stream,对于一系列整数,我想应用一个(“昂贵的”)操作,只过滤出那些有有趣答案的整数,然后对答案进行分组 第一个代码段可以工作,但它在代码和计算中重复了操作(“模数2”): IntStream.range(1, 10).boxed() .filter(p -> (p % 2 != 0)) // Expensive computation .collect(Collectors.groupingBy(p -> (p %
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)
我想映射到一个元组或类似的东西,但还没有找到一个干净的方法。一种方法是首先收集
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<Tuple> 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<Integer, List<Tuple>> 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)));
}
}
A1是问题中的第一个算法,A2是我的答案。因此,即使使用helper类,速度也更快
以下是性能测量,也包括您答案中的算法(如A3所示):
我觉得奇怪的是,你的表现比我的好,虽然这或多或少是一样的。为什么不对昂贵的计算结果进行分组,然后过滤生成的地图
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())));
IntStream.range(1,10).boxed()
.map(p->newAbstractMap.SimpleEntry(p%2,p))
.filter(entry->entry.getKey()!=0)
.collect(收集器).groupingBy(条目::getKey,
Collectors.mapping(Entry::getValue,Collectors.toList());
如果您不介意使用第三方代码,“我的库”特别为此类任务提供了语法糖:
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<Map.Entry> and has additional methods
.mapToEntry(p -> p % 2, Function.identity())
.filterKeys(k -> k != 0)
.grouping();
intstreamx.range(1,10).boxed()
//映射到映射。输入键是您昂贵的计算
//和值是输入元素。结果是EntryStream
//它实现了流并具有其他方法
.mapToEntry(p->p%2,函数.identity())
.filterKeys(k->k!=0)
.分组();
从内部来看,它与第一个解决方案几乎相同。好吧,因为我描述了我将要做的事情,并且对此进行了一些讨论,我想我应该写下我所描述的内容:
class IntPair {
final int input, result;
IntPair(int i, int r) { input = i; result = r; }
}
Map<Integer, List<Integer>> 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())));
类IntPair{
最终整数输入,结果;
IntPair(inti,intr){input=i;result=r;}
}
地图输出=
IntStream.range(1,10)
.mapToObj(i->新的IntPair(i,i%2))
.filter(对->对.result!=0)
.collect(分组依据(配对->配对结果),
映射(pair->pair.input,toList());
请注意,helper类可以(也可能应该)是某种嵌套类,甚至是本地类
为字段命名有一个好处,那就是它确实使理解发生了什么变得更容易。当我最初写这篇文章时,我无意中颠倒了分组操作中
input
和result
的角色,因此得到了错误的结果。在重新阅读代码后,我很容易看到我是通过输入
值分组的,而不是结果
值,而且也很容易修复。如果我必须使用arr[0]
和arr[1]
或tuple.t1
和tuple.t2
的话,这将更难诊断和修复。一般的解决方法是记住计算结果。例如,如果您不想引入新类型,可以使用int
数组:
Map<Integer, List<Integer>> 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())));
那个手术太便宜了,我不在乎它的重复。当然,如果你这样做了
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.filter(i-> (i&1)!=0)
.boxed().collect(groupingBy(i->1));
Map Map=IntStream.range(1,10)
.过滤器(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表达式是一个可以使用的代码片段。您能解释一下您想做什么吗?我们唯一的解释是,你正在尝试做一个“昂贵”的操作(模对我来说并不昂贵,但好吧,我会顺其自然)。但是什么是输入,什么是预期的输出呢?可能最好的方法是创建一个小值类(helper类)来包含原始int和昂贵操作的结果。看,为什么?如果你不想做两次同样昂贵的计算,你需要把它存储在某个地方,这是通过让一个自定义类处理这些结果来实现的。您可以使用
int[2]
数组,但会失去一些语义。@Markus您可以这样做:IntStream.range(1,10).mapToObj(p->new int[]{p,p%2})。filter(arr->arr[1]!=0)。collect(groupingBy(arr->arr[1],mapping(arr->arr[0],toList())代码>@Juru好吧,我的回答表明了我的偏好,但我完全愿意接受其他人有不同的偏好。像Pair
这样的典型元组需要装箱,像int[2]
技巧一样,不能将名称与不同的元素相关联。这些都是选择helper类的原因。但我同意它们冗长而笨重。不过,我不太清楚过去的情况。:-)为什么不干脆range(…).mapToObj(p->newsimpleentry(p%2,p)).filter(e->e.getKey()!=0).collect(groupingBy(SimpleEntry::getKey,mapping(SimpleEntry::getValue,toList())代码>然后?做一个小测试,在这个测试中,你执行这个实现100万次,然后在你的问题中执行另一个正确的实现,看看这两个都花了多长时间,这会让你对性能的影响有一个了解。@AlexisC。SimpleEntry在AbstractMap中定义。意味着包括AbstractMap static或新AbstractMap.SimpleEntry(…)
@Markus这只是一个导入,就像任何其他import java.util.AbstractMap.SimpleEntry代码>@Markus我做过
Map<Integer, List<Integer>> 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())));
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.filter(i-> (i&1)!=0)
.boxed().collect(groupingBy(i->i&1));
Map<Integer, List<Integer>> map = IntStream.range(1, 10)
.filter(i-> (i&1)!=0)
.boxed().collect(groupingBy(i->1));
Map<Integer, List<Integer>> map = Collections.singletonMap(1,
IntStream.range(1, 10).filter(i-> (i&1)!=0)
.boxed().collect(toList()));