study/java

자바/JAVA 스트림(Stream)

올스왑 2021. 3. 13. 16:55

스트림을 람다식을 이용해 배열, 컬렉션을 다루는 기술(클래스)이다. 스트림을 사용하면 자료의 대상과 상관없이 동일한 연산을 수행할 있다.(자료의 추상화) 배열, 컬렉션의 원소들에 대해 동일한 연산을 수행할 수 있다. 또한 스트림 객체는 한번 생성되면 다시 재사용할 수 없기 때문에, 재사용을 위해선 새로운 스트림 객체를 생성해야 한다.

배열의 스트림 생성
java.util.Arrays의 stream() 메소드로 생성.
int[] arr = {1,2,3,4};
Arrays.stream(arr);

컬렉션의 스트림 생성
스트림을 변수에 담으려면 java.util.stream.Stream에 담는다.
List<String> strLIst = {"토르","로키","퀵실버","완다"};
strList.stream(); // 스트림을 바로 사용할 때.
Stream<String> myStream = strList.stream(); // Stream 객체에 담을 때.

 

스트림 연산은 중간 연산과 최종 연산으로 나뉜다.

중간 연산은 조건에 맞게 자료를 필터링하는(filter()), 자료를 변환하는(map()) 등의 연산이 있다.

최종 연산은 스트림의 자료를 소모하면서 연산을 수행한다. 최종 연산 후에 스트림은 더 이상 다른 연산을 적용할 수 없다.

중간 연산은 여러개일 수 있지만, 최종 연산은 마지막에 한번만 적용된다.

최종 연산이 수행되어야 모든 연산이 적용되는 지연 연산이다.

 

중간 연산 - filter() ,map(), ...

filter() - 조건에 맞는 요소 추출.

map() - 요소 변환

 

최종 연산 - forEach(), count(), sum(), ...

forEach() : 요소를 하나씩 꺼내 옴.

count() : 요소의 개수

sum() : 요소의 합

strList.stream().filter(s -> s.length() < 3).forEach(s -> System.out.println(s));
//출력
토르
로키
완다
//
strList.stream() : 스트림 생성
filter(s -> s.length() < 3) : 중간연산. 요소의 길이가 3보다 작은 것만 추출
forEach(s -> System.out.println(s)) : 최종연산. 중간 연산의 결과에 대해 하나씩 꺼내 출력
Person이라는 객체가 age라는 필드를 갖고 가정하면,
personList.stream().map(p -> p.getAge()).forEach(p -> System.out.println(p));
//출력
29
31
19
//
personList.stream() : 스트림 생성
map(p -> p.getAge()) : Person 객체에서 Person.getAge()의 결과값이 담긴 스트림으로 변경.
forEach(p -> System.out.println(p)) : map에서 바뀐 값을 출력

 

스트림엔 reduce() 연산이란 것이 있다. 정의된 연산이 아닌 개발자가 직접 지정한 연산을 적용한다. reduce()는 최종 연산이고 스트림의 요소를 소모하며 연산을 수행한다.

 

정수 배열의 모든 값을 더하는 연산을 reduce 연산을 수행하려면

int[] arr = {1,2,3,4,5,6,7,8,9,10};
int sum = Arrays.stream(arr).reduce(0, (a, b) -> a + b);
System.out.println(sum); // 55 출력

위 코드 reduce의 첫번째 매개변수에는 초기값을 전달하고 두번째에는 람다식을 전달한다. reduce의 연산은 초기값이 a에 담기고 b에 첫번째 요소가 담기고, 연산의 결과가 a에 담긴다. 그리고 그 다음 요소가 b에 담기고 또 다시 연산을 수행한다.

 

reduce() 안에 연산이 너무 길다면, java.util.function.BinaryOperator 인터페이스, apply()를 구현하는 객체로 대신할 수 있다.

BinaryOperator<T> 인터페이스(T는 스트림에 담긴 객체 타입, reduce에서 사용할 객체 타입)를 구현하고 apply()메소드를 구현하는 객체를 생성해 전달하면 apply() 메소드가 실행된다.

class adder implements BinaryOperator<String>{
	@Override
	public String apply(String t, String u) {
		return t + u;
	}
}
public class temp {
	public static void main(String[] args) {
		List<String> strList = new ArrayList<>();
		strList.add("ab");
		strList.add("cd");
		strList.add("ef");
		String result = strList.stream().reduce(new adder()).get();
		System.out.println(result); // abcdef 출력
	}
}