Orders, payments and shipments arrive separately and a status is a function of all three. Index the two secondary feeds, then walk the primary one - the state machine stays readable because each lookup is a plain Map get.
record Order(String ref, double amount) {}
record Payment(String ref, double paid) {}
record Shipment(String ref, String carrier) {}
var orders = List.of(new Order("A-1", 250.0), new Order("A-2", 90.0), new Order("A-3", 40.0));
var payments = List.of(new Payment("A-1", 250.0), new Payment("A-2", 45.0));
var shipments = List.of(new Shipment("A-1", "BlueDart"));
Map<String, Double> paid = payments.stream()
.collect(Collectors.toMap(Payment::ref, Payment::paid));
Map<String, String> shipped = shipments.stream()
.collect(Collectors.toMap(Shipment::ref, Shipment::carrier));
orders.forEach(o -> {
double p = paid.getOrDefault(o.ref(), 0.0);
String state = p == 0.0 ? "UNPAID"
: p < o.amount() ? "PART-PAID"
: shipped.containsKey(o.ref()) ? "SHIPPED" : "READY";
System.out.println(o.ref() + " " + state + " paid=" + p + "/" + o.amount());
});
A-1 SHIPPED paid=250.0/250.0
A-2 PART-PAID paid=45.0/90.0
A-3 UNPAID paid=0.0/40.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