Real joins are rarely on one column. A record gives you equals and hashCode for free, so a two-field key works as a Map key immediately - no string concatenation that breaks the first time a value contains the separator.
record Key(String region, String sku) {}
record Stock(String region, String sku, int onHand) {}
record Demand(String region, String sku, int wanted) {}
var stock = List.of(new Stock("east", "S1", 10), new Stock("west", "S1", 2));
var demand = List.of(new Demand("east", "S1", 4), new Demand("west", "S1", 5),
new Demand("east", "S2", 1));
Map<Key, Integer> onHand = stock.stream()
.collect(Collectors.toMap(s -> new Key(s.region(), s.sku()), Stock::onHand));
demand.stream()
.sorted(Comparator.comparing(Demand::region).thenComparing(Demand::sku))
.forEach(d -> {
int have = onHand.getOrDefault(new Key(d.region(), d.sku()), 0);
System.out.println(d.region() + "/" + d.sku() + " want " + d.wanted() + " have " + have
+ (have >= d.wanted() ? " OK" : " SHORT " + (d.wanted() - have)));
});
east/S1 want 4 have 10 OK
east/S2 want 1 have 0 SHORT 1
west/S1 want 5 have 2 SHORT 3
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