Panduan untuk pengelompokan Java 8Dengan Pengumpul

1. Pengenalan

Dalam tutorial ini, kita akan melihat bagaimana pengelompokanBy collector berfungsi menggunakan pelbagai contoh.

Untuk memahami bahan yang dibahas dalam tutorial ini, kita memerlukan pengetahuan asas mengenai ciri Java 8. Kita boleh melihat intro Java 8 Streams dan panduan untuk Java 8's Collectors untuk asas-asas ini.

2. pengelompokanBoleh Pengumpul

Java 8 Stream API memungkinkan kita memproses koleksi data secara deklaratif.

Kaedah kilang statik Collectors.groupingBy () dan Collectors.groupingByConcurrent () memberi kami fungsi yang serupa dengan klausa ' GROUP BY' dalam bahasa SQL. Kami menggunakannya untuk mengelompokkan objek dengan beberapa harta benda dan menyimpan hasil dalam contoh Peta .

Kaedah pengelompokan yang berlebihan adalah:

  • Pertama, dengan fungsi klasifikasi sebagai parameter kaedah:

static  Collector
    
     > groupingBy(Function classifier)
    
  • Kedua, dengan fungsi klasifikasi dan pengumpul kedua sebagai parameter kaedah:

static  Collector
    
      groupingBy(Function classifier, Collector downstream)
    
  • Akhirnya, dengan fungsi klasifikasi, kaedah pembekal (yang menyediakan pelaksanaan Peta yang berisi hasil akhir), dan pengumpul kedua sebagai parameter kaedah:

static 
    
      Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
    

2.1. Contoh Penyediaan Kod

Untuk menunjukkan penggunaan groupingBy () , mari kita tentukan kelas BlogPost (kami akan menggunakan aliran objek BlogPost ):

class BlogPost { String title; String author; BlogPostType type; int likes; } 

Seterusnya, Jenis BlogPost :

enum BlogPostType { NEWS, REVIEW, GUIDE } 

Kemudian Senarai untuk blogpost objek:

List posts = Arrays.asList( ... );

Mari juga tentukan kelas Tuple yang akan digunakan untuk mengumpulkan catatan dengan gabungan jenis dan atribut pengarang mereka :

class Tuple { BlogPostType type; String author; } 

2.2. Pengelompokan Ringkas oleh Satu Tiang

Mari kita mulakan dengan kaedah pengelompokan yang paling mudah , yang hanya menggunakan fungsi klasifikasi sebagai parameternya. Fungsi klasifikasi diterapkan pada setiap elemen aliran. Kami menggunakan nilai yang dikembalikan oleh fungsi sebagai kunci peta yang kami dapat dari pengumpul groupingBy .

Untuk mengelompokkan catatan blog dalam senarai catatan blog mengikut jenisnya :

Map
    
      postsPerType = posts.stream() .collect(groupingBy(BlogPost::getType)); 
    

2.3. pengelompokanDengan Jenis Kunci Peta Kompleks

Fungsi klasifikasi tidak terhad untuk mengembalikan hanya nilai skalar atau String. Kunci peta yang dihasilkan boleh menjadi objek apa pun selagi kita memastikan bahawa kita melaksanakan kaedah yang sama dan kaedah kod hash .

Untuk mengelompokkan catatan blog dalam senarai mengikut jenis dan pengarang yang digabungkan dalam contoh Tuple :

Map
    
      postsPerTypeAndAuthor = posts.stream() .collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor()))); 
    

2.4. Mengubah Jenis Nilai Peta yang Dikembalikan

Kelebihan pengelompokan kedua dengan mengambil pemungut kedua tambahan (pengumpul hilir) yang digunakan untuk hasil pemungut pertama.

Apabila kita menentukan fungsi klasifikasi, tetapi bukan pengumpul hilir, pengumpul toList () digunakan di belakang tabir.

Mari gunakan pengumpul toSet () sebagai pengumpul hilir dan dapatkan Set catatan blog (bukannya Senarai ):

Map
    
      postsPerType = posts.stream() .collect(groupingBy(BlogPost::getType, toSet())); 
    

2.5. Pengumpulan mengikut Pelbagai Bidang

Aplikasi pengumpul hiliran yang berbeza adalah melakukan pengelompokan sekunder Dengan hasil kumpulan pertama oleh.

