另一个流上的流过滤器和计数

时间:2019-08-12 17:01:53

标签: java java-8 java-stream

我需要根据另一个流过滤一个流,并获取所有匹配条目的计数。

我尝试了以下方法和其他各种组合,但无法正常工作。

想法是:

  • 对于0到256之间的每个数字(流1)
  • 查看该号码是否出现在另一个列表中(流2),是否出现
  • 计算流2中出现的次数除以元素总数(即18)。
  • 如果未出现,请收集0。

这基本上是根据流2中的出现找到流1中数字的频率。

流2是

int[] chars = {332, 255, 271, 232, 194, 39, 162, 89, 200, 126, 225, 218, 42, 237, 87, 63, 63, 229};

以下代码的预期输出是:

[0,0,0,...,1/18for39,0,0,1/18for42,0,0,...,2/18for63,...,1/18for87,0,1/18for89,...1/18for126,0,0,...1/18for162,0,0,...,etc..1/18for255,0]

任何帮助将不胜感激。预先感谢。

BiPredicate<Integer, Integer> predicate = (d, f) -> Integer.valueOf(d)
    .equals(Integer.valueOf(f));

List<Double> fractions = chars.filter(value -> IntStream.rangeClosed(0, 256)
    .anyMatch(nbr -> predicate.test(value, nbr)))
    .count()
    .map(x -> x)
    .mapToDouble(x -> x / chars.size())
    .boxed()
    .collect(Collectors.toList());

3 个答案:

答案 0 :(得分:0)

我会做些不同的事情,以解决手头的问题:

IntStream idents = ...; // Your 0-256
IntStream input = ...; // your random numbers

//Generate a map of input numbers to how often they occur
Map<Integer, Long> freq = input.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
//No need for the stream's source, use the map
long total = freq.values().stream().reduce(0L, Long::sum); // your 18

有了我们的频率图,就可以轻松创建我们想要的东西。我回答了我的建议,也满足了您的要求:

//Generating the 0-256 array in double form
double[] result = idents.mapToDouble(i -> freq.getOrDefault(i, 0) / (double) total).toArray();

说实话,仅使用频率图来确定所需信息,我认为这样做没有多大意义。

//Find the fraction for 39:
double value = freq.getOrDefault(39, 0) / (double) total;

//Find only the values in range:
Predicate<Integer> inRange = i -> i >= 0 && i <= 256;
freq.entrySet().stream().filter(e -> inRange.test(e.getKey())); //you now have a stream of the valid frequencies

//Or the values out of range
....filter(e -> inRange.negate().test(e.getKey()))

如果输入为空,则也可以立即返回数组。让我知道是否还有另一个我没有讲到的案例

答案 1 :(得分:0)

其他解决方案的替代方法(在迭代给定流的元素方面非常有效),但与您现有的尝试更接近的方法是将输入用作数组,以便在使用时可以使用size维处理它们,而不是执行anyMatch,而只是filter,对元素进行计数并计数,以进一步对平均值进行如下处理:

int[] chars = new int[]{332, 255, 271, 232, 194, 39, 162, 89, 200, 126, 225, 218, 42, 237, 87, 63, 63, 229};
List<Double> fractions = IntStream.rangeClosed(0, 256)
        .mapToLong(value -> Arrays.stream(chars).filter(f -> value == f).count())
        .mapToDouble(x -> (double) x / chars.length).boxed()
        .collect(Collectors.toList());

答案 2 :(得分:0)

您应避免将源重复256次。如果是Stream,则无论如何都不能对其进行多次处理,但是即使您有允许对其进行多次迭代的源,也应避免在避免这种情况下经常这样做。

如果您想使其看起来像是单个Stream操作,则可以执行例如

int[] chars = {332, 255, 271, 232, 194, 39, 162, 89, 200, 126, 225, 218, 42, 237, 87, 63, 63, 229};
List<Double> fractions = Arrays.stream(chars).boxed()
    .collect(Collectors.collectingAndThen(
        Collectors.groupingBy(Function.identity(), Collectors.counting()),
        map -> {
            double total = map.values().stream().mapToInt(Long::intValue).sum();
            return IntStream.rangeClosed(0, 256)
                .mapToObj(i -> map.getOrDefault(i, 0L)/total)
                .collect(Collectors.toList());
        }));

但是使用Stream或“一个操作”本身并不是目的。

如果您想要一个简单而有效的解决方案,请考虑

int[] counts = new int[257];
for(int c: chars) if(c >= 0 && c <= 256) counts[c]++;
double total = chars.length;
List<Double> fractions
    = Arrays.stream(counts).mapToObj(c -> c/total).collect(Collectors.toList());