2017-05-06 105 views
4

我們使用3個列表ListA,ListB,ListC爲3個主題(A,B,C)中的10名學生保留標記。使用Java 8流聚合列表對象

主題B和C是可選的,所以只有幾個學生總分10具有標記在那些受試者

Class Student{ 
String studentName; 
int marks; 
} 

利斯塔具有用於10個學生記錄數組listB 5和ListC 3(其也是大小的名單)

想知道我們如何可以總結他們的學生使用java 8蒸汽的標記。

我嘗試以下

List<Integer> list = IntStream.range(0,listA.size() -1).mapToObj(i -> listA.get(i).getMarks() + 
listB.get(i).getMarks() + 
listC.get(i).getMarks()).collect(Collectors.toList());; 

有2個問題與此

一)它會給IndexOutOfBoundsException異常的數組listB和listC沒有10元

二)返回的列表如果是Integer類型,我希望它是Student類型。

任何投入將是非常有益的

+0

所以,一個主題是,實際上,一個學生,在一個主題上有標記,對嗎?這個名字是學生的名字,對嗎? –

+0

我認爲你需要壓縮,坦率地說,壓縮不在標準庫中!看到這裏如何做到這一點:http://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip – niceman

+0

是的,這是一個學生..我已更正細節。謝謝 – girish

回答

4

您可以進行3所列出的流,然後調用flatMap把所有的名單元素融入到一個單一的數據流。該流將包含每個學生每個標記的一個元素,因此您將不得不按學生姓名彙總結果。沿着線的東西:

Map<String, Integer> studentMap = Stream.of(listA, listB, listC) 
     .flatMap(Collection::stream) 
     .collect(groupingBy(student -> student.name, summingInt(student -> student.mark))); 

另外,如果您Student類有干將其領域,你可以改變的最後一行,以使其更易於閱讀:

Map<String, Integer> studentMap = Stream.of(listA, listB, listC) 
     .flatMap(Collection::stream) 
     .collect(groupingBy(Student::getName, summingInt(Student::getMark))); 

然後檢查由印刷結果出studentMap

studentMap.forEach((key, value) -> System.out.println(key + " - " + value)); 

如果你想創建Student對象的列表,而不是,您可以使用R第一張地圖的esult,並創建了項新流(這個特殊的例子假設你的Student類有一個所有參數的構造,因此您可以在一個行吧):

List<Student> studentList = Stream.of(listA, listB, listC) 
     .flatMap(Collection::stream) 
     .collect(groupingBy(Student::getName, summingInt(Student::getMark))) 
     .entrySet().stream() 
      .map(mapEntry -> new Student(mapEntry.getKey(), mapEntry.getValue())) 
      .collect(toList()); 
+0

很好的答案。列表方法的靈活性在於它可以容納任意數量的強制性和選擇性主題。如果'listA','listB'和'listC'是硬編碼的,則可以另外使用兩個'Stream.concat()'調用來將三個流連接成一個。 –

+4

@ OleV.V。 Stream.of(T ...值)是3個或更多列表的任何其他選項。 'Stream.of(listA,listb,listc).flatMap(Collection :: stream)。 ...' –

+0

非常好的主意,@ÖmerErden –

1

我會如下做到這一點:

Map<String, Student> result = Stream.of(listA, listB, listC) 
    .flatMap(List::stream) 
    .collect(Collectors.toMap(
     Student::getName, // key: student's name 
     s -> new Student(s.getName(), s.getMarks()), // value: new Student 
     (s1, s2) -> { // merge students with same name: sum marks 
      s1.setMarks(s1.getMarks() + s2.getMarks()); 
      return s1; 
     })); 

在這裏,我用Collectors.toMap創建地圖(我還以爲你有Student接收名稱和標記的構造函數)。

這的Collectors.toMap版本預計三個參數:

  1. ,返回鍵的每個元素(此爲Student::getName
  2. ,對於每一個元素返回值的函數(我已經創建了一個函數新的Student實例是原始元素的副本,這是爲了不修改來自原始流的實例)
  3. 當有元素具有相同的鍵時使用的合併函數,即對於具有相同鍵的元素名字(我在這裏總結了這些標記)。

如果你可以在下面的拷貝構造函數和方法添加到您的Student類:

public Student(Student another) { 
    this.name = another.name; 
    this.marks = another.marks; 
} 

public Student merge(Student another) { 
    this.marks += another.marks; 
    return this; 
} 

然後,你可以用這種方式重寫上面的代碼:

Map<String, Student> result = Stream.of(listA, listB, listC) 
    .flatMap(List::stream) 
    .collect(Collectors.toMap(
     Student::getName, 
     Student::new, 
     Student::merge)); 
+1

我同意,這是比我的解決方案更有效的方法。我建議將此標記爲接受的答案,@girish。 –

+0

有利有弊。我不喜歡你修改原始列表中的對象。提問者必須權衡這個優點。 –

+0

@ OleV.V。在第一個片段的原始對象沒有被修改 –