2015-11-22 35 views
3

我有一個單詞列表,說的Java 8 - 詞的計數,然後在遞減順序排列

List<String> words = Arrays.asList("Hello alan i am here where are you"+ 
    "and what are you doing hello are you there"); 

我怎樣才能獲得前七名的話,其按降序重複不止一次在列表中?然後單個詞條應按字母順序排列。所以上面的輸出應該是那些前七位字

you (3) 
are (2) 
hello (2) 
alan (1) 
am (1) 
and (1) 
doing (1) 

我正在看Java-8使用流,lamda來做這件事。

我想用這種方式。 首先排序列表 二獲得單詞的地圖,其在單詞列表單詞計數。

List<String> sortedWords = Arrays.asList("Hello alan i am here where are you and what are you doing hello you there".split(" ")) 
      .stream().sorted().collect(toList()); 

Map<String, Long> collect = 
      sortedWords.stream().collect(groupingBy(Function.identity(), counting())); 

回答

8

最困難的部分是排序。由於您只想保留結果中的7個第一個元素,並且您想按照其值對Map進行排序,所以我們需要創建所有結果的Map,對其進行排序,然後保留7個結果。

在下面的代碼,每一個字是小寫和分組本身,計數出現次數的數目。然後,我們需要對這張地圖進行排序,以便在條目上創建一個Stream,根據值(按降序排序),然後根據鍵進行排序。保留7個第一個元素,將其映射到他們的鍵(對應於該詞)並收集到List中,從而保持相遇順序。

public static void main(String[] args) { 
    String sentence = "Hello alan i am here where are you and what are you doing hello are you there"; 
    List<String> words = Arrays.asList(sentence.split(" ")); 

    List<String> result = 
      words.stream() 
       .map(String::toLowerCase) 
       .collect(groupingBy(identity(), counting())) 
       .entrySet().stream() 
       .sorted(Map.Entry.<String, Long> comparingByValue(reverseOrder()).thenComparing(Map.Entry.comparingByKey())) 
       .limit(7) 
       .map(Map.Entry::getKey) 
       .collect(toList()); 

    System.out.println(result); 
} 

輸出:

[are, you, hello, alan, am, and, doing] 

請注意,您在您想要的輸出犯了一個錯誤:"are"實際上看起來像"you" 3倍,所以應該是前

注:此代碼假定大量的靜電進口,即:

import static java.util.Comparator.reverseOrder; 
import static java.util.function.Function.identity; 
import static java.util.stream.Collectors.counting; 
import static java.util.stream.Collectors.groupingBy; 
import static java.util.stream.Collectors.toList; 
+3

這裏我更傾向於將讓這個兩個顯式流管道,而不是做.entrySet()流處理計數,則()在「中間」,因爲它更清楚實際發生了什麼 - 您正在執行兩個不同的流操作。這增強了可讀性,而沒有任何運行成本。 (更一般地說,雖然方法鏈是做飯,但很容易被過度使用 - 只是因爲你可以鏈接,並不意味着你必須一直這樣做。) –

+0

@Brain,我會拿這個,因爲它會更具可讀性如上所述 – bhupen

1

我是一個簡單的人,所以我會使用一個Map<String, Integer>先計算每個字。 然後創建爲每個計數的TreeSet,儲存那些在TreeMap<Integer, TreeSet>。從那裏應該是相當簡單的。

4

雖然@Tunaki解決方案是偉大的,有趣的是使用my StreamEx library,它能夠解決單個流管道的問題(沒有實際操作,直到單個終端操作被稱爲執行):

Map<String, Long> map = StreamEx.of(words) 
    .map(String::toLowerCase) 
    .sorted() // sort original words, so now repeating words are next to each other 
    .runLengths() // StreamEx feature: squash repeating words into Entry<String, Long> 
    .sorted(Entry.<String, Long> comparingByValue().reversed() 
       .thenComparing(Entry.comparingByKey())) 
    .limit(7) // Sort and limit 
    .toCustomMap(LinkedHashMap::new); // Single terminal operation: store to LinkedHashMap 

或者,如果只需要單詞:

List<String> list =StreamEx.of(words) 
    .map(String::toLowerCase) 
    .sorted() // sort original words, so now repeating words are next to each other 
    .runLengths() // StreamEx feature: squash repeating words into Entry<String, Long> 
    .sorted(Entry.<String, Long> comparingByValue().reversed() 
       .thenComparing(Entry.comparingByKey())) 
    .limit(7) // Sort and limit 
    .keys() // Drop counts leaving only words 
    .toList(); // Single terminal operation: store to List 
+2

我讀的答案越多,我越相信您的StreamEx庫首先應該放在API中! – Tunaki

+0

@Tagir,太棒了,我會檢查你的StreamEx。 – bhupen

2

有時問題的最佳解決方案不是算法,而是數據結構。我認爲你需要的是一個袋子。由於您希望按出現次數和按鍵次序對輸出進行排序,因此您應該使用的特定數據結構是TreeBag。下面的代碼將工作使用Eclipse Collections與Java 8流:

String string = 
    "Hello alan i am here where are you and what are you doing hello are you there"; 
List<ObjectIntPair<String>> pairs = 
    Stream.of(string.toLowerCase().split(" ")) 
     .collect(Collectors.toCollection(TreeBag::new)) 
     .topOccurrences(7); 
System.out.println(pairs); 

此代碼將輸出:

// Strings with occurrences 
[are:3, you:3, hello:2, alan:1, am:1, and:1, doing:1, here:1, i:1, there:1, what:1, where:1] 

topOccurrences()方法有邏輯來處理的關係,基本上它留給開發人員確定他們如何處理與關係的情況。如果你想正是從這個列表中的第一個七個品,然後你可以鏈.take(7);

一個調用的代碼還可以進一步簡化爲:

List<ObjectIntPair<String>> pairs = 
    TreeBag.newBagWith(string.split(" ")).topOccurrences(7); 
System.out.println(pairs); 

TreeBag.newBagWith()接受可變參數的參數,所以靜態工廠方法你可以直接將String.split()的結果傳遞給它。

注:我是Eclipse集合的提交者。

0

兩步解決方案:組/通過計數​​下降

List<String> words = Arrays.asList("Hello alan i am here where are you and what are you doing hello you there".split(" ")); 

Map<String, Long> collect = words.stream() 
     .map(String::toLowerCase) // convert to lower case 
     .collect(// group and count by name 
       Collectors.groupingBy(Function.identity(), Collectors.counting())); 

collect.keySet().stream() 
     .sorted(// order by count descending, then by name 
       Comparator 
         .comparing(collect::get) 
         .reversed() 
         .thenComparing(Collator.getInstance())) 
     .map(k -> k + " (" + collect.get(k) + ")") // map to name and count string 
     .limit(7) // only first 7 entries 
     .forEach(System.out::println); // output 
相關問題