스트림(Stream)은 데이터의 추상화된 연속된 흐름을 의미한다.
데이터 컬렉션을 더 간결하고 직관적인 방식으로 처리할 수 있게 해주는 도구이다.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;
public class StreamCreateExample {
public static void main(String[] args) {
// Set
System.out.println("=".repeat(50));
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Stream<Integer> stream1 = set.stream();
stream1.forEach(System.out::println);
// Array
System.out.println("=".repeat(50));
String[] array = {"Java", "Python", "JavaScript"};
Stream<String> stream2 = Arrays.stream(array);
stream2.forEach(System.out::println);
// 부분 배열
System.out.println("=".repeat(50));
Stream<String> stream3 = Arrays.stream(array, 0, 2);
stream3.forEach(System.out::println);
// 정적 메서드 사용
System.out.println("======== 정적 메서드 사용 =======");
// Stream.iterate() - 무한 스트림 생성
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2)
.limit(10);
// Stream.generate() - 무한 스트림 생성
Stream<Double> randomStream = Stream.generate(Math::random)
.limit(5);
iterateStream.forEach(System.out::println);
randomStream.forEach(System.out::println);
// 파일에서 라인 스트림 생성
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.util.*;
import java.util.stream.Collectors;
public class StreamPipelineExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println(result); // [ALICE, CHARLIE, DAVID]
}
}

