2017-05-06 4 views
4

ListA、ListB、ListCという3つのリストを使用して、3人の被験者(A、B、C)の10人の学生のマークを保持しています。 10のうちこれだけ少ない学生は、これらの対象におけるマークを有するリストオブジェクトを集約するためのJava 8ストリームの使用

被写体B及びCは、任意である

Class Student{ 
String studentName; 
int marks; 
} 

リスタは、10人のレコードを有し、ListBの3 5及びListCため(これもサイズであります

Java 8の蒸気を使って、どのようにして科目の学生の印を集計することができるか知りたいですか?

私はとの2つの問題があり、次の

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());; 

を試してみました。この

それは10個の要素

b)に返されたリストを持っていないListBのとlistCとしてはIndexOutOfBoundsExceptionを与えるA) Integer型の場合はStudent型にします

任意の入力は、あなたが3つのリストの流れを作り、その後、単一のストリームにすべてのリスト要素を配置するflatMapを呼び出すことができます

+0

ので、件名があります、実際には、1つの科目にそのマークが付いた生徒、そうですか?名前は学生の名前ですよね? –

+0

私はあなたがそれのためにジッパーが必要だと思う、率直に言うと、標準的な図書館にはない!それを行う方法についてはここを参照してください:http://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip – niceman

+0

はい、それは学生です..私は詳細を修正しました。ありがとう – girish

答えて

4

非常に参考になります。そのストリームには、マークごとに学生1人の要素が含まれるため、結果を学生名で集計する必要があります。線に沿って何か:

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

、あなたのStudentクラスはそのフィールドのgetterを持っている場合、あなたはそれを読みやすくするために、最後の行を変更することができます。

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

次に印刷して、結果を確認してくださいstudentMapアウト:あなたの代わりにStudentオブジェクトのリストを作成したい場合は

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

、あなたは、rを使用することができます次のように

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'がハードコーディングされている場合、3つのストリームを1つに連結するために、' Stream.concat() 'の2つの呼び出しを代わりに使用することができます。 –

+4

@ OleV.V。 Stream.of(T ... values)は、3つ以上のリストの他のオプションです。 'Stream.of(listA、listb、listc).flatMap(Collection :: stream)です。 ... –

+0

非常に良いアイデア@ÖmerErden –

1

が、私はそれを行うだろう:最初のマップのesultと(この例では、1行もできるように、Studentクラスはすべて、引数のコンストラクタを持っていると仮定し)そのエントリから、新しいストリームを作成します。ここで

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; 
     })); 

私は(私はあなたの名前やマークを受けStudentのコンストラクタを持っても想定してきた)マップを作成するためにCollectors.toMapを使用しました。

Collectors.toMapのこのバージョンでは、3つの引数を期待し:

  1. 各要素の値を返します
  2. 機能(私が作成した(それはStudent::getNameていますここで)各要素のキーを返す関数新しい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。最初のスニペットでは元のオブジェクトは変更されていません –

関連する問題