Group the concatenation by the business key and the groups of size two are your duplicates - one pass, and it scales to any number of source files without changing the shape.
record Row(String id, String source) {}
var fileA = List.of(new Row("r1", "A"), new Row("r2", "A"), new Row("r3", "A"));
var fileB = List.of(new Row("r2", "B"), new Row("r3", "B"), new Row("r4", "B"));
Map<String, List<String>> sources = Stream.concat(fileA.stream(), fileB.stream())
.collect(Collectors.groupingBy(Row::id, TreeMap::new,
Collectors.mapping(Row::source, Collectors.toList())));
sources.entrySet().stream()
.filter(e -> e.getValue().size() > 1)
.forEach(e -> System.out.println("duplicate " + e.getKey() + " in " + e.getValue()));
System.out.println("unique ids: " + sources.size());
duplicate r2 in [A, B]
duplicate r3 in [A, B]
unique ids: 4
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-20