Two feeds carrying the same entity need a precedence rule, not a distinct(). toMap's merge function is where that rule belongs - and it is evaluated in stream order, so the first source wins by construction.
record Contact(String email, String name, String source) {}
var crm = List.of(new Contact("ava@x.io", "Ava", "crm"),
new Contact("ben@x.io", "Ben", "crm"));
var web = List.of(new Contact("ben@x.io", "Ben R", "web"),
new Contact("cara@x.io", "Cara", "web"));
var merged = Stream.concat(crm.stream(), web.stream())
.collect(Collectors.toMap(Contact::email, c -> c,
(first, second) -> first, // CRM was streamed first
TreeMap::new));
merged.values().forEach(c ->
System.out.println(c.email() + " " + c.name() + " (" + c.source() + ")"));
ava@x.io Ava (crm)
ben@x.io Ben (crm)
cara@x.io Cara (web)
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