以下lambda是否可能在Java中以某种方式出现?我想对过滤后的流中的元素进行计数,但附带存储前10个
stream().filter(myFilter) //Reduces input to forthcoming operations
.limit(10) //Limits to ten the amount of elements to finish stream
.peek(myList::add) //Stores the ten elements into a list
.count(); //Here is the difficult one. Id like to count everything the total of elements that pass the filter, beyond the 10 I am fetching
编辑:从我的角度来说,这太隐含了,但是这个主意当然是一个潜在的解决方案,它将是最快的(比两次调用流生成器并分别在两个流上执行两个操作更快)。至少):
List<Entity> entities = stream().filter(myFilter)
.limit(10)
.collect(Collectors.toList());
long entitiesCount = stream().filter(myFilter)
.count();
...从一次迭代中获利,而不必将整个集合加载到内存中。我正在用答案的并行化进行测试
答案 0 :(得分:4)
自定义收集器是这里的答案:
Entry<List<Integer>, Integer> result = list.stream()
.collect(Collector.of(
() -> new SimpleEntry<>(new ArrayList<>(), 0),
(l, x) -> {
if (l.getKey().size() < 10) {
l.getKey().add(x);
}
l.setValue(l.getValue() + 1);
},
(left, right) -> {
List<Integer> leftList = left.getKey();
List<Integer> rightList = right.getKey();
while (leftList.size() < 10 && rightList.size() > 0) {
leftList.add(rightList.remove(0));
}
left.setValue(left.getValue() + right.getValue());
return left;
}));
假设您有以下代码:
Set.of(1, 2, 3, 4)
.stream()
.parallel()
.collect(Collector.of(
ArrayList::new,
(list, ele) -> {
System.out.println("Called accumulator");
list.add(ele);
},
(left, right) -> {
System.out.println("Combiner called");
left.addAll(right);
return left;
},
new Characteristics[] { Characteristics.CONCURRENT }));
在开始考虑该代码之前(对于示例而言,代码的正确性很重要),我们需要阅读一下CONCURRENT
特性的文档:
如果CONCURRENT收集器也不是UNDERDERED,那么只有将其应用于无序数据源时,才应同时评估它。
该文档的基本含义是,如果您的收集器是CONCURRENT
,并且流的来源是UNORDERED
(例如Set
),或者我们明确调用unordered
,那么合并将永远不会被调用。
如果运行前面的代码,您将看到Combiner called
永远不会出现在输出中。
如果您将Set.of(1, 2, 3, 4)
更改为List.of(1, 2, 3, 4)
,则会看到不同的图片(忽略您得到的结果的正确性-因为ArrayList
不是线程安全的,但这不是线程安全的)。点)。如果您将流的来源设为List
并同时,则调用unordered
,您将再次看到仅调用累加器,即:< / p>
List.of(1, 2, 3, 4)
.stream()
.unordered()
.parallel()
.collect(Collector.of(
ArrayList::new,
(list, ele) -> {
System.out.println("Called accumulator");
list.add(ele);
},
(left, right) -> {
System.out.println("Combiner called");
left.addAll(right);
return left;
},
new Characteristics[] { Characteristics.CONCURRENT }));
答案 1 :(得分:2)
下面的方法在具有摘要的本地类的帮助下使用可变的归约。
仅通过选择combiner
函数中的前10个元素即可限制所收集元素的数量。
使用IntStream
的示例:
Stat result = IntStream.range(0, 100)
.boxed()
.filter(i -> i % 2 == 0)
.collect(() -> new Stat(0, new ArrayList<Integer>()),
(stat, integer) -> {
stat.count++;
if (stat.list.size() < 10) {
stat.list.add(integer);
}
},
(stat1, stat2) -> {
stat1.list.addAll(stat2.list.subList(0, Math.min(stat2.list.size(),
10 - stat1.list.size())));
});
这是流中使用的Stat
类(您可以轻松使用类似Pair<Long, List<Integer>>
之类的东西):
private static class Stat {
long count;
List<Integer> list;
public Stat(long count, List<Integer> list) {
this.count = count;
this.list = list;
}
}
上面的示例产生[count=50,list=[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]]
答案 2 :(得分:1)
这是一个简单的lambda表达式,该表达式会将超过10个过滤器的所有项目添加到您的列表中:
i -> {if (myList.size() < 10) myList.add(i);}
但是您不能简单地使用count()
on Stream
:
如果实现能够直接从流源计算计数,则实现可以选择不执行流管道(顺序执行或并行执行)。在这种情况下,将不会遍历任何源元素,也不会评估任何中间操作。强烈建议避免带有副作用的行为参数,除了无害的情况(例如调试)外。
对于我来说,使用count()
不会调用peek()
,因为没有遍历元素,并且我的列表为空。
选择一个简单的简化来计算元素。
.reduce(0, (a, b) -> a + 1);
我的代码:
int count = yourCollection.stream()
.filter(myFilter)
.peek(i -> {if (myList.size() < 10) myList.add(i);} )
.reduce(0, (a, b) -> a + 1);
答案 3 :(得分:1)
这是另一种解决方案,不确定是否满足您的要求。
final Count c = new Count();
coll.stream().forEach(e -> {
c.setTotCount(c.getTotCount() + 1);
if (/*your filter*/) {
// add till 10 elements only
if (c.getMyList().size() <= 10) {
c.addMyList(e);
}
}
});
并且定义了辅助类
class Count {
int totCount;
// Student for an example
List<Student> myList = new ArrayList<>();
public List<Student> getMyList() {
return myList;
}
public void addMyList(Student std) {
this.myList.add(std);
}
// getter and setter for totCount
}
现在,您有了列表以及总数,它们都存储在帮助对象c
中。使用:
System.out.println(c.getTotCount());
System.out.println(c.getMyList().size());