実装をストリームAPIと交換した後、JUnitテストが失敗するのはなぜですか?

deHaar

Stringsの概要と、Map<String, List<String>>:の値でのそれらの出現を提供する次のメソッドを実装しまし

public static Map<String, Long> getValueItemOccurrences(Map<String, List<String>> map) {
    Map<String, Long> occurrencesOfValueItems = new HashMap<>();

    map.forEach((key, value) -> {
        value.forEach(item -> {
            if (occurrencesOfValueItems.containsKey(item)) {
                occurrencesOfValueItems.put(item, occurrencesOfValueItems.get(item) + 1);
            } else {
                occurrencesOfValueItems.put(item, 1L);
            }
        });
    });

    return occurrencesOfValueItems;
}

単一のJUnitテストでテストしましたが、テストは成功しました。ここにあります(現在はインポートも含まれています):

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

class TryoutTest {

    static Map<String, List<String>> items = new HashMap<>();
    static List<String> largeList = new ArrayList<String>();
    static List<String> mediumList = new ArrayList<String>();       
    static List<String> smallList = new ArrayList<String>();
    static List<String> differentLargeList = new ArrayList<String>();
    static List<String> differentSmallList = new ArrayList<String>();
    static List<String> anotherList = new ArrayList<String>();
    static List<String> someList = new ArrayList<String>();
    static List<String> justAList = new ArrayList<String>();

    @BeforeAll
    static void setup() {
        largeList.add("Alfred");
        largeList.add("Bakari");
        largeList.add("Christian");
        largeList.add("Dong");
        largeList.add("Etienne");
        largeList.add("Francesco");
        largeList.add("Guido");
        largeList.add("Henrik");
        largeList.add("Ivan");
        largeList.add("Jos");
        largeList.add("Kumar");
        largeList.add("Leonard");
        largeList.add("Marcin");
        largeList.add("Nico");
        largeList.add("Olof");
        items.put("fifteen-01", largeList);

        mediumList.add("Petar");
        mediumList.add("Quentin");
        mediumList.add("Renato");
        mediumList.add("Sadio");
        mediumList.add("Tomislav");
        mediumList.add("Ulrich");
        mediumList.add("Volkan");
        mediumList.add("Wladimir");
        items.put("eight-01", mediumList);

        smallList.add("Xavier");
        smallList.add("Yves");
        smallList.add("Zinedine");
        smallList.add("Alfred");
        items.put("four-01", smallList);

        differentLargeList.add("Bakari");
        differentLargeList.add("Christian");
        differentLargeList.add("Dong");
        differentLargeList.add("Etienne");
        differentLargeList.add("Francesco");
        differentLargeList.add("Xavier");
        differentLargeList.add("Yves");
        differentLargeList.add("Wladimir");
        differentLargeList.add("Jens");
        differentLargeList.add("Hong");
        differentLargeList.add("Le");
        differentLargeList.add("Leigh");
        differentLargeList.add("Manfred");
        differentLargeList.add("Anders");
        differentLargeList.add("Rafal");
        items.put("fifteen-02", differentLargeList);

        differentSmallList.add("Dario");
        differentSmallList.add("Mohammad");
        differentSmallList.add("Abdul");
        differentSmallList.add("Alfred");
        items.put("four-02", differentSmallList);

        anotherList.add("Kenneth");
        anotherList.add("Hong");
        anotherList.add("Bakari");
        anotherList.add("Ulrich");
        anotherList.add("Henrik");
        anotherList.add("Bernd");
        anotherList.add("Samuel");
        anotherList.add("Ibrahim");
        items.put("eight-02", anotherList);

        someList.add("Kumar");
        someList.add("Konrad");
        someList.add("Bakari");
        someList.add("Francesco");
        someList.add("Leigh");
        someList.add("Yves");
        items.put("six-01", someList);

        justAList.add("Bakari");
        items.put("one-01", justAList);
    }

