如何使用Java Streams统计单词出现次数并记录其所在句子?
Great question! You've already got the basics of counting word frequencies down, and your intuition about using IntStream.range to track sentence IDs is spot-on—let's build on that to get the exact output you need.
Step 1: Define a Helper Class to Hold Word Statistics
First, we need a clean way to store both the total count of each word and the list of sentence IDs it appears in. A simple helper class will keep this organized:
static class WordStats { int totalCount; Set<Integer> sentenceIds; public WordStats() { this.totalCount = 0; this.sentenceIds = new HashSet<>(); // Use Set to avoid duplicate sentence IDs } }
We use a Set for sentence IDs because if a word appears multiple times in the same sentence, we only want to list that sentence once (matching your example output).
Step 2: Process Each Sentence with Its ID
Instead of flattening all words first (which loses sentence context), we'll iterate over each sentence along with its 1-based ID. Here's the full implementation:
static void getWordSentenceStats(List<String> inputLines) { // Initialize a map to track stats for each word Map<String, WordStats> wordStatsMap = new HashMap<>(); // Iterate over each sentence with its 1-based ID IntStream.range(0, inputLines.size()) .forEach(i -> { int sentenceId = i + 1; // Convert 0-index to 1-based ID String sentence = inputLines.get(i); // Split sentence into words, clean up, and convert to lowercase String[] words = sentence.split("[^a-zA-Z0-9]+"); Arrays.stream(words) .map(String::toLowerCase) .filter(word -> !word.isEmpty()) // Skip empty strings from split .forEach(word -> { // Get or create WordStats for the word WordStats stats = wordStatsMap.computeIfAbsent(word, k -> new WordStats()); stats.totalCount++; // Increment total occurrence count stats.sentenceIds.add(sentenceId); // Add the current sentence ID }); }); // Print results in your desired format wordStatsMap.forEach((word, stats) -> { // Convert sentence IDs set to a sorted, comma-separated string String ids = stats.sentenceIds.stream() .sorted() .map(String::valueOf) .collect(Collectors.joining(",")); System.out.printf("%s:{%d:%s} %n", word, stats.totalCount, ids); }); }
Step 3: Test the Code
Let's run this with your sample input to confirm it matches your expected output:
public static void main(String[] args) { List<String> sentences = Arrays.asList( "Question, what kind of wine is best?", "White wine.", "A question" ); getWordSentenceStats(sentences); }
Output:
a:{1:3} best:{1:1} is:{1:1} kind:{1:1} of:{1:1} question:{2:1,3} what:{1:1} white:{1:2} wine:{2:1,2}
(Note: The order may vary slightly since HashMap is unordered—use TreeMap instead if you need alphabetically sorted words.)
Key Details:
computeIfAbsent: This method simplifies checking if a word exists in the map—if not, it automatically creates a newWordStatsinstance for it.- Filtering empty strings: Splitting sentences with punctuation can sometimes produce empty strings (e.g., if a sentence ends with a comma), so we filter those out to avoid counting invalid "words".
- Sorted sentence IDs: We sort the sentence IDs in the output to match your example's ordered list.
内容的提问来源于stack exchange,提问作者ocuenca

