Current date and time with java.time, concatenating streams, cube-and-filter, sorting an array then streaming it, uppercase mapping, list-to-map with duplicate keys in sorted order, word counts, duplicates with counts, null-safe list iteration with Optional, max of an array, and character counts.
Published September 25, 2026
The second half of the Level 2 stream problems. Pay attention to the three places where commonly shared answers are wrong: a typo'd lambda, a misleading comment about which duplicate "wins" in toMap, and map(note -> Notes::getTagName), which doesn't compile. Knowing why they're wrong is great interview material.
LocalDate today = LocalDate.now(); // 2026-09-25 (no time, no zone)
LocalTime now = LocalTime.now(); // 14:03:27.118
LocalDateTime local = LocalDateTime.now(); // date + time, but NO time zone
ZonedDateTime inIndia = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
Instant instant = Instant.now(); // a UTC point on the timeline: use for timestamps
String formatted = local.format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"));
Key points to cover:
Instant (or OffsetDateTime) for events. LocalDateTime has no zone, so it's ambiguous across servers.Clock (LocalDate.now(clock)) to make time-dependent code testable.java.time types are immutable and thread-safe, unlike Date, Calendar and SimpleDateFormat.Stream<String> all = Stream.concat(list1.stream(), list2.stream());
List<String> merged = Stream.of(list1, list2, list3).flatMap(List::stream).toList(); // three or more sources
Key points to cover:
Stream.concat is lazy. Deeply nested concat calls (in a loop) can cause a StackOverflowError. Use flatMap for many sources.List<Integer> cubes = List.of(4, 5, 6, 7, 1, 2, 3).stream()
.map(i -> i * i * i)
.filter(c -> c > 50)
.toList(); // [64, 125, 216, 343]
Key points to cover:
map must come first.int above 1290. Use mapToLong(i -> (long) i * i * i).int[] arr = { 99, 55, 203, 99, 4, 91 };
Arrays.sort(arr); // or Arrays.parallelSort(arr) for very large arrays
Arrays.stream(arr).forEach(n -> System.out.print(n + " ")); // 4 55 91 99 99 203
int[] sortedCopy = Arrays.stream(arr).sorted().toArray(); // without mutating the original
Common trap: the widely shared version writes forEach(n > System.out.print(...)). That's a typo for ->, and it doesn't compile. Also, parallelSort only pays off for large arrays (thousands of elements).
map to convert strings to uppercase.List<String> upper = names.stream().map(s -> s.toUpperCase(Locale.ROOT)).toList(); // [AA, BB, CC, DD]
Key points to cover:
String::toUpperCase uses the default locale. Pass Locale.ROOT for identifiers and codes, to avoid locale surprises (the Turkish dotless i).Short answer: Use toMap with a merge function and a map supplier. Here, notes are keyed by tag name, and the map is ordered by tag ID, descending:
record Note(int id, String tagName, long tagId) { }
List<Note> notes = List.of(new Note(1, "note1", 11), new Note(2, "note2", 22), new Note(3, "note3", 33),
new Note(4, "note4", 44), new Note(5, "note5", 55), new Note(6, "note4", 66));
Map<String, Long> byTag = notes.stream()
.sorted(Comparator.comparingLong(Note::tagId).reversed()) // 66, 55, 44, 33, 22, 11
.collect(Collectors.toMap(Note::tagName, Note::tagId,
(first, second) -> first, // keep the FIRST one encountered…
LinkedHashMap::new)); // …and keep insertion (sorted) order
// {note4=66, note5=55, note3=33, note2=22, note1=11}
Common trap: believing the comment "keeps 44 for the duplicate key". The stream is sorted descending, so note4 with 66 is encountered first, and the merge function keeps 66. Always reason about the encounter order when you choose (a, b) -> a or (a, b) -> b. For keys sorted by name instead, use TreeMap::new.
List<String> names = List.of("AA", "BB", "AA", "CC");
Map<String, Long> counts = names.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); // {AA=2, BB=1, CC=1} (HashMap order)
Map<String, Long> sortedCounts = names.stream()
.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()));
Map<String, Long> duplicates = names.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() > 1)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // {AA=2}
Key points to cover:
filter(x -> Collections.frequency(names, x) > 1), is O(n²). It's fine for tiny lists, but it's a red flag on large ones. Counting once with groupingBy is O(n).Optional and print each element.Optional.ofNullable(notes)
.orElseGet(List::of) // null → empty list
.stream()
.filter(Objects::nonNull) // skip null elements
.map(Note::tagName) // a method reference
.forEach(System.out::println);
Common trap: the popular answer uses .map(note -> Notes::getTagName). That's a lambda returning a method reference, so it doesn't compile (the target type isn't a functional interface). Write map(Note::tagName) or map(n -> n.tagName()).
Key points to cover:
null collections. Return an empty list, and callers need no Optional gymnastics. Optional.ofNullable(list) is a workaround for legacy APIs.static int findMax(int[] arr) {
return Arrays.stream(arr).max()
.orElseThrow(() -> new IllegalArgumentException("array is empty"));
}
// [12, 19, 20, 88, 0, 9] → 88
Key points to cover:
getAsInt() on an empty OptionalInt throws NoSuchElementException, with no context. Prefer orElseThrow with a message.IntStream.summaryStatistics() gives min, max, sum and average in one pass.String s = "string data to count each character";
Map<Character, Long> counts = s.toLowerCase(Locale.ROOT).chars()
.filter(Character::isLetter) // skip spaces (clarify with the interviewer)
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
// {s=1, t=5, r=3, i=1, n=2, g=1, d=1, a=5, o=2, c=4, u=1, e=2, h=2}
Key points to cover:
chars() avoids the split("") approach, which creates a String object per character.codePoints().Q: How do you find the day of the week, or add business days, with java.time?
A: date.getDayOfWeek(). For business days, iterate with plusDays while skipping SATURDAY/SUNDAY (and holidays from a calendar), or use a TemporalAdjuster.
Q: How do you convert Date to LocalDateTime?
A: date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(). Be explicit about the zone.
Q: How do you join the elements of a list into a comma-separated string?
A: list.stream().map(String::valueOf).collect(Collectors.joining(", ")), or String.join(", ", stringList) when you already have strings.
Q: How do you get the top 3 most frequent words?
A: Count with groupingBy(identity(), counting()), then stream the entry set, sort with Map.Entry.<String, Long>comparingByValue().reversed(), limit(3), and map to the keys.