    @Test
    void valueOccurrencesTest() {
        Map<String, Integer> expected = new HashMap<>();
        expected.put("Abdul", 1);
        expected.put("Alfred", 3);
        expected.put("Anders", 1);
        expected.put("Bakari", 5);
        expected.put("Bernd", 1);
        expected.put("Christian", 2);
        expected.put("Dario", 1);
        expected.put("Dong", 2);
        expected.put("Etienne", 2);
        expected.put("Francesco", 3);
        expected.put("Guido", 1);
        expected.put("Henrik", 2);
        expected.put("Hong", 2);
        expected.put("Ibrahim", 1);
        expected.put("Ivan", 1);
        expected.put("Jens", 1);
        expected.put("Jos", 1);
        expected.put("Kenneth", 1);
        expected.put("Konrad", 1);
        expected.put("Kumar", 2);
        expected.put("Le", 1);
        expected.put("Leigh", 2);
        expected.put("Leonard", 1);
        expected.put("Manfred", 1);
        expected.put("Marcin", 1);
        expected.put("Mohammad", 1);
        expected.put("Nico", 1);
        expected.put("Olof", 1);
        expected.put("Petar", 1);
        expected.put("Quentin", 1);
        expected.put("Rafal", 1);
        expected.put("Renato", 1);
        expected.put("Sadio", 1);
        expected.put("Samuel", 1);
        expected.put("Tomislav", 1);
        expected.put("Ulrich", 2);
        expected.put("Volkan", 1);
        expected.put("Wladimir", 2);
        expected.put("Xavier", 2);
        expected.put("Yves", 3);
        expected.put("Zinedine", 1);
        assertThat(FunctionalMain.getValueItemOccurrences(items), is(expected));
    }
}

メソッドの実装をに変更すると

public static Map<String, Long> getValueItemOccurrences(Map<String, List<String>> map) {
    return map.values().stream()
            .flatMap(Collection::stream)
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}

テストケースは失敗し、結果のマップが期待されるマップと等しくないことを示します。この日食のスクリーンショットを参照してください。これは、明らかに、要素の順序によってテストが失敗することを示しています。

ここに画像の説明を入力してください

本当にそれだけですか?私は、HashMapsは一般的にキーの順序を保証しないことを読んだと思います。

私の(かなり長い)質問は次のとおりです。ストリームAPI呼び出しでテストに合格する結果を生成するにはどうすればよいですか、それともテストケースを変更する必要がありますか、別のアサーションを使用する必要がありますか?

いくつかのサブ質問は次のとおりです。

  • このメソッドにストリームAPIを使用するための代替/より良い方法はありますか?
  • Map順序が重要な場合(TreeMap多分)、特定の実装を返す必要がありますか?
ユジン

TL; DRテストが壊れています、修正してください。

まず第一に、これは次の方法でより簡単に再現できます。

List<String> list = ImmutableList.of("Kumar", "Kumar", "Jens");

public static Map<String, Long> getValueItemOccurrences1(List<String> list) {
    return list
        .stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}

public static Map<String, Long> getValueItemOccurrences2(List<String> list) {
    Map<String, Long> occurrencesOfValueItems = new HashMap<>();

    list.forEach(item -> {
        if (occurrencesOfValueItems.containsKey(item)) {
            occurrencesOfValueItems.put(item, occurrencesOfValueItems.get(item) + 1);
        } else {
            occurrencesOfValueItems.put(item, 1L);
        }
    });

    return occurrencesOfValueItems;
}

問題は、内部HashMap::hash(再ハッシュとも呼ばれる)と、選択するバケットを決定するときに実際に重要な最後のビットを取得した後、それらが同じ値を持つことです。

    System.out.println(hash("Kumar".hashCode()) & 15);
    System.out.println(hash("Jens".hashCode()) & 15);

簡単に言うと、HashMapはエントリの場所に基づいてエントリを配置する場所を決定します(バケットが選択されhashCodeます)。まあ、ほとんどの場合、hashCodeが計算されると、内部で別の処理がhash行われます-エントリをより適切に分散させるためです。の最終int値はhashCode、バケットを決定するために使用されます。16new HashMapたとえば)のデフォルト容量でHashMapを作成する場合、エントリの移動先は最後の4ビットのみが重要です(そのため& 15、最後の4ビットを確認するためにそこで作成しました)。

