Strem은 Java 8 의 기능 중 하나로, 리눅스에서 사용되는 파이프 라인 처럼 한번에 한 개 씩 만들어지는 연속적인 데이터 항목들의 모임이다.
Stream은 배열, List 등의 요소들의 처리를 담당한다.
기존 배열처리를 간단하게 해주며 Functional style로 처리할 수 있도록 해준다.
List<String> list = Arrays.asList("홍길동", "신용권", "김남준");
list.stream().forEach(name -> System.out.println(name));
스트림 연산은 원본을 변경하지 않는다. 즉, Immutable 하다.
내부 반복자(Internal iteration)를 사용한다.
외부 반복자 : 개발자가 코드로 직접컬렉션의 요소를 반복해서 가져오는 것을 말한다.
내부 반복자 : 컬렉션 내부에서 요소들을 반복시키고 개발자는 각 요소당 처리해야하는 코드만 제공하는 것을 말한다.
많은 Stream의 기능들은 Stream 자기 자신을 리턴한다. 이 방식은 처리 작업이 체인처럼 연결되어 큰 파이프라인처럼 작동 하도록할 수 있다.
List<Integer> list = Arrays.asList(3, 5, 2, 4);
list.stream().sorted().filter(e -> e < 5).forEach(System.out::println);
🤚 병렬 처리?
병렬처리란, 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는 것을 말한다.
distinct()
어떤 스트림에서 중복되는 아이템들을 모두 제거해주고 새로운 스트림을 반환한다.
List<Integer> list = Arrays.asList(1, 2, 3, 3);
list.stream().distinct().forEach(System.out::println);
결과
1
2
3
filter()
특정 조건으로 스트림의 요소를 필터링 하고 필터링된 새로운 스트림을 반환한다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().filter(e -> e > 3).forEach(System.out::println);
결과
4
5
map()
각각의 요소들을 변경하여 새로운 스트림을 생성한다.
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().map(e -> e * 2).forEach(System.out::println);
결과
2
4
6
flatMap()
배열로 감싸져 있는 모든 원소를 단일 원소 스트림으로 반환한다.
Integer arr[][] = { { 1, 2 }, { 2, 4 }, { 1, 5 } };
Arrays.stream(arr).flatMap(array -> Arrays.stream(array)).forEach(System.out::println);
결과
1
2
2
4
1
5
// 똑같은 결과를 map으로 하려면? 차이점을 확인해보자!
Arrays.stream(arr)
.map(array -> Arrays.stream(array))
.forEach(numbers -> numbers.forEach(System.out::println));
MaptoInt()
map과 똑같은 기능을 하지만 IntStream을 반환한다.
mapToLong, mapToDouble 또한 특화 스트림으로 변환해 반환한다.
class Student {
String name;
int num;
Student (String name, int num) {
this.name = name;
this.num = num;
}
}
List<Student> students = new ArrayList<>();
students.add(new Student("James", 1));
students.add(new Student("Alice", 2));
students.add(new Student("Tom", 3));
int sum = students.stream().mapToInt(e -> e.num).sum();
System.out.println(sum);
결과
6
// 오류발생
int[] arr = students.stream().map(e -> e.num).toArray();
// 정상 작동
int[] arr = students.stream().mapToInt(e -> e.num).toArray();
asDoubleStream()
IntStream의 요소 또는 LongStream의 요소를 double 요소로 타입 변환해서 DoubleStream을 생성한다.
asLongStream()
IntStream의 int 요소를 long 요소로 타입 변환해서 LongStream을 생성한다.
boxed()
int 요소, long요소, double요소를 Integer, Long, Double 요소로 박싱해서 Stream을 생성한다.
allMatch()
Stream의 모든 요소가 조건을 충족하는지 확인한다.
List<Integer> list = new ArrayList<>();
IntStream.range(1, 5).forEach(e -> list.add(e));
boolean result1 = list.stream().allMatch(e -> e >= 1);
boolean result2 = list.stream().allMatch(e -> e >= 2);
System.out.println(result1 + " " + result2);
결과
true false
allMatch()
Stream의 요소가 1개라도 충족하는지 확인한다.
noneMatch()
Stream의 모든 요소가 충족하지 않는지 확인한다.
count()
스트림의 요소의 수를 반환한다.
findFirst(), findAny()
스트림에서 첫 번째 요소를 참조하는 Optional 객체를 반환한다. findAny()는 병렬스트림에서 주로 사용한다.
🤚 Optional?
Optional 클래스는 Integer나 Double 클래스처럼 'T'타입의 객체를 포장해 주는 래퍼 클래스이다.따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있다.
아직 Optional에 대해서는 공부를 충분히 하지 않았기 때문에 추후에 공부를 더 하고 설명에 대한 글을 써봐야 겠다..
List<Integer> list = new ArrayList<>();
IntStream.range(1, 5).forEach(e -> list.add(e));
Optional<Integer> a = list.stream().filter(e -> e > 1).findFirst();
System.out.println(a.get());
결과
2
min(), max()
스트림의 요소 중에서 가장 큰 값과 가장 작은 값을 가지는 요소를 참조하는 Optional 객체를 반환한다.
sum()
스트림의 모든 요소의 합을 반환한다.
average()
스트림의 모든 요소의 평균을 Optional 객체로 반환한다.
List<Integer> list = new ArrayList<>();
IntStream.range(1, 5).forEach(e -> list.add(e));
OptionalDouble avg = list.stream().filter(e -> e > 1).mapToInt(e -> e).average();
System.out.println(avg.getAsDouble());
결과
3.0
reduce()
reduce는 첫 번째와 두 번째 요소를 가지고 연산을 수행한 뒤, 그 결과와 세 번째 요소를 가지고 또다시 연산을 수행한다.
이런 식으로 해당 스트림의 모든 요소를 소모하여 연산을 수행하고 그 결과를 반환한다.
초기값이 있는 경우 반환 타입은 Optional<T>가 아닌 T 타입이다. 전달 받은 초기값과 동일한 타입을 반환해야 하기 때문이다.
Stream<String> stream1 = Stream.of("넷", "둘", "셋", "하나");
Stream<String> stream2 = Stream.of("넷", "둘", "셋", "하나");
Optional<String> result1 = stream1.reduce((s1, s2) -> s1 + "++" + s2);
result1.ifPresent(System.out::println);
// 초기값이 있는 reduce
String result2 = stream2.reduce("시작", (s1, s2) -> s1 + "++" + s2);
System.out.println(result2);
결과
넷++둘++셋++하나
시작++넷++둘++셋++하나
collect()
인수로 전달되는 Collectors 객체에 구현된 방법대로 스트림의 요소를 수집한다.
기본적으로 toCollection(), toList(), toSet(), toMap() 등이 있다.
그 외에 통계와 연산, 그룹화와 분할 등을 위한 메서드가 존재하는데, 이 부분에 대해서는 추후에 더 공부하여 포스팅 하는걸로..
// List로 변환
Stream<String> stream = Stream.of("a", "b", "c", "d");
List<String> list = stream.collect(Collectors.toList());
// Set으로 변환
stream = Stream.of("a", "b", "c", "d");
Set<String> set = stream.collect(Collectors.toSet());
참고한 곳
https://palpit.tistory.com/647
https://ict-nroo.tistory.com/43
http://tcpschool.com/java/java_stream_concept