Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

ความแตกต่างระหว่าง Streams และ Collections ใน Java 8

Collections** = โครงสร้างข้อมูลในหน่วยความจำ (In-memory Data Structure) | **Streams** = Pipeline สำหรับ **ประมวลผลข้อมูล** (Data Processing Pipeline)

ตารางเปรียบเทียบ

ลำดับหัวข้อCollectionsStreams
1วัตถุประสงค์**เก็บและจัดการ** ข้อมูล (Store & Manipulate)**ประมวลผล** ข้อมูล (Process/Transform)
2Packagejava.util.*java.util.stream.*
3Storageเก็บข้อมูลจริง (Eager - คำนวณทุกอย่างตั้งแต่ต้น)**ไม่เก็บข้อมูล** - แค่ Pipeline (Lazy - คำนวณตอน Terminal)
4Modificationเพิ่ม/ลบ/แก้ไข ได้ (Mutable)**ไม่สามารถแก้ไข Stream เองได้** (Immutable pipeline)
5IterationExternal Iterator (for-each, Iterator)Internal Iterator (Stream จัดการ loop ให้)
6Reusabilityใช้ซ้ำได้หลายครั้ง**ใช้ครั้งเดียว** (Consumed after terminal)
7ParallelManual (ต้องเขียนเอง)Built-in .parallelStream()
8Infiniteไม่ได้ (Finite)รองรับ Infinite Streams (Stream.generate, iterate)

Collections - Data Storage

import java.util.*;

public class CollectionsExample {
    public static void main(String[] args) {
        List laptops = new ArrayList<>();
        laptops.add("HCL");
        laptops.add("Apple");
        laptops.add("Dell");
        
        // External iteration - เราควบคุม loop
        for (String laptop : laptops) {
            System.out.println(laptop);
        }
        
        // Sort - แก้ไข Collection ตรงๆ (Mutable)
        Collections.sort(laptops, (a, b) -> a.compareTo(b));
        System.out.println("Sorted: " + laptops); // [Apple, Dell, HCL]
        
        // เพิ่ม/ลบ ได้
        laptops.add("Lenovo");
        laptops.remove("Apple");
    }
}

Streams - Data Processing

import java.util.*;
import java.util.stream.*;

public class StreamsExample {
    public static void main(String[] args) {
        List laptops = Arrays.asList("HCL", "Apple", "Dell");
        
        // Stream Pipeline - Internal iteration
        // ไม่แก้ไข list เดิม!
        List sorted = laptops.stream()
            .sorted()                    // Intermediate
            .collect(Collectors.toList()); // Terminal
        
        System.out.println("Original: " + laptops);  // [HCL, Apple, Dell] - ไม่เปลี่ยน
        System.out.println("Sorted: " + sorted);     // [Apple, Dell, HCL]
        
        // Filter + Map + Collect
        List filtered = laptops.stream()
            .filter(s -> s.startsWith("A")) // Intermediate
            .map(String::toUpperCase)       // Intermediate
            .collect(Collectors.toList());  // Terminal
        // [APPLE]
        
        // Stream ใช้ครั้งเดียว!
        Stream stream = laptops.stream();
        stream.count(); // OK
        // stream.count(); // ❌ IllegalStateException: stream has already been operated upon
    }
}

Key Differences แบบลึก

1. Eager vs Lazy Evaluation

List numbers = Arrays.asList(1, 2, 3, 4, 5);

// Collection - Eager
List doubled = new ArrayList<>();
for (int n : numbers) {
    doubled.add(n * 2); // คำนวณทันทีทุกตัว
}

// Stream - Lazy
Stream doubledStream = numbers.stream()
    .map(n -> {
        System.out.println("Mapping: " + n); // Print ตอน Terminal เท่านั้น
        return n * 2;
    });
System.out.println("Stream created, not executed yet");
List result = doubledStream.collect(Collectors.toList()); // ตอนนี้ถึง run

2. Internal vs External Iteration

// External (Collection) - เราเขียน loop
for (String s : list) { process(s); }

// Internal (Stream) - Stream จัดการ loop
list.stream().forEach(this::process);
// Parallel: list.parallelStream().forEach(this::process);

3. Stream Operations Fusion

// Stream รวม operations (Loop Fusion) - วนลูปแค่ 1 ครั้ง
list.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .forEach(System.out::println);
// เทียบเท่า: for (s: list) if (len>3) print(upper(s))

// Collection - อาจต้อง loop หลายครั้ง
list.stream().filter(...).collect(...); // Loop 1
list.stream().map(...).collect(...);    // Loop 2 (แยกกัน)

Conversion ระหว่าง Collections และ Streams

จากไปMethod
CollectionStreamcollection.stream() / collection.parallelStream()
ArrayStreamArrays.stream(array) / Stream.of(array)
StreamListstream.collect(Collectors.toList())
StreamSetstream.collect(Collectors.toSet())
StreamMapstream.collect(Collectors.toMap(...))
StreamArraystream.toArray() / stream.toArray(String[]::new)
StreamCollectionstream.collect(Collectors.toCollection(ArrayList::new))

Infinite Streams (เฉพาะ Streams)

// Generate infinite stream
Stream.iterate(0, n -> n + 2)     // 0, 2, 4, 6, 8...
    .limit(10)                     // ต้องมี limit/short-circuit terminal!
    .forEach(System.out::println);

Stream.generate(Math::random)      // Random infinite
    .limit(5)
    .forEach(System.out::println);

// Collection ไม่สามารถเป็น Infinite ได้

สรุป: เมื่อไหร่ใช้อะไร?

สถานการณ์เลือกเหตุผล
เก็บข้อมูล, เพิ่ม/ลบ/แก้ไข บ่อยCollection (List, Set, Map)Mutable, Random access, Reusable
Transform, Filter, Aggregate ข้อมูลStreamDeclarative, Composable, Parallelizable
One-time processing pipelineStreamLazy, Fusion, Memory efficient
ต้อง iterate หลายครั้งCollectionStream ใช้ครั้งเดียว
Parallel processingParallel StreamBuilt-in parallelism
Infinite sequenceStreamgenerate/iterate + limit

Best Practice

  • 🔄 **Collection → Stream → Collection**: รับ Collection, Process ด้วย Stream, คืน Collection
  • ❌ อย่าเก็บ Stream ไว้เป็น field / ใช้ซ้ำ
  • ✅ ใช้ Primitive Streams (IntStream ฯลฯ) สำหรับตัวเลข
  • ✅ ใช้ Collectors สำหรับ terminal operations ซับซ้อน