자바 스트림

최창효·2022년 12월 6일
0
post-thumbnail

스트림이란

스트림은 데이터 집합을 읽는 객체입니다.

자바 8부터 제공되는 기능입니다.

스트림의 특징

  • 스트림은 데이터 소스(원본 데이터)를 변경하지 않습니다.
  • 스트림은 일회용입니다.
    • 스트림은 데이터를 모두 읽고나면 사라집니다.
  • 스트림은 반복문이 코드에 노출되지 않습니다.
  • 지연연산을 수행합니다. 중간 체이닝마다 모두 연산을 실행하는 게 아니라 실행 결과가 필요해지는 시점에 연산을 수행합니다.

for문 vs 스트림

  • for문이 스트림보다 속도가 빠릅니다.
  • 스트림은 코드가 간결해 가독성이 좋습니다.

주요 스트림 연산

of()

스트림 객체를 생성합니다.

// 기본
Stream<String> test1 = Stream.of("a","b","c");
test1.forEach(val -> System.out.println(val)); // a b c 각각 출력

// 배열
String[] arr = {"a","b","c"};
Stream<String> test2 = Stream.of(arr); 
test2.forEach(val -> System.out.println(val)); // a b c 각각 출력

// 컬렉션에서는 .stream()메서드를 사용하면 스트림으로 변환됩니다. 
List<String> test3 = new ArrayList<String>();
test3.add("a");
test3.add("b");
test3.add("c");
test3.stream().forEach(val -> System.out.println(val)); // a b c 각각 출력

forEach()

데이터를 모두 읽어들인 뒤 스트림 데이터 각각에 대해 작업을 수행합니다. map과 달리 스트림 요소를 소모하기 때문에 아무런 반환값이 없습니다.

Stream<Integer> test4 = Stream.of(1,2,3);
test4.forEach(val -> ++val);
// 위에서 스트림 요소를 소모했기 때문에 에러가 발생합니다.
// java.lang.IllegalStateException: stream has already been operated upon or closed
test4.forEach(val -> System.out.println(val)); // 1 2 3 각각 출력

Stream.of(1,2,3).forEach(val -> ++val).xxx(); //뒤에 .으로 추가연산을 실행할 수 없습니다. 

map()

forEach와 동일하게 스트림 데이터 각각에 대해 작업을 수행합니다. forEach와 달리 스트림 연산을 계속할 수 있습니다.

Stream<Integer> test5 = Stream.of(1,2,3);
// val++를 실행하면 1 2 3이 출력됩니다.
test5.map(val -> ++val).forEach(val2 -> System.out.println(val2)); // 2 3 4 각각 출력

// 뒤에 추가적인 연산이 가능한거지 Stream 값이 지속적으로 유지되는 건 아닙니다. (스트림은 일회용)
Stream<Integer> test6 = Stream.of(1,2,3);
test6.map(val -> ++val);
test6.forEach(val2 -> System.out.println(val2)); // 에러가 발생합니다.

filter()

각 데이터에 대해 연산을 수행하고 그 결과가 true인 값만 출력합니다.

Stream.of(1,2,3).filter(val -> val<=2).forEach(val2 -> System.out.println(val2)); // 1 2 각각 출력

sorted()

값을 정렬합니다. Comparator를 인자로 받습니다.

List<Node> test7 = new ArrayList<Node>();
test7.add(new Node(5,"a"));
test7.add(new Node(2,"b"));
test7.add(new Node(8,"c"));

// Node의 Comparable을 기준으로 값이 정렬됩니다.
test7.stream().sorted().forEach(val -> System.out.println(val)); // 2 5 8 노드가 각각 출력됩니다.

// 인자로 받은 Comparator를 기준으로 값이 정렬됩니다.
test7.stream().sorted(new Comparator<Node>(){
		@Override
		public int compare(Node o1, Node o2){ 
				return o2.idx - o1.idx; // 내림차순
		}	
}).forEach(val -> System.out.println(val)); // 8 5 2 노드가 각각 출력됩니다.

public static class Node implements Comparable<Node>{
		int idx;
		String name;
		public Node(int idx, String name) {
				super();
				this.idx = idx;
				this.name = name;
		}
		@Override
		public int compareTo(Node o) {
				return this.idx - o.idx; // 오름차순
		}
		
	}

collect()

스트림을 컬렉션으로 변환해 줍니다.

List<Integer> test8 = Stream.of(1,2,3).collect(Collectors.toList());
System.out.println(test8.toString()); // [1,2,3] 출력

reduce()

BinaryOperator를 인자로 받아 그 결과값을 Optional 형태로 반환합니다.
stream이 아니라 연산 수행 결과값을 반환하는 게 특징입니다.

int result = Stream.of(1,2,3,4,5,6,7,8,9,10).reduce((x,y) -> x+y) // 모든 값을 더합니다.
		.orElseThrow(() -> new Exception()); // Optional을 처리하기 위한 문법입니다.
System.out.println(result); // 55가 반환됩니다.

// 메소드 레퍼런스를 활용할 수 있습니다.
int result = Stream.of(1,5,9,2,3,10,8,7).reduce(Integer::max) // 최댓값을 찾습니다.
		.orElseThrow(() -> new Exception()); // 또는 .get()
System.out.println(result) // 10이 반환됩니다.

// 직접 구현할 수도 있습니다
int result = Stream.of(1,5,9,2,3,10,8,7).reduce((x, y) -> (x>y ? x : y)) // 최댓값을 찾습니다.
		.orElseThrow(() -> new Exception());
System.out.println(result); // 10이 반환됩니다.

References

profile
기록하고 정리하는 걸 좋아하는 개발자.

0개의 댓글