はどこhashですか:

// xor first 16 and last 16 bits
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

ここで、上記のアルゴリズムが適用された後、["Kumar" and "Jens"] または ["Xavier", "Kenneth", "Samuel"]同じ最後の4桁(最初のケースでは3桁、2番目のケースでは1桁)であることがわかります。

この情報がわかったので、これは実際にはさらに単純化できます。

Map<String, Long> map = new HashMap<>();
map.put("Kumar", 2L);
map.put("Jens", 1L);

System.out.println(map); // {Kumar=2, Jens=1}

map = new HashMap<>();
map.computeIfAbsent("Kumar", x -> 2L);
map.computeIfAbsent("Jens", x -> 1L);
System.out.println(map); // {Jens=1, Kumar=2}

これが内部で使用さmap.computeIfAbsentれているものでCollectors.groupingByあるため、私は使用しました


これは、ことが判明したputcomputeIfAbsent、中の要素を入れてHashMap別の方法を使用しました。マップには順序がないため、これは完全に許可されます。これらの要素は、インポート部分である同じバケットに入れられます。したがって、コードをキーごとにテストします。以前のテストコードは壊れていました。


あなたが望むなら、これはさらに楽しい読書です:

HashMap::putLinkedTreeエントリが作成されるまで)ある方法で要素を追加するため、1つの要素が存在する場合、他のすべての要素は次のように追加されます。

one --> next --> next ... so on.

要素はend of this queueputメソッドに入るときにに追加されます。

一方computeIfAbsent、少し異なり、キューの先頭に要素を追加します。上記の例をとると、最初Xavierに追加されます。次に、Kennethが追加されると、最初になります。

 Kenneth -> Xavier // Xavier was "first"

ときにSamuel追加され、それが最初に次のようになります。

 Samuel -> [Kenneth -> Xavier] 

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

PhantomJSを使用してChutzpahで実行すると、typescriptテストが失敗するのはなぜですか?

分類Dev

テストが失敗した後、@ Afterメソッドの前にJUnit 4.8でコードを実行するにはどうすればよいですか?

分類Dev

この未定義のケースで、ダイクストラのアルゴリズムの実装が失敗するのはなぜですか?

分類Dev

この未定義のケースで、ダイクストラのアルゴリズムの実装が失敗するのはなぜですか?

分類Dev

AntビルドシステムでJUnitテストが失敗するのはなぜですか?

分類Dev

アイテムをジェネリック型にキャストできないのはなぜですか?ジェネリック型はインターフェイスであり、アイテムがそのインターフェイスを実装していることを確認した後ですか?

分類Dev

JUnitテストの例外が常に失敗するのはなぜですか?

分類Dev

次のコードでベクトルの代わりにリストを使用すると、イテレータ間で減算を実行しようとした行でコンパイルが失敗するのはなぜですか?

分類Dev

pinentryをインストールしたときにgpgが失敗するのはなぜですか?

分類Dev

認証コードからのアクセストークン交換の呼び出しが失敗したのはなぜですか?

分類Dev

パラメータを追加した後、ユニットテストが失敗するのはなぜですか?

分類Dev

mochaiとchaiを介してテストするときに、スローエラーテストに失敗したのはなぜですか?

分類Dev

LinkedListメソッドのJUnitテストが失敗したのはなぜですか?

分類Dev

コードがIllegalArgumentExceptionのJUnitテストに失敗するのはなぜですか?

分類Dev

JUnitで実際の値と期待値が同じ場合、テストが失敗するのはなぜですか?

分類Dev

Gradleが失敗したテストでビルドを成功としてマークするのはなぜですか?

分類Dev

udev から実行するとスクリプトが失敗するのはなぜですか?

分類Dev

Googleカスタム検索APIの呼び出しがリクエストエラー(無効な引数)で失敗するのはなぜですか?

分類Dev

スレッドで実行すると、一部のステートメントが実行に失敗するのはなぜですか?

分類Dev

