학습 개요
- 스트림은 컬렉션이나 배열로부터 만들어지는 원소의 시퀀스를 표현하며, 간결하고 효율적인 다양한 처리 방법을 제공하는 인터페이스임
forEach()
메소드를 사용하고 매개 변수로 람다 식을 제공하면 컬렉션이나 배열의 내부에서 반복을 처리함- 스트림이 제공하는 다양한 기능을 활용하여 컬렉션이나 배열의 원소를 처리하는 방법을 학습함
학습 목표
- 내부 반복과 외부 반복의 차이점을 설명할 수 있음
forEach()
메소드를 활용할 수 있음- 스트림을 이용하여 컬렉션/배열의 원소에 대한 병렬 처리 방법을 설명할 수 있음
- 스트림 파이프 라인을 이해하고 중간 처리과 최종 처리의 차이점을 설명할 수 있음
강의록
forEach()
메소드
외부 반복
- 컬렉션이나 배열의 원소를 다룰 때, 원소의 반복 처리를 프로그램에서 명시적으로 제어하는 방식
- 원소를 프로그램에서 선언된 변수로 복사한 후, 작업을 처리함
for
, 향상된for
,while
,do
-while
,Iterator
등을 이용한 반복 작업이 외부 반복에 해당됨
1 2 3 4 5 6 7 8 9 10 11
List<String> names = Arrays.asList("Kim", "Lee", "Park"); for (String name : names) { System.out.println(name); } Iterator<String> iterator = names.listIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }
내부 반복과 forEach()
메소드
- 원소의 반복 처리를 컬렉션 또는 스트림과 같은 데이터 구조 내부에서 반복을 처리하는 방식
- 프로그램에서 스트림 API를 이용하여 반복 처리를 위임하고, 처리 코드만 람다식으로 제공함
- 컬렉션의
forEach()
메소드를 이용하고, 매개변수로 람다식을 지정forEach()
메소드는 함수형 인터페이스인Consumer
객체를 인자로 받음
1 2 3 4
List<String> names = Arrays.asList("Kim", "Lee", "Park"); // 내부 반복 names.forEach(item -> System.out.println("내부 반복: " + item));
- 코드가 간결해 지며, 가독성이 좋아지고, 병렬 처리가 가능하며, 성능 최적화에 유리
스트림
스트림 정의
- 컬렉션이나 배열과 같은 데이터 소스로부터 만들어지는 원소의 시퀀스를 표현하며, 간결하고 효율적 처리 방법을 제공하는 인터페이스
- 내부 반복과 함수형 프로그래밍 방식을 지원
- 멀티 코어 CPU를 활용한 병렬 처리 지원
- 관련 클래스와 인터페이스는
java.util.stream
패키지에 있음 - 다양한 집합체 연산을 지원함
- 중간 처리 연산과 최종 처리 연산
filter()
,map()
,sorted()
,count()
,collect()
,anyMatch()
등
스트림의 특성
- 데이터 원본으로부터 스트림을 생성하며, 원본 데이터를 변경하지 않음
- 원본 데이터의 변경이 필요하면 새로운 스트림을 생성해야 함
- 스트림은 일회용
- 스트림 연산을 파이프라인 형태로 연결할 수 있음
- 지연 평가(lazy evaluation)를 통해 연산을 최적화
java.util.stream
패키지에 포함된 스트림 인터페이스
숫자와 스트림
IntStream
,LongStream
,DoubleStream
인터페이스- 기본형 int, long, double형의 원소로 이루어진 데이터를 다루기 위한 스트림
- 주요 기능
- ex) IntStream의 경우
static IntStream range(int, int)
,static IntStream rangeClosed(int, int)
static IntStream of(int... values)
sum()
,average()
,min()
,max()
등filter()
,map()
,reduce()
등
1 2 3 4
System.out.println(IntStream.rangeClosed(1, 100).sum()); System.out.println(IntStream.rangeClosed(1, 100).average().getAsDouble()); System.out.println(IntStream.rangeClosed(1, 100).min().getAsInt()); System.out.println(IntStream.rangeClosed(1, 100).max().getAsInt());
- ex) IntStream의 경우
배열과 스트림
Arrays.stream()
를 사용하여 배열로부터 스트림을 생성할 수 있음Arrays
클래스에서 제공되는 메소드1
static DoubleStream stream(double[] array)
1
static IntStream stream(int[] array)
1
static <T> Stream<T> stream(T[])
- 원소가 객체인 배열로부터 스트림 생성
Stream
은 제네릭 인터페이스
1 2 3 4 5 6 7
String[] strArray = { "홍길동", "이순신", "김유신" }; Stream<String> strStream = Arrays.stream(strArray); strStream.forEach(item -> System.out.println(item)); int[] intArray = {1, 2, 3}; IntStream intStream = Arrays.stream(intArray); intStream.forEach(item -> System.out.println(item));
파일과 스트림
Files.lines(Path)
을 사용하여 텍스트 파일로부터 행 단위 문자열로 구성 된 스트림을 생성할 수 있음1 2 3 4
Path path = Paths.get("c:\\data\\data.txt"); Stream<String> fileStream = Files.lines(path); fileStream.forEach(line -> System.out.println(line)); fileStream.close();
컬렉션과 스트림
Collection
유형의 객체인 경우Collection
인터페이스에서stream()
과parallelStream()
메소드가 디폴트 메소드로 제공 됨parallelStream()
은 병렬 처리가 가능한 스트림을 리턴함
HashSet
,ArrayList
,LinkedList
객체 등으로부터 스트림을 생성할 때 사용
HashMap
객체인 경우- 먼저
entrySet()
을 사용하여 Set 유형의 객체를 얻은 후,stream()
또는parallelStream()
메소드를 사용하여 스트림을 생성함
- 먼저
HashSet
과 스트림 사용 예
HashSet
객체와 스트림1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Set<Integer> set = new HashSet<>(); for (int i = 0; i < 10; i++) { set.add(i); } // 일반 스트림 사용 System.out.println("일반 스트림 사용"); Stream<Integer> n_stream = set.stream(); n_stream.forEach(item -> System.out.println(item)); // 병렬 스트림 사용 System.out.println("병렬처리 스트림 사용"); Stream<Integer> p_stream = set.parallelStream(); p_stream.forEach(item -> System.out.println(item + "(" + Thread.currentThread().getName() + ")")); } } // 일반 스트림 사용 // 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 // 9 // 병렬처리 스트림 사용 // 6(ForkJoinPool.commonPool-worker-2) // 7(ForkJoinPool.commonPool-worker-2) // 4(ForkJoinPool.commonPool-worker-1) // 5(ForkJoinPool.commonPool-worker-1) // 8(main) // 9(main) // 2(ForkJoinPool.commonPool-worker-3) // 0(ForkJoinPool.commonPool-worker-4) // 1(ForkJoinPool.commonPool-worker-4) // 3(ForkJoinPool.commonPool-worker-3)
ArrayList
와 스트림 사용 예
객체를 원소로 갖는
ArrayList
와 스트림1 2 3 4 5 6 7 8 9 10 11 12 13
class People { String name; int age; People(String name, int age) { this.name = name; this.age = age; } public String toString() { return ("name : " + this.name + ", age : " + this.age); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { List<People> list = new ArrayList<People>(); list.add(new People("홍길동", 30)); list.add(new People("이순신", 40)); list.add(new People("김유신", 50)); list.add(new People("유관순", 20)); // 일반 스트림 사용 System.out.println("일반 스트림 사용"); Stream<People> n_stream = list.stream(); n_stream.forEach(item -> System.out.println(item)); // 병렬 스트림 사용 System.out.println("병렬처리 스트림 사용"); Stream<People> p_stream = list.parallelStream(); p_stream.forEach(item -> System.out.println(item + " : " + Thread.currentThread().getName())); } } // 일반 스트림 사용 // name : 홍길동, age : 30 // name : 이순신, age : 40 // name : 김유신, age : 50 // name : 유관순, age : 20 // 병렬처리 스트림 사용 // name : 김유신, age : 50 : main // name : 이순신, age : 40 : ForkJoinPool.commonPool-worker-1 // name : 홍길동, age : 30 : main // name : 유관순, age : 20 : ForkJoinPool.commonPool-worker-2
스트림 파이프라인
스트림 파이프라인과 중간 연산
- 컬렉션, 배열, 또는 파일로부터 생성 된 스트림에 어떤 처리를 수행하고 새로운 스트림이 만들어지며, 계속해서 이러한 처리 과정을 반복하는 것
- 메소드 체이닝을 사용하여 파이프라인을 구축할 수 있음
- 중간 연산(또는 중간 처리)
- 원본 스트림에서 데이터를 반환하거나 필터링 등으로 새로운 스트림을 생성하는 연산
- 중간 연산에서 생성 된 스트림은 다음 연산으로 연결 되어 파이프라인을 형성 함
filter()
,map()
,sorted()
등의 연산
스트림 파이프라인과 종료 연산
- 종료 연산(또는 최종 연산)
- 중간 연산을 거친 스트림에 대해 최종적인 결과를 만들거나 동작을 수행하는 부분
- 스트림 파이프라인에서 마지막에 한번만 수행 됨
forEach()
,collect()
,count()
,anyMatch
,reduce()
등
1 2 3 4 5 6 7 8
List<String> words = Arrays.asList("apple", "banana", "cherry", "Avocado"); long count = words.stream() // 원본 스트림 .map(String::toUpperCase) // 중간 연산 .filter(word -> word.startsWith("A")) // 중간연산 .count(); // 최종연산 System.out.println(count); // 2가 출력됨
스트림 파이프라인과 메소드 체이닝
메소드 체이닝의 활용 - 메소드의 연속적 호출
1 2 3 4 5 6
List<Book> books = new ArrayList<Book>(); // ... ... Stream<Book> stream = books.stream(); IntStream price = stream.mapToInt(book -> book.getPrice()); double avg = price.average().getAsDouble(); System.out.println(avg);
1 2 3 4 5 6
List<Book> books = new ArrayList<Book>(); // ... ... double avg = books.stream() .mapToInt(book -> book.getPrice()) .average().getAsDouble(); System.out.println(avg);
중간 연산
필터링
- 컬렉션 원소들 중에서 중복을 제거하거나 특정 조건을 만족하는 원소만 추출하여 새로운 스트림을 생성하는 작업
Stream<T>
,IntStream
,LongStream
,DoubleStream
에서 필터링 메소드는 중간 연산이므로 같은 스트림을 리턴 함- 중복 제거에는
distinct()
메소드를 사용- 원소가 객체인 경우
hashCode()
의 리턴 값이 같고,equals()
로 비교할 때 true인 경우, 중복으로 판단함
- 원소가 객체인 경우
- 조건을 통한 걸러 내기에는
filter()
메소드를 사용filter()
메소드의 매개 변수는 함수형 인터페이스이며, true 또는 false를 리턴하는 람다식을 매개 변수로 전달
필터링 예
distinct()
메소드로 중복을 제거하는 필터링 스트림 예1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
import java.util.*; class Book { String title; String author; int price; Book(String title, String author, int price) { this.title = title; this.author = author; this.price = price; } @Override public String toString() { return "title:" + title + ", author:" + author + ", price:" + price; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Book book = (Book) o; return price == book.price && Objects.equals(title, book.title) && Objects.equals(author, book.author); } @Override public int hashCode() { return Objects.hash(title, author, price); } } public class Main { public static void main(String[] args) { List<Book> books = new ArrayList<Book>(); books.add(new Book("JAVA 프로그래밍", "홍길동", 10000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("빅데이터 연구", "유관순", 12000)); books.add(new Book("커뮤니케이션 이론", "강감찬", 15000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("알고리즘", "권율", 17000)); books.stream() .distinct() .forEach(book -> System.out.println(book)); } } // title:JAVA 프로그래밍, author:홍길동, price:10000 // title:PHP 프로그래밍, author:이순신, price:20000 // title:빅데이터 연구, author:유관순, price:12000 // title:커뮤니케이션 이론, author:강감찬, price:15000 // title:알고리즘, author:권율, price:17000
filter()
메소드를 사용하는 필터링 스트림 예1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import java.util.*; public class Main { public static void main(String[] args) { String[] strArray = {"ABC", "BCD", "AFE", "CDE", "ABZ", "ACCZ"}; List <String> strList = Arrays.asList(strArray); strList.stream() .filter(item -> item.startsWith("A")) .filter(item -> item.endsWith("Z")) .filter(item -> item.length() > 3) .forEach(item -> System.out.println(item)); strList.stream() .filter(item -> item.startsWith("A") && item.endsWith("Z") && item.length() > 3) .forEach(item -> System.out.println(item)); } } // ACCZ // ACCZ
매핑
- 원소들을 다른 원소로 변환하여 새로운 스트림을 생성하는 작업
Stream<T>
에서map()
은 매개 변수로 주어지는 함수(람다식)를 적용하여Stream<T>
로,mapToInt()
은 함수를 적용하여IntStream
으로 변환 시킴flatMap()
과flatMapToInt()
등은 스트림의 각 요소에 매핑 함수(람다식)를 적용하여 스트림으로 변환한 후, 여러 스트림을 다시 하나의 스트림으로 합침IntStream
에서asDoubleStream()
과asLongStream()
은 기본형을 다른 기본형으로 변환 시키는 것으로DoubleStream
또는LongStream
객체를 리턴,boxed()
는 기본형 int 값을 포장형Integer
객체로 변환시키고Stream<Integer>
객체를 리턴
매핑 예
스트림 매핑 예
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { //String 배열을 IntStream으로 변환 String[] strNums = {"10", "20", "30"}; Arrays.stream(strNums) .mapToInt(item -> Integer.parseInt(item)) .forEach(item -> System.out.println(item)); //int 배열을 Stream<String>으로 변환 int[] numbers = {10, 20, 30}; Arrays.stream(numbers) .mapToObj(item -> String.valueOf(item)) .forEach(item -> System.out.println(item + "(" + item.length() +")")); } } // 10 // 20 // 30 // 10(2) // 20(2) // 30(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Arrays.asList("Hello world, this is java", "Welcome to java world") .stream() .flatMap(sentence -> Arrays.stream(sentence.split(" "))) .forEach(word -> System.out.println(word)); } } // Hello // world, // this // is // java // Welcome // to // java // world
정렬
- 스트림의 원소들을 오름차순 또는 내림차순으로 정렬하여 새로운 스트림을 생성하는 작업
Stream<T>
에서sorted()
메소드 사용- 정렬 된 새로운 스트림을 리턴
- T 유형의 객체(원소)는
Comparable
이어야 하며, 크기 비교에compareTo()
메소드가 사용 됨 - 기본형에 대응 되는 포장 클래스는 모두
Comparable
임
정렬 예
compareTo()
가 구현 된 Book 클래스와 정렬 된 스트림 생성 예1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
class Book implements Comparable<Book> { String title; String author; int price; Book(String title, String author, int price) { this.title = title; this.author = author; this.price = price; } public String toString() { return ("title:" + this.title + ", author:" + this.author + ", price:" + this.price); } @Override public int compareTo(Book book) { return Integer.compare(this.price, book.price); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
import java.util.*; public class Main { public static void main(String[] args) { List<Book> books = new ArrayList<Book>(); books.add(new Book("JAVA 프로그래밍", "홍길동", 10000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("마이크로프로세서 입문", "김유신", 14000)); books.add(new Book("데이터베이스 입문", "신사임당", 21000)); books.add(new Book("빅데이터 연구", "유관순", 12000)); books.add(new Book("커뮤니케이션 이론", "강감찬", 15000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("알고리즘", "권율", 17000)); books.stream().sorted().forEach(book-> System.out.println(book)); } } // title:JAVA 프로그래밍, author:홍길동, price:10000 // title:빅데이터 연구, author:유관순, price:12000 // title:마이크로프로세서 입문, author:김유신, price:14000 // title:커뮤니케이션 이론, author:강감찬, price:15000 // title:알고리즘, author:권율, price:17000 // title:PHP 프로그래밍, author:이순신, price:20000 // title:PHP 프로그래밍, author:이순신, price:20000 // title:데이터베이스 입문, author:신사임당, price:21000
루핑
- 스트림의 원소들을 하나씩 순회하면서 반복적으로 처리하고 새로운 스트림을 생성하는 작업
peek()
메소드는 각 요소를 순회하면서 주어진 동작(람다식)을 수행하는 중간 연산으로 디버깅이나 로깅에 자주 사용 됨- 새로운 스트림을 반환하며, 최종 연산과 함께 사용되어야 함
- 참고로
forEach()
메소드는 각 요소를 반복하면서 주어진 동작(람다식)을 수행하는 종료 연산 anyMatch()
메소드는 종료 연산으로 주어진 조건(람다식)을 만족하는지 원소를 순회하면서 조사함- 주어진 조건을 만족하는 요소를 찾으면 즉시 검색을 중단
- 스트림 파이프라인에서
peek()
와anyMatch()
를 조합하면 처리되는 원소를, 조건식에서 처음 true가 되는 원소까지로 제한하게 됨
- 참고로
- 주로 디버깅 용도로 사용됨
- 종료 연산이 있어야 작동함
루핑 예
peek()
메소드 사용 예1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
import java.util.*; import java.util.stream.*; class Book { String title; String author; int price; Book(String title, String author, int price) { this.title = title; this.author = author; this.price = price; } public String toString() { return ("title:" + this.title + ", author:" + this.author + ", price:" + this.price); } } public class Main { public static void main(String[] args) { List <Book> books = new ArrayList <Book> (); books.add(new Book("JAVA 프로그래밍", "홍길동", 10000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("마이크로프로세서 입문", "김유신", 14000)); books.add(new Book("데이터베이스 입문", "신사임당", 21000)); books.add(new Book("빅데이터 연구", "유관순", 12000)); books.add(new Book("커뮤니케이션 이론", "강감찬", 15000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("알고리즘", "권율", 17000)); books.stream() .peek(book -> System.out.println(book)) .anyMatch(m -> m.price > 20000); // 종료 연산 } } // anyMatch가 true를 반환하기 전까지 peek 실행 // title:JAVA 프로그래밍, author:홍길동, price:10000 // title:PHP 프로그래밍, author:이순신, price:20000 // title:마이크로프로세서 입문, author:김유신, price:14000 // title:데이터베이스 입문, author:신사임당, price:21000`
종료 연산
종료 연산이란
- 중간 처리를 거친 스트림에 대해 집계나 결과 출력 등의 최종 처리를 수행하는 최종 연산
- 집계(합계, 평균, 최대, 최소 등), 매칭, 수집 등의 작업을 수행함
- 스트림 파이프라인의 마지막 단계에서 사용되며, 스트림을 다시 사용할 수 없게 함
- 종료 연산이 수행되기 전까지는 스트림 파이프라인이 실제로 실행 되지 않음(lazy evaluation)
- 지연 평가를 통해 스트림 파이프 라인을 효율적 구성하며 필요한 결과를 얻음
집계
- 원소들의 개수, 합계, 평균, 최대 값, 최소 값 등을 구하는 최종 처리
count()
,sum()
,average()
,min()
,max()
,reduce()
count()
- 스트림의 원소 개수 반환
sum()
- 숫자 스트림의 합계 반환
average()
- 숫자 스트림의 평균 반환 (
OptionalDouble
반환)
- 숫자 스트림의 평균 반환 (
min()
- 스트림의 최소 값 반환 (
OptionalInt
,OptionalLong
,OptionalDouble
반환)
- 스트림의 최소 값 반환 (
max()
- 스트림의 최대 값 반환 (
OptionalInt
,OptionalLong
,OptionalDouble
반환)
- 스트림의 최대 값 반환 (
reduce()
- 사용자 정의 집계 연산 수행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1부터 10까지의 정수 중에서 짝수만 필터링하여 출력
long count = Arrays.stream(intArray).filter(n -> n % 2 == 0).count();
System.out.println("2의 배수의 개수 : " + count);
// 1부터 10까지의 정수 중에서 짝수만 필터링하여 합계 계산
long sum = Arrays.stream(intArray).filter(n -> n % 2 == 0).sum();
System.out.println("2의 배수의 합 : " + sum);
// 1부터 10까지의 정수 중에서 짝수만 필터링하여 최소 값 계산
OptionalInt min = Arrays.stream(intArray).min();
System.out.println("최소값 : " + min.getAsInt());
// 1부터 10까지의 정수 중에서 짝수만 필터링하여 평균 계산
OptionalDouble avg = Arrays.stream(intArray).average();
System.out.println("평균 : " + avg.getAsDouble());
// 2의 배수의 개수 : 5
// 2의 배수의 합 : 30
// 최소값 : 1
// 평균 : 5.5
매칭
- 스트림의 원소가 특정 조건을 만족하는지 검사하는 연산
anyMatch()
- 스트림의 최소 하나의 요소가 조건을 만족할 때 true 반환
allMatch()
- 스트림의 모든 원소가 조건을 만족할 때 true 반환
noneMatch()
- 스트림의 모든 원소가 조건을 만족하지 않으면 true 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
boolean result = false;
result = Arrays.stream(intArray).anyMatch(n -> n % 2 == 0);
System.out.println("적어도 하나는 2의 배수인가? " + result);
result = Arrays.stream(intArray).allMatch(n -> n % 2 == 0);
System.out.println("모두 2의 배수인가? " + result);
result = Arrays.stream(intArray).filter(n -> n % 2 != 0)
.noneMatch(n -> n % 2 == 0);
System.out.println("모두 2의 배수가 아닌가? " + result);
// 적어도 하나는 2의 배수인가? true
// 모두 2의 배수인가? false
// 모두 2의 배수가 아닌가? true
수집
- 스트림의 원소들을 필터링 또는 매핑한 후 최종적으로 새로운 컬렉션(
List
,Set
,Map
)을 생성하는 종료 연산 Collectors
클래스는 스트림의 요소들을 수집하는 데 사용 되는 다양한 static 유틸리티 메소드를 제공- 이것의 결과가
collect()
의 인자로 사용 됨
- 이것의 결과가
collect()
- Collector를 인자로 받아 다양한 수집 작업 수행
Collectors.toList()
- List로 수집
Collectors.toSet()
- Set으로 수집
Collectors.toMap()
- Map으로 수집
Collectors.groupingBy()
- 특정 기준으로 그룹화하여 Map으로 수집
Collectors.joining()
- 문자열 스트림의 원소를 연결
1
2
3
4
5
6
7
8
9
10
11
12
List<Member> male_members = members.stream().filter(member->
member.getGender().equals("남")).collect(Collectors.toList());
male_members.stream().forEach(member -> System.out.println(member));
Set<Member> female_members2 = members.stream().filter(member ->
member.getGender().equals("여")).collect(Collectors.toSet());
female_members2.stream().forEach(member -> System.out.println(member));
Map<String, Integer> ages =
female_members2.stream().collect(Collectors.toMap(member ->
member.getName(), member -> member.getAge()));
System.out.println(ages);
학습 정리
- 내부 반복은 컬렉션이나 배열의 내부에서 각 원소의 반복 처리를 수행하는 것으로, 처리용 코드만 람다 식으로 전달하며 반복 작업을 위한 코드를 작성하지 않음
forEach()
메소드는 람다 식과 함께 사용하여 컬렉션과 배열의 원소들을 외부로 꺼내오지 않고 내부에서 탐색하게 함- 필터링은 스트림의 원소 중에서 중복을 제거하거나 특정 조건을 만족하는 원소만 걸러 내고 새로운 스트림을 리턴하는 중간 연산임
- 매핑은 스트림의 원소를 다른 원소로 변환한 후 새로운 스트림으로 리턴하는 중간 연산임
- 매칭은 스트림의 원소 중 특정 조건을 만족하는지 확인하는 최종 연산임
- 수집은 스트림의 원소 중 필터링 또는 매핑 작업 후 결과 원소들을 취합하여 새로운
List
,Set
, 또는Map
객체를 생성하는 최종 연산임
연습 문제
주어진 배열을 스트림으로 만드려고 한다. 밑줄 부분에 들어갈 적당한 내용은?
1 2
int[] numbers = {1, 2, 3, 4, 5}; IntStream numberStream = ________;
a.
Arrays.stream(numbers)
주어진 문자열 배열에서 길이가 6 이상인 단어를 대문자로 변환한 후 정렬하여 출력하려고 한다. 밑줄 부분에 들어갈 메소드는 무엇인가?
1 2 3 4 5 6 7 8
List<String> words = Arrays.asList("banana", "orange", "grape", "strawberry", "kiwi"); List<String> result = words.stream() // .______(word -> word.length() >= 5) // 빈칸 .map(str -> str.toUpperCase()) .sorted() .collect(Collectors.toList()); System.out.println(result);
a.
filter
중간 연산과 종료 연산의 차이를 설명하시오.
a. 중간 연산은 스트림을 변환하거나 필터링 하는 등의 작업을 수행하며, 다음 단계 처리를 위해 새로운 스트림을 리턴하는데, 체인 형태로 연속해 여러 번 호출될 수 있다. 종료 연산은 스트림의 원소를 이용해 최종 결과를 만들어 리턴한다.