Java 8 Collectors toMap

1. Perkenalan

Dalam tutorial singkat ini, kita akan berbicara tentang metode toMap () dari kelas Collectors . Kami akan menggunakannya untuk mengumpulkan Stream menjadi instance Peta .

Untuk semua contoh yang dibahas di sini, kami akan menggunakan daftar buku sebagai titik awal dan mengubahnya menjadi implementasi Peta yang berbeda .

2. Daftar ke Peta

Kami akan mulai dengan kasus paling sederhana, dengan mengubah Daftar menjadi Peta .

Kelas Buku kami didefinisikan sebagai:

class Book { private String name; private int releaseYear; private String isbn; // getters and setters }

Dan kami akan membuat daftar buku untuk memvalidasi kode kami:

List bookList = new ArrayList(); bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318")); bookList.add(new Book("The Two Towers", 1954, "0345339711")); bookList.add(new Book("The Return of the King", 1955, "0618129111"));

Untuk skenario ini kita akan menggunakan overload metode toMap () berikut :

Collector
    
      toMap(Function keyMapper, Function valueMapper)
    

Dengan toMap , kita dapat menunjukkan strategi bagaimana mendapatkan kunci dan nilai untuk peta:

public Map listToMap(List books) { return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName)); }

Dan kita dapat dengan mudah memvalidasi kerjanya dengan:

@Test public void whenConvertFromListToMap() { assertTrue(convertToMap.listToMap(bookList).size() == 3); }

3. Memecahkan Konflik Kunci

Contoh di atas berfungsi dengan baik, tetapi apa yang akan terjadi jika ada kunci duplikat?

Mari kita bayangkan bahwa kita memasukkan Peta kita pada setiap tahun rilis Buku :

public Map listToMapWithDupKeyError(List books) { return books.stream().collect( Collectors.toMap(Book::getReleaseYear, Function.identity())); }

Mengingat daftar buku kami sebelumnya, kami akan melihat IllegalStateException :

@Test(expected = IllegalStateException.class) public void whenMapHasDuplicateKey_without_merge_function_then_runtime_exception() { convertToMap.listToMapWithDupKeyError(bookList); }

Untuk mengatasinya, kita perlu menggunakan metode berbeda dengan parameter tambahan, mergeFunction :

Collector toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction) 

Mari perkenalkan fungsi penggabungan yang menunjukkan bahwa, dalam kasus tabrakan, kita tetap menggunakan entri yang ada:

public Map listToMapWithDupKey(List books) { return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (existing, replacement) -> existing)); }

Atau, dengan kata lain, kita mendapatkan perilaku pemenang pertama:

@Test public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() { Map booksByYear = convertToMap.listToMapWithDupKey(bookList); assertEquals(2, booksByYear.size()); assertEquals("0395489318", booksByYear.get(1954).getIsbn()); }

4. Jenis Peta Lainnya

Secara default, metode toMap () akan mengembalikan HashMap .

Tapi bisakah kita mengembalikan implementasi Map yang berbeda ? Jawabannya iya:

Collector toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)

Di mana mapSupplier adalah fungsi yang mengembalikan Peta baru yang kosong beserta hasilnya.

4.1. Daftar ke ConcurrentMap

Mari ambil contoh yang sama seperti di atas dan tambahkan fungsi mapSupplier untuk mengembalikan ConcurrentHashMap:

public Map listToConcurrentMap(List books) { return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(), (o1, o2) -> o1, ConcurrentHashMap::new)); }

Mari lanjutkan dan uji kode kita:

@Test public void whenCreateConcurrentHashMap() { assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap); }
4.2. Peta yang Disortir

Terakhir, mari kita lihat bagaimana mengembalikan peta yang diurutkan. Untuk itu kita akan menggunakan TreeMap sebagai parameter mapSupplier .

Karena TreeMap diurutkan menurut urutan natural kuncinya secara default, kita tidak perlu mengurutkan sendiri buku secara eksplisit :

public TreeMap listToSortedMap(List books) { return books.stream() .collect( Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new)); }

Jadi dalam kasus kami, TreeMap yang dikembalikan akan diurutkan menurut abjad berdasarkan nama buku:

@Test public void whenMapisSorted() { assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals( "The Fellowship of the Ring")); }
5. Kesimpulan

Pada artikel ini, kita melihat ke metode toMap () dari kelas Collectors . Ini memungkinkan kami membuat Peta baru dari Stream . Kami juga belajar bagaimana menyelesaikan konflik utama dan membuat implementasi peta yang berbeda.

Seperti biasa, kode tersedia di GitHub.