import java.util.*;
public class FilterExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("Apple", "Banana", "Cherry", "Apple", "Cherry", "Date");
List<String> resultWords = new ArrayList<>();
for (String word : words) {
if (word.length() >= 5) {
if (resultWords.contains(word)) continue;
resultWords.add(word);
}
}
System.out.println(resultWords);
System.out.println("=".repeat(50));
}
}
import java.util.*;
import java.util.stream.Collectors;
public class DistinctExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("Apple", "Banana", "Cherry", "Apple", "Cherry", "Date");
List<String> resultWords2 = words.stream()
.filter(word -> word.length() >= 5)
.distinct()
.toList();
System.out.println(resultWords2);
System.out.println("=".repeat(50));
List<String> resultWords3 = words.stream()
.filter(word -> word.length() >= 5)
.distinct()
.collect(Collectors.toList());
System.out.println(resultWords3);
resultWords3.add("test");
System.out.println(resultWords3);
}
}
import java.util.*;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperNames);
System.out.println("=".repeat(50));
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths);
}
}
import java.util.*;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("Apple", "Banana"),
Arrays.asList("Cherry", "Date"),
Arrays.asList("Elderberry", "Fig")
);
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println(flatList);
List<String> words = Arrays.asList("Hello", "World");
List<String> letters = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.distinct()
.collect(Collectors.toList());
System.out.println(letters);
}
}
import java.util.*;
import java.util.stream.Collectors;
public class SortedExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("Banana", "Apple", "Cherry", "Date");
List<String> sortedAsc = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("오름차순: " + sortedAsc);
List<String> sortedDesc = fruits.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println("내림차순: " + sortedDesc);
List<String> sortedByLength = fruits.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
System.out.println("길이순: " + sortedByLength);
}
}
import java.util.*;
public class ForEachExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.forEach(n -> System.out.println("Number: " + n));
}
}
import java.util.*;
import java.util.stream.Collectors;
public class PeekExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubled = numbers.stream()
.peek(n -> System.out.println("원본: " + n))
.map(n -> n * 2)
.peek(n -> System.out.println("두 배: " + n))
.collect(Collectors.toList());
System.out.println(doubled);
}
}
import java.util.*;
public class MatchExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0);
System.out.println("모두 양수? " + allPositive);
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
System.out.println("짝수 존재? " + hasEven);
boolean noNegative = numbers.stream()
.noneMatch(n -> n < 0);
System.out.println("음수 없음? " + noNegative);
}
}
import java.util.*;
public class FindExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Optional<String> first = names.stream()
.filter(name -> name.startsWith("C"))
.findFirst();
first.ifPresent(System.out::println);
Optional<String> any = names.parallelStream()
.filter(name -> name.length() > 3)
.findAny();
any.ifPresent(System.out::println);
}
}
import java.util.*;
public class BasicStatsExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();
System.out.println("개수: " + count);
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
min.ifPresent(n -> System.out.println("최소값: " + n));
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
max.ifPresent(n -> System.out.println("최대값: " + n));
}
}
import java.util.*;
import java.util.IntSummaryStatistics;
public class PrimitiveStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("합계: " + sum);
double average = numbers.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
System.out.println("평균: " + average);
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println("통계: " + stats);
}
}
import java.util.*;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> {
System.out.println(a + "::" + b);
return a + b;
});
System.out.println("합계: " + sum);
int sum2 = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("합계2: " + sum2);
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
max.ifPresent(n -> System.out.println("최대값: " + n));
List<String> words = Arrays.asList("Hello", " ", "World", "!");
String sent = words.stream()
.reduce("", String::concat);
System.out.println(sent);
}
}
import java.util.*;
import java.util.stream.Collectors;
public class CollectorsExample {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 85, "CS"),
new Student("Bob", 92, "Math"),
new Student("Charlie", 78, "CS"),
new Student("David", 88, "Physics"),
new Student("Eve", 95, "Math")
);
List<String> names = students.stream()
.map(Student::getName)
.collect(Collectors.toList());
System.out.println(names);
Set<String> departments = students.stream()
.map(Student::getDepartment)
.collect(Collectors.toSet());
System.out.println(departments);
Map<String, Integer> nameToScore = students.stream()
.collect(Collectors.toMap(Student::getName, Student::getScore));
System.out.println(nameToScore);
String allNames = students.stream()
.map(Student::getName)
.collect(Collectors.joining(", "));
System.out.println(allNames);
}
static class Student {
private final String name;
private final int score;
private final String department;
public Student(String name, int score, String department) {
this.name = name;
this.score = score;
this.department = department;
}
public String getName() { return name; }
public int getScore() { return score; }
public String getDepartment() { return department; }
}
}
import java.util.*;
import java.util.stream.Collectors;
public class GroupingByExample {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "IT", 5000),
new Employee("Bob", "HR", 4000),
new Employee("Charlie", "IT", 5500),
new Employee("David", "HR", 4500),
new Employee("Eve", "Sales", 4800)
);
Map<String, List<Employee>> byDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println("부서별 직원:");
byDepartment.forEach((dept, emps) -> {
System.out.println(dept + ": " +
emps.stream().map(Employee::getName).collect(Collectors.toList()));
});
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
System.out.println("\n부서별 평균 급여:");
avgSalaryByDept.forEach((dept, avg) ->
System.out.println(dept + ": " + avg)
);
Map<String, Map<Boolean, List<Employee>>> complexGrouping =
employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.partitioningBy(e -> e.getSalary() > 4500)
));
}
static class Employee {
private final String name;
private final String department;
private final double salary;
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
}
}
import java.util.*;
import java.util.stream.Collectors;
public class PartitioningByExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Map<Boolean, List<Integer>> evenOddPartition = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("짝수: " + evenOddPartition.get(true));
System.out.println("홀수: " + evenOddPartition.get(false));
}
}
병렬 스트림은 스트림 연산을 여러 스레드에서 병렬로 나눠 처리해서 속도를 높이는 기능이다.
import java.util.*;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 1_000_000; i++) {
numbers.add(i);
}
long startTime = System.currentTimeMillis();
long sum1 = numbers.stream()
.mapToLong(Integer::longValue)
.sum();
long sequentialTime = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
long sum2 = numbers.parallelStream()
.mapToLong(Integer::longValue)
.sum();
long parallelTime = System.currentTimeMillis() - startTime;
System.out.println("순차 처리 시간: " + sequentialTime + "ms");
System.out.println("병렬 처리 시간: " + parallelTime + "ms");
System.out.println("결과 동일: " + (sum1 == sum2));
}
}
하루만에 스트림 관련 메서드와 유틸리티들을 빠르게 살펴보니 머리가 좀 아팠다.
그래도 생각보다 어렵진 않고, 메서드들이 어디서 언제 어떻게 사용되는지만 알면 쉽게 사용할 수 있을 것 같았다.
또한 어제 배운 람다식과 연결되어 많이 사용되다 보니까 람다 쪽도 다시 복습해야겠다는 생각이 들었다.
내일은 네트워크 프로그래밍에 들어가는데 전공이 네트워크 쪽이라서 꽤 재밌을 것 같다.