2017-10-21 20 views
2

编辑 - 第二类不具有基于索引的访问,而不是它实现迭代Java流上的所有值进行操作

假设一个阶级结构是这样的:

public class Values 
{ 
    public int getValue(int i) 
    { 
     return values_[i]; 
    } 

    private int[] values_; 
} 

和第二类像这样

public class ValuesCollection implements Iterable 
{ 
    private Values[] valuesCollection_; 
} 

是否有一种方法使用java8 streams API来操作每个维度的统计信息,例如:sum,mean,min ,max,range,variance,std等。例如[[2,4,8],[1,5,7],[3,9,6]],为了得到min,它会返回[1,4, 6]

我可以想出最接近的是这样的:

public int[] getMin(ValuesCollection valuesCollection) 
{ 

    IntStream.range(0, valuesCollection.size()).boxed().collect(Collectors.toList()) 
      .forEach(i -> { 

       List<Integer> vals = valuesCollection.stream() 
         .map(values -> values.getValue(i)) 
         .collect(Collectors.toList()); 

       // operate statistics on vals 
       // no way to return the statistics 
      }); 

} 
+2

min 1,4,6?^^而不是2,1,3? – azro

+0

您正在寻找'Stream.reduce'。或自定义收集器。 –

+0

@azro是1,4,6我试图穿过维度 – rossb83

回答

2

你可以做到这一点。我已经使用了数组而不是你的包装类。另外,我应该包含一些验证数组是矩形的,并且使用orElseThrow而不是getAsInt,但您明白了。

int[][] vals = {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}}; 

int[] min = IntStream 
       .range(0, vals[0].length) 
       .map(j -> IntStream.range(0, vals.length).map(i -> vals[i][j]).min().getAsInt()) 
       .toArray(); 

System.out.println(Arrays.toString(min));  // Prints [1, 4, 6] as expected 

(因为我用数组,我可以用这条线,而不是

.map(j -> Arrays.stream(vals).mapToInt(arr -> arr[j]).min().getAsInt()) 

,但我写的像我一样对你的情况你的对象不是数组,但确实有基于索引的密切模型访问)。

为标准差做这件事显然比较困难,但您可以将我的答案与this one结合使用。

编辑

如果外部类没有基于索引的访问,而是实现Iterable可以通过转换IterableStream做到这一点。

Iterable<int[]> vals = Arrays.asList(new int[][] {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}}); 

int[] min = IntStream 
       .range(0, vals.iterator().next().length) 
       .map(j -> StreamSupport.stream(vals.spliterator(), false).mapToInt(a -> a[j]).min().getAsInt()) 
       .toArray(); 

System.out.println(Arrays.toString(min));  // Prints [1, 4, 6] as expected 
+0

假设外部集合类不允许基于索引的访问,而是实现迭代,这仍然是可能的吗? – rossb83

+1

是的,但它会是一个完整的混乱,速度较慢,而且你也失去了单独处理尺寸的能力。相反,你需要一次处理整行的'收集器'。 –

+0

你能举一个快速的例子吗? – rossb83

1

可以通过简单地做到这一点平展整数[] []为整数[]然后在其上以等于所述阵列的长度的周期进行操作。 以下算法仅执行最小,最大和总和!

public class Test { 
    public static void main(String... strings) { 
      Integer[][] vals = {{2, 4, 8}, {1, 5, 7}, {3, 9, 6}}; 

      BinaryOperator<Integer> minFunction = (x, y) -> x < y? x: y; 
      BinaryOperator<Integer> maxFunction = (x, y) -> x > y? x: y; 
      BinaryOperator<Integer> sumFunction = (x, y) -> x+y; 
    Integer[] performStatistic = performStatistic(vals, minFunction); // 1 4 6 
    Integer[] performStatistic2 = performStatistic(vals, maxFunction); // 3 9 8 
    Integer[] performStatistic3 = performStatistic(vals, sumFunction); // 6 18 21 
      } 

public static Integer[] performStatistic(Integer[][] vals, BinaryOperator<Integer> f){ 

      List<Integer> res = new ArrayList<>(vals.length); 
      int[] i = {0}; 
    Arrays.asList(vals).stream().flatMap((Integer[] x)-> Arrays.asList(x).stream()) 
      .forEach(x -> { 
       if(i[0]<vals.length){ 
        res.add(i[0], x); 
       }else{ 
        int cyclicPos = i[0]%vals.length; 
        res.set(cyclicPos, f.apply(res.get(cyclicPos), x)); 
       } 
       i[0]++; 
      }); 
      return res.toArray(new Integer[res.size()]); 
     } 

    } 

要执行其他操作,您可以按照相同的步骤,并使用BiFunction<Integer,Double,Double>执行范围,方差... 也许下面可以帮助你!

double avg = 100; // the avg you can get it from the previous algorithm 
BiFunction<Integer,Double,Double> varianceFunction = (Integer x, Double y) -> { 
      return Math.pow(new Double(x) - avg, 2)+y; 

     };// after getting the result just divided it by the (size of the array -1) 
+0

编辑问题,外部类没有基于索引的选择,而是它实现了可迭代的 – rossb83

+0

我根据你的简单输入'[[2,4,8],[1,5,7],[3,9,6 ]]'就像@Paul Boddington的回答,请添加'iterator()'方法的实现,也许我可以帮助你。 –