자바 스트림(Streams)

min·2021년 12월 8일
0

스트림이 익숙해지지 않아서 정리해본다. for문을 계속 돌리면서 약간 자괴감이 느껴지기 때문에 확실히 정리 해두는 것이 필요 할 것 같다.

참조
https://futurecreator.github.io/2018/08/26/java-8-streams/
https://mangkyu.tistory.com/112?category=872426

스트림 (Streams)

  • 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
  • 코드가 간결하고 내부 반복자를 사용하므로 병렬 처리가 쉬움
    * 쓰레드를 이용해 많은 요소들을 빠르게 처리 할 수 있음
  • 배열 또는 컬렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 필터링하고 가공된 결과를 얻을 수 있음
  • 배열과 컬렉션을 함수형으로 처리 할 수 있음
  • Iterator와 비슷하지만 코드가 좀 더 간결하고 병렬 처리가 쉬움
  • 추상화를 통해 데이터의 종류에 상관 없이 같은 방식으로 데이터를 처리 할 수 있음

특징

  • 원본의 데이터를 변경하지 않음
    * 원본의 데이터에서 정렬이나 필터링 작업을 진행하는 것이 아니라 별도의 Stream요소들에서 처리 된다.
  • 일회용으로 재사용이 불가능함
  • 내부 반복으로 작업을 처리함

순서

  1. 생성하기: 스트림 인스턴스를 생성한다.
  2. 가공하기: 필터링(filtering) 및 맵핑(mapping)등 원하는 결과를 만들기 위한 가공을 진행한다.
  3. 결과 만들기: 최종적으로 결과를 만들어낸다.
list.stream() // 생성하기
    .filter(s -> s.startWith("c")) // 가공하기
    .map(String::toUpperCase) // 가공하기
    .sorted() // 가공하기
    .count() // 결과 만들기

1. 생성하기

  • 스트림은 배열 또는 컬렉션 인스턴스를 이용해서 생성 할 수 있음
  • Stream 객체를 생성함
  • 재사용이 불가능하므로 닫히면 다시 생성 필요

배열 스트림

Arrays.stream 메소드를 이용해서 접근

Car[] cars = new Car[] {"min", "choi", "test"};
Stream<Stream> stream = Arrays.stream(arr); // stream 생성
stream.forEach(car -> System.out.println(car));

컬렉션 스트림

해당 컬렉션 인터페이스에 구현되어 있는 stream 메소드를 이용해서 만들 수 있음

List<Car> cars = Arrays.asList("min", "choi", "test");
Steam<String> stream = list.stream();

Stream.builder()

builder를 사용하여 스트림에 직접 원하는 값을 넣을 수 있음

Stream<String> stream =
	Stream.<String>builder()
	.add("min").add("choi").add("java")
	.build();

ParallelStream()

병렬 스트림을 쉽게 생성하기 위해서 Stream 대신 parallelStream메소드를 사용함
쓰레드를 이용하여 병렬 처리 됨

cars
	.parallelStream() // 생성하기
	.map()
	.anyMatch()

원시 스트림

int, long, double과 같은 원시 자료형을 사용하기 위한 스트림(IntStream, LongStream, DoubleStream)들을 사용 할 수 있음

IntStream stream = IntStream.range(4, 10); // for문 대신

2. 가공하기

  • 원본의 데이터를 별도의 데이터로 가공하기 위한 중간 연산
  • 연산 결과를 Stream으로 반환하므로 연속해서 중간 연산(Intermediate operation)이 가능하다

Filtering

스트림 내의 요소들을 하나씩 걸러내는 작업
조건에 맞는 데이터만을 가져와서 더 작은 컬렉션을 만들어내는 연산

Stream<T> filter(Predicate<? super T> predicate); // boolean 값을 return하는 Predicate 함수형 인터페이스를 매개 변수로 받음

Mapping

기존의 Stream 요소들을 변환하여 새로운 Stream을 형성하는 연산
저장된 값을 특정한 형태로 변환하는데 주로 사용함 > 스트림에 저장되어 있는 값이 특정 로직을 거친 후 특정한 형태로 변환되는 연산

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

