Berbagi teknologi

Penjelasan mendetail tentang Java Stream API: alat yang ampuh untuk pemrosesan pengumpulan data yang efisien

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

perkenalan

Java 8 memperkenalkan banyak fitur baru, yang paling menonjol adalah ekspresi Lambda dan Stream API. Stream API menyediakan metode yang efisien dan ringkas untuk memproses pengumpulan data, menjadikan kode lebih ringkas dan jelas, serta memiliki keterbacaan dan pemeliharaan yang tinggi. Artikel ini akan mempelajari penggunaan Java Stream API, termasuk konsep dasar, operasi umum, pemrosesan paralel, kasus praktis, dan praktik terbaik.

Daftar isi

  1. Apa itu Streaming API
  2. Operasi dasar Stream API
  3. Operasi lanjutan dari Stream API
  4. Aliran Paralel
  5. Kasus praktis Stream API
  6. Praktik Terbaik untuk Stream API
  7. Pertanyaan dan Solusi yang Sering Diajukan
  8. Meringkaskan

Apa itu Streaming API

Stream API adalah abstraksi yang diperkenalkan di Java 8 untuk memproses kumpulan data yang memungkinkan data diproses secara deklaratif (mirip dengan pernyataan SQL). Stream API menyediakan banyak operasi canggih yang dapat digunakan untuk memfilter, mengurutkan, memetakan, mengurangi, dan operasi lain pada koleksi, sehingga sangat menyederhanakan kode.

Fitur

  • pemrograman deklaratif: Gunakan Stream API untuk menulis kode secara deklaratif, sehingga mengurangi kode boilerplate.
  • panggilan berantai: Pengoperasian Stream API dapat dipanggil secara berantai, sehingga meningkatkan keterbacaan kode.
  • evaluasi malas: Operasi perantara dievaluasi dengan lambat dan hanya dievaluasi ketika operasi terminal dilakukan.
  • proses paralel: Mendukung pemrosesan paralel dan dapat memanfaatkan CPU multi-core secara maksimal.

Operasi dasar Stream API

Buat Aliran

Stream API menyediakan berbagai cara untuk membuat Stream. Yang umum mencakup hal berikut:

  1. Buat dari koleksi
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
  • 1
  • 2
  1. Buat dari array
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
  • 1
  • 2
  1. menggunakanStream.of
Stream<String> stream = Stream.of("a", "b", "c");
  • 1
  1. menggunakanStream.generate
Stream<Double> stream = Stream.generate(Math::random).limit(10);
  • 1
  1. menggunakanStream.iterate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
  • 1

operasi perantara

Operasi perantara digunakan untuk mengonversi Stream dan dievaluasi secara lambat. Operasi perantara yang umum mencakup hal berikut:

  1. filter: digunakan untuk memfilter elemen.
Stream<String> stream = list.stream().filter(s -> s.startsWith("a"));
  • 1
  1. map: Digunakan untuk memetakan setiap elemen ke hasil yang sesuai.
Stream<String> stream = list.stream().map(String::toUpperCase);
  • 1
  1. flatMap: Digunakan untuk mengonversi setiap elemen menjadi Aliran dan kemudian menggabungkannya menjadi satu Aliran.
Stream<String> stream = list.stream().flatMap(s -> Stream.of(s.split("")));
  • 1
  1. distinct: Digunakan untuk menghapus duplikat.
Stream<String> stream = list.stream().distinct();
  • 1
  1. sorted: digunakan untuk menyortir.
Stream<String> stream = list.stream().sorted();
  • 1
  1. peek: Digunakan untuk melihat setiap elemen selama pemrosesan.
Stream<String> stream = list.stream().peek(System.out::println);
  • 1

Operasi terminal

Operasi terminal digunakan untuk memulai penghitungan Aliran dan menghasilkan hasil Operasi terminal umum meliputi:

  1. forEach: Melakukan operasi pada setiap elemen.
list.stream().forEach(System.out::println);
  • 1
  1. collect: Mengonversi Stream ke bentuk lain.
List<String> result = list.stream().collect(Collectors.toList());
  • 1
  1. reduce: Mengurangi elemen dalam Aliran menjadi sebuah nilai.
Optional<String> result = list.stream().reduce((s1, s2) -> s1 + s2);
  • 1
  1. toArray:Konversi Aliran ke array.
String[] array = list.stream().toArray(String[]::new);
  • 1
  1. count: Menghitung jumlah elemen.
long count = list.stream().count();
  • 1
  1. anyMatchallMatchnoneMatch: Digunakan untuk penilaian yang cocok.
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("a"));
boolean allMatch = list.stream().allMatch(s -> s.startsWith("a"));
boolean noneMatch = list.stream().noneMatch(s -> s.startsWith("a"));
  • 1
  • 2
  • 3
  1. findFirstfindAny: Digunakan untuk mencari elemen.
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.stream().findAny();
  • 1
  • 2

Operasi lanjutan dari Stream API

menyortir

menggunakansortedMetode ini mengurutkan Aliran dan dapat meneruskan pembanding.

List<String> list = Arrays.asList("b", "c", "a");
List<String> sortedList = list.stream().sorted().collect(Collectors.toList());
// 逆序排序
List<String> sortedListDesc = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
  • 1
  • 2
  • 3
  • 4

Saring

menggunakanfilterMetode untuk memfilter elemen di Stream.

List<String> list = Arrays.asList("a", "b", "c");
List<String> filteredList = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
  • 1
  • 2

pemetaan

menggunakanmapMetode memetakan elemen dalam Aliran.

List<String> list = Arrays.asList("a", "b", "c");
List<String> mappedList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Statuta

menggunakanreduceMetode untuk mengurangi elemen di Stream.

