如何使用Stream重构统计日期编码次数的Java方法?
Hey there! Let's take that date-counting method and refactor it using Java Streams—they're made for exactly this kind of aggregation work, and will make your code shorter, more readable, and more efficient than the nested loop approach you have now.
First, let's recap what your original code does: it takes a LinkedList<String> of dates, creates a Set to get unique values, then loops through each unique date and counts how many times it appears in the original list. This works, but it's O(n²) time complexity because of the nested loops. Streams can do this in a single pass (O(n) time) with much cleaner syntax.
Here are a few solid ways to refactor it:
1. Using groupingBy + counting() (with type conversion)
This is the most straightforward approach using standard collectors:
import java.util.function.Function; import java.util.stream.Collectors; public Map<String, Integer> countCodingsByDate() { return codingHistory.historyDate.stream() .collect(Collectors.groupingBy( Function.identity(), // Use the date string itself as the map key Collectors.collectingAndThen(Collectors.counting(), Long::intValue) )); }
groupingBy(Function.identity())groups all identical date strings togetherCollectors.counting()tallies the number of elements in each group (returns aLong)collectingAndThen(..., Long::intValue)converts theLongcount to anIntegerto match your original method's return type
2. Using groupingBy + summingInt() (direct Integer count)
If you want to avoid converting from Long to Integer, use summingInt instead—it increments a counter by 1 for each element in the group:
import java.util.function.Function; import java.util.stream.Collectors; public Map<String, Integer> countCodingsByDate() { return codingHistory.historyDate.stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.summingInt(date -> 1) // Add 1 for each occurrence of the date )); }
This is even more concise and returns Integer counts directly, no conversion needed.
3. Using toMap with a merge function
Another clean option is Collectors.toMap, where we define how to handle duplicate keys (by summing their counts):
import java.util.function.Function; import java.util.stream.Collectors; public Map<String, Integer> countCodingsByDate() { return codingHistory.historyDate.stream() .collect(Collectors.toMap( Function.identity(), // Key is the date string date -> 1, // Initial count for each date is 1 Integer::sum // Merge duplicate keys by adding their counts )); }
This is super intuitive—every time we encounter a date, we start with a count of 1, and if the date already exists in the map, we add the new count (1) to the existing one.
All these methods will produce the exact same output as your original code:
{28-11-2019=5, 29-11-2019=3}
Not only are these versions shorter, but they're also more efficient (single pass through the list) and easier to maintain—no manual set creation or nested loops to keep track of.
内容的提问来源于stack exchange,提问作者Natali