Kepada kumpulan List of blogpost yang pertama oleh penulis dan kemudian oleh jenis :

Map
    
      map = posts.stream() .collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));
    

2.6. Mendapatkan Purata dari Hasil Berkumpulan

Dengan menggunakan pengumpul hilir, kita dapat menerapkan fungsi agregasi dalam hasil fungsi klasifikasi.

Sebagai contoh, untuk mencari bilangan suka purata untuk setiap jenis catatan blog :

Map averageLikesPerType = posts.stream() .collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes))); 

2.7. Mendapatkan Jumlah dari Hasil Berkumpulan

Untuk mengira jumlah suka untuk setiap jenis :

Map likesPerType = posts.stream() .collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes))); 

2.8. Mendapatkan Maksimum atau Minimum dari Hasil Berkumpulan

Another aggregation that we can perform is to get the blog post with the maximum number of likes:

Map
    
      maxLikesPerPostType = posts.stream() .collect(groupingBy(BlogPost::getType, maxBy(comparingInt(BlogPost::getLikes)))); 
    

Similarly, we can apply the minBy downstream collector to get the blog post with the minimum number of likes.

Note that the maxBy and minBy collectors take into account the possibility that the collection to which they are applied could be empty. This is why the value type in the map is Optional.

2.9. Getting a Summary for an Attribute of Grouped Results

The Collectors API offers a summarizing collector that we can use in cases when we need to calculate the count, sum, minimum, maximum and average of a numerical attribute at the same time.

Let's calculate a summary for the likes attribute of the blog posts for each different type:

Map likeStatisticsPerType = posts.stream() .collect(groupingBy(BlogPost::getType, summarizingInt(BlogPost::getLikes))); 

The IntSummaryStatistics object for each type contains the count, sum, average, min and max values for the likes attribute. Additional summary objects exist for double and long values.

2.10. Mapping Grouped Results to a Different Type

We can achieve more complex aggregations by applying a mapping downstream collector to the results of the classification function.

Let's get a concatenation of the titles of the posts for each blog post type:

Map postsPerType = posts.stream() .collect(groupingBy(BlogPost::getType, mapping(BlogPost::getTitle, joining(", ", "Post titles: [", "]")))); 

What we have done here is to map each BlogPost instance to its title and then reduce the stream of post titles to a concatenated String. In this example, the type of the Map value is also different from the default List type.

2.11. Modifying the Return Map Type

When using the groupingBy collector, we cannot make assumptions about the type of the returned Map. If we want to be specific about which type of Map we want to get from the group by, then we can use the third variation of the groupingBy method that allows us to change the type of the Map by passing a Map supplier function.

Let's retrieve an EnumMap by passing an EnumMap supplier function to the groupingBy method:

EnumMap
    
      postsPerType = posts.stream() .collect(groupingBy(BlogPost::getType, () -> new EnumMap(BlogPostType.class), toList())); 
    

3. Concurrent groupingBy Collector

Similar to groupingBy is the groupingByConcurrent collector, which leverages multi-core architectures. This collector has three overloaded methods that take exactly the same arguments as the respective overloaded methods of the groupingBy collector. The return type of the groupingByConcurrent collector, however, must be an instance of the ConcurrentHashMap class or a subclass of it.

To do a grouping operation concurrently, the stream needs to be parallel:

ConcurrentMap
    
      postsPerType = posts.parallelStream() .collect(groupingByConcurrent(BlogPost::getType)); 
    

If we choose to pass a Map supplier function to the groupingByConcurrent collector, then we need to make sure that the function returns either a ConcurrentHashMap or a subclass of it.

4. Java 9 Additions

Java 9 memperkenalkan dua pengumpul baru yang berfungsi dengan baik dengan pengelompokanBy ; lebih banyak maklumat mengenai mereka boleh didapati di sini.

5. Kesimpulan

Dalam artikel ini, kami meneroka penggunaan pengelompokan oleh pengumpul yang ditawarkan oleh Java 8 Collectors API.

Kami belajar bagaimana pengelompokanBy dapat digunakan untuk mengklasifikasikan aliran elemen berdasarkan salah satu atributnya, dan bagaimana hasil klasifikasi ini dapat dikumpulkan, dimutasi, dan dikurangkan menjadi bekas akhir.

Pelaksanaan lengkap contoh dalam artikel ini terdapat dalam projek GitHub.