List<String> list = Arrays.asList("a", "b", "c");
String result = list.stream().reduce("", (s1, s2) -> s1 + s2);
  • 1
  • 2

mengumpulkan

menggunakancollectMetode mengonversi Aliran ke bentuk lain.

List<String> list = Arrays.asList("a", "b", "c");
List<String> collectedList = list.stream().collect(Collectors.toList());
Set<String> collectedSet = list.stream().collect(Collectors.toSet());
String joinedString = list.stream().collect(Collectors.joining(","));
  • 1
  • 2
  • 3
  • 4

Aliran Paralel

Parallel Stream dapat memanfaatkan sepenuhnya keunggulan CPU multi-core untuk meningkatkan efisiensi pemrosesan data.bisa menggunakanparallelStreamMetode menciptakan Aliran paralel.

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.parallelStream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Bisa juga digunakanparallelMetode mengubah Aliran normal menjadi Aliran paralel.

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.stream().parallel().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Perlu dicatat bahwa Aliran paralel tidak selalu lebih cepat daripada Aliran serial, dan perlu diuji sesuai dengan situasi spesifik.

Kasus praktis Stream API

Memproses pengumpulan data

Kasus 1: Filter dan transformasi koleksi

Diberikan kumpulan string, filter string yang panjangnya kurang dari 3 dan ubah string yang tersisa menjadi huruf besar.

List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
List<String> result = list.stream()
    .filter(s -> s.length() >= 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
System.out.println(result); // 输出:[ABC, ABCD]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Kasus 2: Hitung rata-ratanya

Diberikan sekumpulan bilangan bulat, hitung rata-rata semua bilangan bulat.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
OptionalDouble average = list.stream()
    .mapToInt(Integer::intValue)
    .average();
average.ifPresent(System.out::println); // 输出:3.0
  • 1
  • 2
  • 3
  • 4
  • 5

Operasi berkas

Kasus 3: Membaca konten file

Menggunakan API Aliran

Baca konten file dan output ke konsol.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
Kasus 4: Hitung jumlah kemunculan kata

Baca isi file dan hitung jumlah kemunculan setiap kata.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    Map<String, Long> wordCount = lines
        .flatMap(line -> Arrays.stream(line.split("\W+")))
        .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
    wordCount.forEach((word, count) -> System.out.println(word + ": " + count));
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Operasi basis data

Kasus 5: Memproses hasil query database

Katakanlah kita memiliki tabel databaseusers, berisi bidangidnameDanage . Kita bisa menggunakan Stream API untuk memproses hasil query.

List<User> users = queryDatabase();
List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());
System.out.println(names);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Praktik Terbaik untuk Stream API

  1. Hindari paralelisasi yang tidak perlu: Aliran Paralel tidak selalu lebih cepat dan harus dipilih berdasarkan kasus per kasus.
  2. Penggunaan yang tepat dari operasi perantara dan operasi terminal: Operasi perantara dievaluasi dengan lambat dan hanya dievaluasi ketika operasi terminal dilakukan.
  3. Perhatikan penggunaan kembali Stream: Setelah Aliran digunakan, Aliran tersebut tidak dapat digunakan lagi. Jika Anda perlu menggunakannya kembali, Anda dapat mempertimbangkan untuk mengubah Aliran menjadi koleksi sebelum menggunakannya.
  4. Gunakan kolektor yang tepatCollectorsKelas ini menyediakan berbagai macam kolektor, dan Anda dapat memilih kolektor yang sesuai dengan kebutuhan spesifik.
  5. Tangani pengecualian: Saat menggunakan Stream API, Anda perlu menangani kemungkinan pengecualian, terutama dalam operasi file dan operasi database.

Pertanyaan dan Solusi yang Sering Diajukan

Aliran ditutup

Setelah Aliran digunakan, Aliran tersebut tidak dapat digunakan lagi. Jika Anda perlu menggunakannya kembali, Anda dapat mempertimbangkan untuk mengubah Aliran menjadi koleksi sebelum menggunakannya.

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 会抛出IllegalStateException
  • 1
  • 2
  • 3
  • 4

Masalah kinerja

Aliran Paralel tidak selalu lebih cepat daripada Aliran serial dan perlu diuji berdasarkan kasus per kasus.bisa menggunakanForkJoinPoolUntuk mengoptimalkan kinerja Aliran paralel.

ForkJoinPool customThreadPool = new ForkJoinPool(4);
customThreadPool.submit(() ->
    list.parallelStream().forEach(System.out::println)
).get();
  • 1
  • 2
  • 3
  • 4

kebocoran memori

Saat menggunakan Stream API untuk memproses data dalam jumlah besar, Anda perlu memperhatikan masalah kebocoran memori.bisa menggunakanclosemetode untuk menutup Aliran, atau gunakantry-with-resourcespernyataan secara otomatis menutup Stream.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5

Meringkaskan

Artikel ini memperkenalkan penggunaan Java Stream API secara mendetail, termasuk operasi dasar, operasi lanjutan, pemrosesan paralel, kasus praktis, dan praktik terbaik. Dengan memanfaatkan Stream API secara rasional, pengembang dapat menyederhanakan kode, meningkatkan keterbacaan dan pemeliharaan kode, dan juga meningkatkan efisiensi pemrosesan data. Saya harap artikel ini bermanfaat bagi Anda dalam penggunaan Stream API dalam pengembangan Java.

Java Stream API adalah alat yang ampuh untuk memproses pengumpulan data melalui penggunaan berbagai operasi yang fleksibel, pemrosesan data yang efisien dan komputasi streaming dapat dicapai. Jika Anda belum pernah menggunakan Stream API, disarankan untuk mempelajari dan menguasai alat canggih ini sesegera mungkin dan menerapkannya pada proyek Anda untuk meningkatkan efisiensi pengembangan dan kualitas kode.