// Example, 대문자로 변환한 값들이 stream에 담기게 됨
Stream<String> stream = 
	names
    	.stream() // 생성
        .map(String::toUpperCase); // 가공하기

Stream<Integer> stream =
	products
    	.stream()
        .map(Product::getAmount()); // Product.getAmount(); 값을 stream으로 맵핑(?)

Sorting

Stream<T> sotred();
Stream<T> sorted(Comparator<? super T> comparator);

Stream<String> stream =
	list
    	.stream() // 생성
       	.sorted() // 정렬

Stream<String> stream = 
	list
    	.stream() // 생성
        .sorted(Comparator.reverseOrder()) // 정렬
        .collect(Collectors.toList());

Distinct

Stream<String> stream = 
	list
    	.stream() // 생성
        .distinct() // 중복 제거

Peek

Stream에 영향을 주지 않고 특정 연산을 수행한다.
Consumer 함수형 인터페이스를 인자로 받는다.

IntStream.of(1,3,5,7,9)
	.peek(System.out::println)
	.sum()

원시 Stream <-> Stream

Stream 객체를 원시 Stream으로 바꾸거나 원시 Stream을 Stream 객체로 바꿔야 함
mapToInt(), mapToLong(), mapToDouble(), mapToObject

// IntStream (원시Stream) > Stream<Integer>
IntStream
	.range(1,4)
	.mapToObj(i -> i+1);
        
// Stream<Double> > IntStream > Stream<String>
Stream.of(1.0, 2.0, 3.0)
	.mapToInt(Double::intValue)
	.mapToObj(i -> "test" + i);

3. 결과 만들기

  • 가공한 데이터로부터 원하는 결과를 만들기 위한 최종 연산
  • Stream의 요소를 소모하면서 연산이 수행되므로 1번만 처리 가능함

Max, Min, Sum, Average, Count

max, min, average의 경우 Stream이 비어있는 경우에는 값을 줄 수 없기 때문에 Optional로 값이 반환된다.

Collect

데이터 수집
Stream 요소들을 List, Set, Map과 같이 다른 종류의 결과로 수집하고 싶은 경우 사용

List<Car> cars = Arrays.asList(
	new Car(1000, "min"),
  	new Car(2000, "choi"),
	new Car(3999, "test"));
  • Collectors.toList()
    cars의 이름 요소들을 가져와서 List로 반환 하는 작업
// 원래의 코드라면
List list = new ArrayList();
for (Car car : cars) {
	car.add(list);
}

// stream을 사용해서 멋지게 리스트로 받을 수 있다.
List<String> names = cars
			.stream()
			.map(Car::getName)
			.collect(Collectors.toList());
  • Collectors.joining()
    Stream에서 작업한 결과를 이어붙이는 경우에 사용한다.
    파라미터는 총 3개로 .joining(구분자, 맨 앞에 붙는 문자, 맨 뒤에 붙는 문자)이다.
// result: <min, choi, test>
String names = cars
		.stream()
                .map(Car::getName)
                .collect(Collectors.joining(",", "<", ">");
  • Collectors.averagingInt(), Collectors.summingInt(), Collectors.summarizingInt()
Integer priceSum = cars
			.stream()
			.collect(Collectors.summingInt(Car::getPrice));
  • Collectors.groupingBy
    특정 그룹으로 결과를 묶을 수 있음
    결과는 Map으로 반환 받음 (group 키(key)-value)

Match

Stream의 요소들이 특정한 조건을 충족하는지 검사하고 싶은 경우 match 함수를 이용한다
Predicate 함수형 인터페이스를 받아 해당 조건을 만족하는 요소가 있는지 결과를 리턴 함.

  • anyMatch : 하나라도 조건을 만족하는 요소가 있는지 확인 함
  • allMatch : 모두 조건을 만족하는지 확인 함
  • noneMatch : 모두 조건을 만족하지 않는지 확인 함
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);

boolean anyMatch = list
			.stream()
			.anyMatch(name -> name.contains("min")); 
profile
발등에 불이 따뜻하다..

0개의 댓글