簡體   English   中英

Java Streams | groupingBy相同的元素

[英]Java Streams | groupingBy same elements

我有一個單詞流,我想根據相同元素(=單詞)的出現對它們進行排序。

例如:{hello,world,hello}

Map<String, List<String>>

你好你好你好}

世界,{世界}

到目前為止我有什么:

Map<Object, List<String>> list = streamofWords.collect(Collectors.groupingBy(???));

問題1:流似乎丟失了他正在處理字符串的信息,因此編譯器強制我將類型更改為Object,List

問題2:我不知道在胃腸道內放入什么,以同樣的方式將其分組。 我知道我能夠處理lambda表達式中的單個元素,但我不知道如何到達每個元素的“外部”以檢查是否相等。

謝謝

要獲取Map<String, List<String>> ,您只需要告訴groupingBy收集器您要按標識對值進行分組,因此函數x -> x

Map<String, List<String>> occurrences = 
     streamOfWords.collect(groupingBy(str -> str));

然而,這有點無用,因為你看到你有兩次相同類型的信息。 您應該查看Map<String, Long> ,其中值表示Stream中String的出現。

Map<String, Long> occurrences = 
     streamOfWords.collect(groupingBy(str -> str, counting()));

基本上不是使用groupingBy返回值作為List ,而是使用下游收集器counting()來告訴您要計算此值出現的次數。

你的排序要求應該意味着你應該有一個Map<Long, List<String>> (如果不同的字符串出現的次數是多少?),並且由於默認的toMap collector返回一個HashMap ,它沒有排序的概念,但您可以將元素存儲在TreeMap


我試着總結一下我在評論中所說的內容。

你似乎對str -> str如何判斷“你好”或“世界”是否有所不同感到麻煩。

首先str -> str是一個函數,也就是說,對於輸入x,產生一個值f(x)。 例如, f(x) = x + 2是對於任何值x返回x + 2的函數。

這里我們使用identity函數,即f(x) = x 當您從Map收集管道中的元素時,將在調用此函數之前從該值獲取鍵。 所以在你的例子中,你有3個身份函數產生的元素:

f("hello") = "hello"
f("world") = "world"

到現在為止還挺好。

現在,當調用collect()時,對於流中的每個值,您將在其上應用函數並計算結果(這將是Map的鍵)。 如果一個鍵已經存在,我們將獲取當前映射的值,並在List合並我們想要放置的值(即剛剛應用該函數的值)與此先前的映射值。 這就是你最后得到Map<String, List<String>>的原因。

讓我們再看一個例子。 現在流包含值“hello”,“world”和“hey”,我們想要應用於組合元素的函數是str -> str.substring(0, 2) ,即取得的函數字符串的前兩個字符。

同樣,我們有:

f("hello") = "he"
f("world") = "wo"
f("hey") = "he"

在這里,您會看到“hello”和“hey”在應用函數時產生相同的鍵,因此在收集它們時它們將被分組在同一個List ,因此最終結果為:

"he" -> ["hello", "hey"]
"wo" -> ["world"]

要與數學進行類比,你可以采用任何非雙射函數,例如x 2 對於x = -2x = 2我們得到f(x) = 4 因此,如果我們通過此函數對整數進行分組,則-2和2將位於相同的“包”中。

查看源代碼不會幫助您了解最初發生的情況。 如果你想知道它是如何在幕后實現的話,它會很有用。 但是首先嘗試用更高級別的抽象來思考這個概念,然后事情會變得更加清晰。

希望能幫助到你! :)

您要搜索的KeyExtractor是標識功能:

Map<String, List<String>> list = streamofWords.collect(Collectors.groupingBy(Function.identity()));

編輯補充說明:

  • Function.identity()使用一個方法返回一個'Function',它只返回它獲得的參數。
  • Collectors.groupingBy(Function<S, K> keyExtractor)提供了一個收集器,它將流的所有元素收集到Map<K, List<S>> 它使用keyExtractor實現來檢查流的S類對象,並從中推導出類型為K的鍵。 此鍵是映射的鍵,用於獲取(或創建)添加了流元素的結果映射中的列表。

如果你想按對象的某些字段進行分組,而不是整個對象,並且你不想改變你的equals和hashCode方法,我會創建一個包含一組鍵的類,用於分組:

import java.util.Arrays;

@Getter
public class MultiKey {

    public MultiKey(Object... keys) {
        this.keys = keys;
    }

    private Object[] keys;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MultiKey multiKey = (MultiKey) o;
        return Arrays.equals(keys, multiKey.keys);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(keys);
    }

}

groupingBy本身:

Map<MultiKey, List<VhfEventView>> groupedList = list
        .stream()
        .collect(Collectors.groupingBy(
                 e -> new MultiKey(e.getGroupingKey1(), e.getGroupingKey2())));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM