컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.
import java.util.List;
public class ImperativeProgramming {
public static void main(String[] args){
// List에 있는 숫자들 중에서 4보다 큰 짝수의 합계 구하기
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum = 0;
for(int number : numbers){
if(number > 4 && (number % 2 == 0)){
sum += number;
}
}
System.out.println("명령형 프로그래밍을 사용한 합계 : " + sum);
}
}
//출력값
명령형 프로그래밍을 사용한 합계 : 14
위 코드는 "어떻게"에 초점을 둔 명령형 프로그래밍이다.
import java.util.List;
public class DeclarativePrograming {
public static void main(String[] args){
// List에 있는 숫자들 중에서 4보다 큰 짝수의 합계 구하기
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum =
numbers.stream()
.filter(number -> number > 4 && (number % 2 == 0))
.mapToInt(number -> number)
.sum();
System.out.println("선언형 프로그래밍을 사용한 합계 : " + sum);
}
}
//출력값
선언형 프로그래밍을 사용한 합계 : 14
직관적이고 간결한 코드
배열을 데이터 소스로 하는 스트림 생성은 Arrays
클래스의 stream()
메서드 또는 Stream
클래스의 of()
메서드를 사용할 수 있다.
Arrays.stream()
public class StreamCreator {
public static void main(String[] args) {
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Arrays.stream(arr);
// 출력
stream.forEach(System.out::println);
}
}
// 출력값
김코딩
이자바
박해커
Stream.of()
import java.util.stream.Stream;
public class StreamCreator {
public static void main(String[] args) {
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Stream.of(arr);
// 출력
stream.forEach(System.out::println);
}
}
// 출력값
김코딩
이자바
박해커
Arrays
클래스 종류
IntStream
의 유용한 기능들
import java.util.Arrays;
import java.util.stream.IntStream;
public class StreamCreator {
public static void main(String[] args) {
// int형 배열로 스트림 생성
int[] intArr = {1,2,3,4,5,6,7};
IntStream intStream = Arrays.stream(intArr);
// 숫자와 관련된 경우 intStream을 사용하는 것을 권장
System.out.println("sum=" + intStream.sum());
// System.out.println("average=" + intStream.average());
}
}
//출력값
sum=28
Collection
으로부터 확장된 하위클래스 List
와 Set
을 구현한 컬렉션 클래스들은 모두 stream()
메서드를 사용하여 스트림을 생성할 수 있다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreator {
public static void main(String[] args) {
// 요소들을 리스트
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Stream<Integer> stream = list.stream();
stream.forEach(System.out::print);
}
}
//출력값
1234567
import java.util.Random;
import java.util.stream.IntStream;
public class StreamCreator {
public static void main(String[] args) {
// 난수 생성
IntStream ints = new Random().ints();
ints.forEach(System.out::println);
}
}
이렇게 하면 출력값이 무한대로 생성된다.
스트림의 크기가 정해지지 않은 것을 무한 스트림(infinite stream)이라 부른다.
import java.util.Random;
import java.util.stream.IntStream;
public class StreamCreator {
public static void main(String[] args) {
// 스트림 생성의 범위를 5개로 제한
IntStream ints = new Random().ints(5);
// IntStream ints = new Random().ints().limit(5);
ints.forEach(System.out::println);
}
}
ints(5) 처럼 ()에 5를 넣으면 특정 범위를 반환하는 것이 가능하다.
limit(5)
도 가능
range(1, 10)
: start 부터 end까지의 범위를 지정할 수 있다. 1~9까지만 나옴
rangeClosed(1, 10)
: start 부터 end까지의 범위를 지정할 수 있다. 1~10까지 나옴
가장 빈번하게 사용하는 것 위주로 설명
distinct()
: Stream의 중복 데이터가 존재하는 경우, 중복을 제거하기 위해 사용한다.
filter()
: Stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어낸다.
() 안에 조건이 참이 되는 요소만 필터링한다. 여기서 조건은 람다식을 사용하여 정의할 수 있다.
//예제
import java.util.Arrays;
import java.util.List;
public class FilteringExample {
public static void main(String[] args) throws Exception {
List<String> names = Arrays.asList("김코딩", "이자바", "박해커", "김코딩", "박해커");
names.stream()
.distinct() //중복 제거
.forEach(element -> System.out.println(element));
System.out.println();
names.stream()
.filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링
.forEach(element -> System.out.println(element));
System.out.println();
names.stream()
.distinct() //중복제거
.filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링
.forEach(element -> System.out.println(element));
}
}
// 출력값
김코딩
이자바
박해커
김코딩
김코딩
김코딩
원하는 필드만 추출하거나 특정 형태로 변환할 때 사용
map()
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("kimcoding", "javalee", "hackerna", "luckyguy");
names.stream()
.map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
.forEach(element->System.out.println(element));
}
}
// 출력값
KIMCODING
JAVALEE
HACKERNA
LUCKYGUY
(((((((2)))))))
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args)
{
List<Integer> list = Arrays.asList(1, 3, 6, 9);
// 각 요소에 3을 곱한 값을 반환
list.stream().map(number -> number * 3).forEach(System.out::println);
}
}
// 출력값
3
9
18
27
flatMap()
// 주어진 이중 배열
String[][] namesArray = new String[][]{{"박해커", "이자바"}, {"김코딩", "나박사"}};
// 기대하는 출력값
박해커
이자바
김코딩
나박사
// flatMap()
Arrays.stream(namesArray).flatMap(Arrays::stream).forEach(System.out::println);
// 출력값
박해커
이자바
김코딩
나박사
flatMap()
을 사용하여 중첩 구조를 제거하고 단일 컬렉션(Stream<String>
)으로 만들어주는 역할을 한다. 이를 요소들을 평평하게 한다는 의미에서 플래트닝(flattening)이라고 한다.
정렬할 때 사용한다.
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 동물들의 이름을 모아둔 리스트
List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
// 인자값 없는 sort() 호출
animals.stream().sorted().forEach(System.out::println);
}
}
// 출력값
Cow
Duck
Horse
Lion
Monkey
Tiger
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
// 인자값에 Comparator 인터페이스에 규정된 메서드 사용
animals.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
}
}
// 출력값
Tiger
Monkey
Lion
Horse
Duck
Cow
skip()
- 일부 요소를 건너 뛴다. import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 1~10 범위의 정수로 구성된 스트림 생성
IntStream intStream = IntStream.rangeClosed(1, 10);
// 앞의 5개의 숫자를 건너뛰고 숫자 6부터 출력
intStream.skip(5).forEach(System.out::println);
}
}
// 출력값
6
7
8
9
10
limit()
- 스트림의 일부를 자른다.import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 1~10 범위의 정수로 구성된 스트림 생성
IntStream intStream = IntStream.rangeClosed(1, 10);
// 앞에서부터 5개의 숫자만 출력
intStream.limit(5).forEach(System.out::println);
}
}
// 출력값
1
2
3
4
5
peek()
- forEach()
와 마찬가지로, 요소들을 순회하며 특정 작업을 수행한다.import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 요소들을 사용하여 IntStream 생성
IntStream intStream3 = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 7, 7, 7, 8);
// 짝수만 필터링하여 합계 구하기
int sum = intStream3.filter(element -> element % 2 == 0)
.peek(System.out::println)
.sum();
System.out.println("합계 = " + sum);
}
}
// 출력값
2
2
4
8
합계 = 16
import java.util.Arrays;
public class TerminalOperationExample {
public static void main(String[] args) {
// int형 배열 생성
int[] intArray = {1,2,3,4,5};
// 카운팅
long count = Arrays.stream(intArray).count();
System.out.println("intArr의 전체 요소 개수 " + count);
// 합계
long sum = Arrays.stream(intArray).sum();
System.out.println("intArr의 전체 요소 합 " + sum);
// 평균
double average = Arrays.stream(intArray).average().getAsDouble();
System.out.println("전체 요소의 평균값 " + average);
// 최대값
int max = Arrays.stream(intArray).max().getAsInt();
System.out.println("최대값 " + max);
// 최소값
int min = Arrays.stream(intArray).min().getAsInt();
System.out.println("최소값 " + min);
// 배열의 첫 번째 요소
int first = Arrays.stream(intArray).findFirst().getAsInt();
System.out.println("배열의 첫번째 요소 " + first);
}
}
// 출력값
intArr의 전체 요소 개수 5
intArr의 전체 요소 합 15
전체 요소의 평균값 3.0
최대값 5
최소값 1
배열의 첫번째 요소 1
뒤에 getAsInt()
나 getAsDouble()
을 붙이는 이유는 Optional로 결과가 나오기 때문에 붙여줘야한다.
//예시
import java.util.Arrays;
import java.util.OptionalDouble;
public class TerminalOperationExample {
public static void main(String[] args) {
// int형 배열 생성
int[] intArr = {1,2,3,4,5};
// 평균값을 구해 Optional 객체로 반환
OptionalDouble average = Arrays.stream(intArr).average();
System.out.println(average);
// 기본형으로 변환
double result = average.getAsDouble();
System.out.println("전체 요소의 평균값 " + result);
}
}
//출력값
OptionalDouble[3.0]
전체 요소의 평균값 3.0
allMatch()
- 모든 요소들이 조건을 만족하는 지 여부를 판단한다.noneMatch()
- 모든 요소들이 조건을 만족하지 않는 지 여부를 판단한다.anyMatch()
- 하나라도 조건을 만족하는 요소가 있는 지 여부를 판단한다.//예제
import java.util.Arrays;
public class TerminalOperationExample {
public static void main(String[] args) throws Exception {
// int형 배열 생성
int[] intArray = {2,4,6};
// allMatch()
boolean result = Arrays.stream(intArray).allMatch(element-> element % 2 == 0);
System.out.println("요소 모두 2의 배수인가요? " + result);
// anyMatch()
result = Arrays.stream(intArray).anyMatch(element-> element % 3 == 0);
System.out.println("요소 중 하나라도 3의 배수가 있나요? " + result);
// noneMatch()
result = Arrays.stream(intArray).noneMatch(element -> element % 3 == 0);
System.out.println("요소 중 3의 배수가 하나도 없나요? " + result);
}
}
// 출력값
요소 모두 2의 배수인가요? true
요소 중 하나라도 3의 배수가 있나요? true
요소 중 3의 배수가 하나도 없나요? false
스트림의 요소를 줄여나가면서 연산을 수행하고 최종적인 결과를 반환한다.
reduce()
import java.util.Arrays;
public class TerminalOperationExample {
public static void main(String[] args) throws Exception {
int[] intArray = {1,2,3,4,5};
// sum()
long sum = Arrays.stream(intArray).sum();
System.out.println("intArray 전체 요소 합: " + sum);
// 초기값이 없는 reduce()
int sum1 = Arrays.stream(intArray)
.map(element -> element * 2)
.reduce((a , b) -> a + b)
.getAsInt();
System.out.println("초기값이 없는 reduce(): " + sum1);
// 초기값이 있는 reduce()
int sum2= Arrays.stream(intArray)
.map(element -> element * 2)
.reduce(5, (a ,b) -> a + b);
System.out.println("초기값이 있는 reduce(): " + sum2);
}
}
// 출력값
intArray 전체 요소 합: 15
초기값이 없는 reduce(): 30
초기값이 있는 reduce(): 35
요소들을 수집하는 최종 처리 메서드이다.
collect는 요소 그룹핑 및 분할 등 다른 기능들을 제공한다.
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TerminalOperationExample {
public static void main(String[] args) {
// Student 객체로 구성된 배열 리스트 생성
List<Student> totalList = Arrays.asList(
new Student("김코딩", 100, Student.Gender.Male),
new Student("박해커", 80, Student.Gender.Male),
new Student("이자바", 90, Student.Gender.Female),
new Student("나미녀", 60, Student.Gender.Female)
);
// 스트림 연산 결과를 Map으로 반환
Map<String, Integer> maleMap = totalList.stream()
.filter(s -> s.getGender() == Student.Gender.Male)
.collect(Collectors.toMap(
student -> student.getName(), // Key
student -> student.getScore() // Value
));
// 출력
System.out.println(maleMap);
}
}
class Student {
public enum Gender {Male, Female};
private String name;
private int score;
private Gender gender;
public Student(String name, int score, Gender gender) {
this.name = name;
this.score = score;
this.gender = gender;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
public Gender getGender() {
return gender;
}
}
// 출력값
{김코딩=100, 박해커=80}