2017-10-21 80 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()'方法的實現,也許我可以幫助你。 –