私は最近、技術的なインタビューを持っていたし、ストリームAPIに小さなコーディングタスクを得ました。
のは、次の入力を考えてみましょう:
public class Student {
private String name;
private List<String> subjects;
//getters and setters
}
Student stud1 = new Student("John", Arrays.asList("Math", "Chemistry"));
Student stud2 = new Student("Peter", Arrays.asList("Math", "History"));
Student stud3 = new Student("Antony", Arrays.asList("Music", "History", "English"));
Stream<Student> studentStream = Stream.of(stud1, stud2, stud3);
タスクがすることですストリームAPIを使用して独自の科目で生徒を見つけます。
だから、与えられた入力期待される結果(順番を無視して)ためです[John, Anthony]
。
私は、カスタムコレクタを使用して解決策を提示しました。
Collector<Student, Map<String, Set<String>>, List<String>> studentsCollector = Collector.of(
HashMap::new,
(container, student) -> student.getSubjects().forEach(
subject -> container
.computeIfAbsent(subject, s -> new HashSet<>())
.add(student.getName())),
(c1, c2) -> c1,
container -> container.entrySet().stream()
.filter(e -> e.getValue().size() == 1)
.map(e -> e.getValue().iterator().next())
.distinct()
.collect(Collectors.toList())
);
List<String> studentNames = studentStream.collect(studentsCollector);
しかし、解決策は、最適な/効率的ではないと考えられました。
あなたは、このタスクのためのより効率的なソリューションにあなたのアイデアを共有していただけますか?
UPDATE:私は彼が減速(Stream.reduce()メソッド)を使用することを1人から別の意見を得ました。しかし、私は、これは効率性を高めることができる方法を理解することはできません。どう思いますか?
ここでは別のものです。
// using SimpleEntry from java.util.AbstractMap
Set<Student> list = new HashSet<>(studentStream
.flatMap(student -> student.getSubjects().stream()
.map(subject -> new SimpleEntry<>(subject, student)))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l, r) -> Student.SENTINEL_VALUE)
.values());
list.remove(Student.SENTINEL_VALUE);
(意図的により以下それについて、センチネル値を使用して)。
手順:
Set<Student> list = new HashSet<>(studentStream
私たちは、当社が収集するつもりコレクションからのHashSetを作成しています。我々は、重複学生(あなたのケースアントニー内に複数のユニークな科目、学生)を取り除きたいからです。
.flatMap(student -> student.subjects() .map(subject -> new SimpleEntry(subject, student)))
私たちは、ストリームに各生徒の科目をflatmappingされていますが、最初に私たちは、学生のキーとして被験者とし、値としてペアに各要素をマップします。我々が対象と学生との間の関連を保持する必要があるためです。私が使用しています
AbstractMap.SimpleEntry
が、もちろん、あなたはペアのいずれかの実装を使用することができます。.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l, r) -> Student.SENTINEL_VALUE)
私たちは、結果のマップの値としてキーや学生などの対象を設定し、マップに値を収集しています。私たちは第三引数(Aに渡す
BinaryOperator
キーの衝突が発生した場合にどうすべきかを定義します)。私たちは、渡すことができないnull
私たちがセンチネル値を使用して、1。
この時点で、我々は学生に各被験者をマッピングすることにより、関係学生↔対象を反転している(またはSENTINEL_VALUE
対象が複数の生徒を持っている場合)。.values());
私たちは、ユニークな主題を持つすべての学生のリストに加え、センチネル値が得られ、マップの値をとります。
list.remove(Student.SENTINEL_VALUE);
唯一のものは、センチネル値を退治されやって残しました。
1私たちは、使用することはできませんnull
。このような状況で。地図のほとんどの実装はにマッピングされたキーの区別作らないnull
か、その特定のキーが存在しないこと。または、より正確に、のマージ方法HashMap
積極的には、ノードを削除したときに再マッピング関数が戻りますnull
。我々はセンチネル値を避けたい場合は、次に我々は実装しなければならないか、自分のmerge
このようなもののように実装することができ方法、: return (!containsKey(key) ? super.merge(key, value, remappingFunction) : put(key, null));
。