カスタムローカルリポジトリとのapklib依存関係がないためにビルドが失敗するのはなぜですか?

分類Dev

Spring 3.1 WebMvcConfigを使用した単体テストが失敗するのはなぜですか?

分類Dev

glimpseが失敗したリクエストを無視するのはなぜですか?

分類Dev

再インストールを回避しながら、失敗した HDD を Ubuntu で SSD に交換する

分類Dev

iOSの重複した実装->テストが失敗する

分類Dev

失敗したRubyユニットテストが失敗しないのはなぜですか?

分類Dev

RecyclerViewでswipeRightを実行しても、テストが失敗しないのはなぜですか?

分類Dev

前のステートメントが失敗したときにステートメントの実行を確実にするクリーンな方法

分類Dev

C#MediaElement-ソースを切り替えた後、Play()がサイレントに失敗することがあるのはなぜですか?

分類Dev

このルビーテストが失敗するのはなぜですか?

Related 関連記事

  1. 1

    PhantomJSを使用してChutzpahで実行すると、typescriptテストが失敗するのはなぜですか?

  2. 2

    テストが失敗した後、@ Afterメソッドの前にJUnit 4.8でコードを実行するにはどうすればよいですか?

  3. 3

    この未定義のケースで、ダイクストラのアルゴリズムの実装が失敗するのはなぜですか?

  4. 4

    この未定義のケースで、ダイクストラのアルゴリズムの実装が失敗するのはなぜですか?

  5. 5

    AntビルドシステムでJUnitテストが失敗するのはなぜですか?

  6. 6

    アイテムをジェネリック型にキャストできないのはなぜですか?ジェネリック型はインターフェイスであり、アイテムがそのインターフェイスを実装していることを確認した後ですか?

  7. 7

    JUnitテストの例外が常に失敗するのはなぜですか?

  8. 8

    次のコードでベクトルの代わりにリストを使用すると、イテレータ間で減算を実行しようとした行でコンパイルが失敗するのはなぜですか?

  9. 9

    pinentryをインストールしたときにgpgが失敗するのはなぜですか?

  10. 10

    認証コードからのアクセストークン交換の呼び出しが失敗したのはなぜですか?

  11. 11

    パラメータを追加した後、ユニットテストが失敗するのはなぜですか?

  12. 12

    mochaiとchaiを介してテストするときに、スローエラーテストに失敗したのはなぜですか?

  13. 13

    LinkedListメソッドのJUnitテストが失敗したのはなぜですか?

  14. 14

    コードがIllegalArgumentExceptionのJUnitテストに失敗するのはなぜですか?

  15. 15

    JUnitで実際の値と期待値が同じ場合、テストが失敗するのはなぜですか?

  16. 16

    Gradleが失敗したテストでビルドを成功としてマークするのはなぜですか?

  17. 17

    udev から実行するとスクリプトが失敗するのはなぜですか?

  18. 18

    Googleカスタム検索APIの呼び出しがリクエストエラー(無効な引数)で失敗するのはなぜですか?

  19. 19

    スレッドで実行すると、一部のステートメントが実行に失敗するのはなぜですか?

  20. 20

    カスタムローカルリポジトリとのapklib依存関係がないためにビルドが失敗するのはなぜですか?

  21. 21

    Spring 3.1 WebMvcConfigを使用した単体テストが失敗するのはなぜですか?

  22. 22

    glimpseが失敗したリクエストを無視するのはなぜですか?

  23. 23

    再インストールを回避しながら、失敗した HDD を Ubuntu で SSD に交換する

  24. 24

    iOSの重複した実装->テストが失敗する

  25. 25

    失敗したRubyユニットテストが失敗しないのはなぜですか?

  26. 26

    RecyclerViewでswipeRightを実行しても、テストが失敗しないのはなぜですか?

  27. 27

    前のステートメントが失敗したときにステートメントの実行を確実にするクリーンな方法

  28. 28

    C#MediaElement-ソースを切り替えた後、Play()がサイレントに失敗することがあるのはなぜですか?

  29. 29

    このルビーテストが失敗するのはなぜですか?

ホットタグ

アーカイブ