Map<String, Employee> ans1 = empList.stream() .collect(Collectors.groupingBy( Employee::getDeptName, Collectors.collectingAndThen( Collectors.minBy( Comparator.comparing(Employee::getYearOfJoining)), Optional::get)));
// ✦ equivalent forms: List<Employee> ans2 = empList.stream() .sorted(Comparator.comparing(Employee::getAge) .reversed() .thenComparing(Employee::getName) .reversed()) // reverses ENTIRE chain .toList(); List<Employee> ans3 = empList.stream() .sorted(Comparator.comparing(Employee::getAge) .thenComparing(Employee::getName, Comparator.reverseOrder())) // only name DESC .toList();
DoubleSummaryStatistics stats = empList.stream() .collect(Collectors.summarizingDouble(Employee::getSalary));
Map<String, List<Employee>> ans5 = empList.stream() .collect(Collectors.groupingBy( Employee::getDeptName, Collectors.collectingAndThen( Collectors.toList(), list -> list.stream() .sorted(Comparator.comparing( Employee::getSalary, Comparator.reverseOrder())) .limit(2).toList())));
Map<String, Map<String, Optional<Employee>>> ans6 = empList.stream().collect( Collectors.groupingBy(Employee::getDeptName, Collectors.groupingBy(Employee::getGender, Collectors.minBy( Comparator.comparing(Employee::getSalary)))));
Map<String, List<Employee>> ans7 = empList.stream() .collect(Collectors.groupingBy( Employee::getDeptName, Collectors.collectingAndThen( Collectors.groupingBy(Employee::getSalary), salaryMap -> salaryMap.entrySet().stream() .max(Map.Entry.comparingByKey()) .map(Map.Entry::getValue).get())));
Collectors.toMap( Employee::getId, // key e -> e, // value (existing, newer) -> existing );
Collectors.partitioningBy( e -> e.getSalary() > 50000 ); // → Map<Boolean, List<Employee>>
.collect(Collectors.joining(", ")) Collectors.averagingInt(Employee::getAge) Collectors.summingDouble(Employee::getSalary)
// flatMap: each element → sub-stream, all merged list.stream() .flatMap(e -> e.getProjects().stream()); // int[] → IntStream → object stream Arrays.stream(arr) .mapToObj(i -> "#" + i); // String chars as characters str.chars() .mapToObj(c -> (char) c); // range: 1 inclusive, 100 exclusive IntStream.range(1, 100); IntStream.rangeClosed(1, 100);
// Stream.iterate: seed + unary operator → infinite stream Stream.iterate( new long[]{ 0, 1 }, f -> new long[]{ f[1], f[0] + f[1] }) .limit(10) .map(f -> f[0]) .forEach(System.out::println);
// accumulator only → Optional (like max, min) stream.reduce((a, b) -> a + b); // identity + accumulator → T (never empty) stream.reduce(0, Integer::sum); // parallel: identity + accumulator + combiner parallelStream.reduce(0, (a, b) -> a + b, (a, b) -> a + b); // combiner merges sub-results
// hint JVM: order doesn't matter → better distribution list.parallelStream().unordered() .filter(e -> e.getAge() > 30);
// Filter prime numbers List<Integer> primes = nums.stream() .filter(n -> n > 1 && IntStream.range(2, (int) Math.sqrt(n) + 1) .noneMatch(i -> n % i == 0)) .toList();
// Factorial using reduce int fact = IntStream.rangeClosed(1, n) .reduce(1, (a, b) -> a * b);
// Reverse a string with streams String reversed = Arrays.stream(str.split("")) .reduce("", (a, b) -> b + a);
// Reverse each word in a sentence String result = Arrays.stream(str.split(" ")) .map(s -> new StringBuilder(s).reverse().toString()) .collect(Collectors.joining(" "));
// Sum digits of a numeric string int sum = str.chars() .map(c -> c - '0') // char to int digit .sum();
// First duplicate number Optional<Integer> dup = list.stream() .collect(Collectors.groupingBy( Function.identity(), LinkedHashMap::new, Collectors.counting())) .entrySet().stream() .filter(e -> e.getValue() > 1) .map(Map.Entry::getKey) .findFirst();
// Flatten comma-separated strings List<String> flat = list.stream() .flatMap(s -> Arrays.stream(s.split(","))) .toList();
// Intersection of two lists List<Integer> inter = list1.stream() .filter(list2::contains).toList(); // Union of two lists List<Integer> union = Stream.concat( list1.stream(), list2.stream()).distinct().toList();
// Sort elements by frequency Map<Integer, Long> freqMap = list.stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting())); List<Integer> sorted = freqMap.entrySet().stream() .sorted(Map.Entry.<Integer,Long>comparingByValue().reversed() .thenComparing(Map.Entry::getKey)) .flatMap(e -> Collections.nCopies( e.getValue().intValue(), e.getKey()).stream()) .toList();
// Group anagrams together Map<String, List<String>> anagrams = words.stream() .collect(Collectors.groupingBy(s -> s.chars().sorted() .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()));
// collect with supplier, accumulator, combiner List<String> result = stream.collect( ArrayList::new, // supplier ArrayList::add, // accumulator ArrayList::addAll // combiner (parallel) ); // Custom Collector.of Collector.of( ArrayList::new, List::add, (l1, l2) -> { l1.addAll(l2); return l1; } );
• thenComparing added after comparing — can pass Comparator.reverseOrder() as second param
• Sort alphabetically: just use s → s inside comparing
• No isUpperCase() in streams — use s → s.equals(s.toUpperCase())
• When streaming entrySet: e → e.getValue() and Map.Entry.comparingByKey() / comparingByValue()
• Collectors.counting() — count per group
• Collectors.minBy(cmp) / maxBy(cmp)
• Collectors.summingInt() / averagingDouble()
• stream().mapToInt(Integer::intValue).summaryStatistics()
• Add combiner as 3rd param to reduce: e.g. Integer::sum
• Only use associative operations: a+b=b+a ✓
• Non-associative ops (subtraction) give wrong results with parallel ✗
• Files.lines(path) — process large files lazily
Always use .orElse() or .orElseThrow() to unwrap
Return Optional
Short-circuit
Common sources