Line to product to discount is two joins in one map step. Build the priced rows first and aggregate after - a sum that prints as it goes is a side effect inside a pipeline, and it breaks the moment anyone parallelises it.
record Product(String sku, double list) {}
record Discount(String sku, double pct) {}
record Line(String sku, int qty) {}
record Priced(String sku, int qty, double unit, double amount) {}
var products = List.of(new Product("S1", 100.0), new Product("S2", 60.0));
var discounts = List.of(new Discount("S2", 25.0));
var lines = List.of(new Line("S1", 2), new Line("S2", 3));
Map<String, Double> list = products.stream().collect(Collectors.toMap(Product::sku, Product::list));
Map<String, Double> off = discounts.stream().collect(Collectors.toMap(Discount::sku, Discount::pct));
var priced = lines.stream().map(l -> {
double unit = list.get(l.sku()) * (1 - off.getOrDefault(l.sku(), 0.0) / 100);
return new Priced(l.sku(), l.qty(), unit, unit * l.qty());
}).toList();
priced.forEach(p -> System.out.println(p.sku() + " x" + p.qty() + " @ " + p.unit() + " = " + p.amount()));
System.out.println("total: " + priced.stream().mapToDouble(Priced::amount).sum());
S1 x2 @ 100.0 = 200.0
S2 x3 @ 45.0 = 135.0
total: 335.0
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