Collections** = โครงสร้างข้อมูลในหน่วยความจำ (In-memory Data Structure) | **Streams** = Pipeline สำหรับ **ประมวลผลข้อมูล** (Data Processing Pipeline)
ตารางเปรียบเทียบ
| ลำดับ | หัวข้อ | Collections | Streams |
| 1 | วัตถุประสงค์ | **เก็บและจัดการ** ข้อมูล (Store & Manipulate) | **ประมวลผล** ข้อมูล (Process/Transform) |
| 2 | Package | java.util.* | java.util.stream.* |
| 3 | Storage | เก็บข้อมูลจริง (Eager - คำนวณทุกอย่างตั้งแต่ต้น) | **ไม่เก็บข้อมูล** - แค่ Pipeline (Lazy - คำนวณตอน Terminal) |
| 4 | Modification | เพิ่ม/ลบ/แก้ไข ได้ (Mutable) | **ไม่สามารถแก้ไข Stream เองได้** (Immutable pipeline) |
| 5 | Iteration | External Iterator (for-each, Iterator) | Internal Iterator (Stream จัดการ loop ให้) |
| 6 | Reusability | ใช้ซ้ำได้หลายครั้ง | **ใช้ครั้งเดียว** (Consumed after terminal) |
| 7 | Parallel | Manual (ต้องเขียนเอง) | Built-in .parallelStream() |
| 8 | Infinite | ไม่ได้ (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 |
| Collection | Stream | collection.stream() / collection.parallelStream() |
| Array | Stream | Arrays.stream(array) / Stream.of(array) |
| Stream | List | stream.collect(Collectors.toList()) |
| Stream | Set | stream.collect(Collectors.toSet()) |
| Stream | Map | stream.collect(Collectors.toMap(...)) |
| Stream | Array | stream.toArray() / stream.toArray(String[]::new) |
| Stream | Collection | stream.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 ข้อมูล | Stream | Declarative, Composable, Parallelizable |
| One-time processing pipeline | Stream | Lazy, Fusion, Memory efficient |
| ต้อง iterate หลายครั้ง | Collection | Stream ใช้ครั้งเดียว |
| Parallel processing | Parallel Stream | Built-in parallelism |
| Infinite sequence | Stream | generate/iterate + limit |
Best Practice
- 🔄 **Collection → Stream → Collection**: รับ Collection, Process ด้วย Stream, คืน Collection
- ❌ อย่าเก็บ Stream ไว้เป็น field / ใช้ซ้ำ
- ✅ ใช้ Primitive Streams (
IntStream ฯลฯ) สำหรับตัวเลข
- ✅ ใช้
Collectors สำหรับ terminal operations ซับซ้อน