티스토리 뷰

목차

1. Strean

2. 스트림 생성

3. 중간 연산(중개 오퍼래이션)

4. 최종 연산(종료 오퍼레이션)

 

 

 

1. Strean

스트림을 통해 배열, 컬렉션 인스턴스의 데이터들을 가공할 수 있다.

스트림 생성, 중간 연산, 최종 연산 순으로 진행되고 최종 연산이 없으면 아무 결과를 얻지 못한다.

 

2. 스트림 생성

1) 배열

Arrays 클래스의 static 메소드 사용

public static IntStream stream(int[] array, int startInclusive, int endExclusive)
int[] ints = {1, 2, 3, 4, 5};
// 인덱스 1 ~ 3 데이터를 대상으로 스트림 생성
Arrays.stream(ints, 1, 4)
        .forEach(System.out::println); // 2, 3, 4

2) 컬렉션 인스턴스

Collection<E> 인터페이스의 디폴트 메소드 사용

default Stream<E> stream()
List<String> list = Arrays.asList("Toy", "Robot", "Box");
list.stream()
        .forEach(System.out::println);

 

3) of() 메소드

인자로 데이터를 전달하면서 스트림을 생성한다.

public static<T> Stream<T> of(T t)
public static<T> Stream<T> of(T... values)
public static<T> Stream<T> ofNullable(T t)
// ex 1
Stream.of(11, 22, 33, 44)
    .forEach(n -> System.out.print(n + "\t"));
System.out.println();

// ex 2
Stream.of("So Simple")
    .forEach(s -> System.out.print(s + "\t"));
System.out.println();

// ex 3
List<String> sl = Arrays.asList("Toy", "Robot", "Box");
Stream.of(sl)
    .forEach(w -> System.out.print(w + "\t"));
System.out.println();

ex3 은 컬렉션 인스턴스의 원소를 통해 스트림을 생성한 것이 아닌

인스턴스의 참조변수인 sl 을 원소로 갖는 스트림을 생성한 것이다.

따라서 다수의 컬렉션 인스턴스의 참조값을 원소로 갖는 스트림도 생성할 수 있다.

 

4) IntStream, DoubleStream, LongStream

public static IntStream of(int... values)
public static DoubleStream of(double... values)
public static LongStream of(long... values)

public static IntStream range(int startInclusive, int endExclusive)
public static IntStream rangeClosed(int startInclusive, int endInclusive)
public static LongStream range(long startInclusive, final long endExclusive)
public static LongStream rangeClosed(long startInclusive, final long endInclusive)

 

5) 스트림 연결

Stream, IntStream, DoubleStream, LongStream 의 concat 메소드를 활용해 스트림을 연결할 수 있다.

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
Stream.concat(s1, s2);

 

 

3. 중간 연산(중개 오퍼래이션) : 스트림을 리턴한다.

1) map

(1) map (1:1 매핑)

원소 한 개의 매핑 리턴 값으로 한 개의 데이터가 반환된다.

List<String> list = Arrays.asList("Toy", "Robot", "Box");
list.stream()
        .map(s -> s.length())
        .forEach(System.out::println);

위의 코드에서 데이터를 매핑할 때 map 메소드는 다음과 같이 Function 타입의 인자를 받는다.

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public interface Function<T, R> {
    R apply(T t);
}

Function 인터페이스는 리턴 타입을 R 타입으로 제네릭을 사용하는데 제네릭으로 기본 자료형 타입을 사용할 수 없기 때문에 R을 통해 전달되는 과정에서 int는 Integer로 auto boxing과 다시 auto unboxing 이 진행된다.

이런 경우에는 리턴 타입이 int로 선언되어 있는 mapToInt 메소드를 사용해 불필요한 boxing, unboxing을 줄일 수 있다.

IntStream mapToInt(ToIntFunction<? super T> mapper);
public interface ToIntFunction<T> {
    int applyAsInt(T value);
}

mapToInt, mapToLong, mapToDouble

 

 

(2) flatMap (1:N 매핑)

원소 한 개의 매핑 리턴 값으로 N개의 데이터가 반환된다.

Stream<String> ss1 = Stream.of("MY_AGE", "YOUR_LIFE");

Stream<String> ss2 = ss1.flatMap(s -> Arrays.stream(s.split("_")));
ss2.forEach(s -> System.out.print(s + "\t")); // MY	AGE	YOUR	LIFE

flatMapToInt, flatMapToLong, flatMapToDouble

 

2) sorted : 정렬

Stream<T> sorted(Comparator<? super T> comparator);
Stream.of("Box", "Apple", "Rabbit")
    .sorted((s1, s2) -> s1.length() - s2.length())
    .forEach(s -> System.out.print(s + '\t'));

 

3) looping

반복 연산 forEach 는 최종 연산이지만 peek 은 중간 연산이기 때문에 최종 연산이 없으면 연산을 진행하지 않는다.

IntStream.of(1, 3, 5)
    .peek(d -> System.out.print(d + "\t")); // 출력하지 않음

IntStream.of(5, 3, 1)
    .peek(d -> System.out.print(d + "\t")) // 5	3	1
    .sum();

 

4. 최종 연산(종료 오퍼레이션) : 스트림을 리턴하지 않는다.

1) sum, count, average, min, max

average 는 OptionalDouble 타입을

min, max 는 OptionalInt 타입을 리턴한다.

int sum = IntStream.of(1, 3, 5, 7, 9)
                  .sum();
                  
long cnt = IntStream.of(1, 3, 5, 7, 9)
                  .count();
                  
IntStream.of(1, 3, 5, 7, 9)
        .average()
        .ifPresent(av -> System.out.println("avg = " + av));
        
IntStream.of(1, 3, 5, 7, 9)
        .min()
        .ifPresent(mn -> System.out.println("min = " + mn));
        
IntStream.of(1, 3, 5, 7, 9)
        .max()
        .ifPresent(mx -> System.out.println("max = " + mx));

 

2) forEach

default void forEach(Consumer<? super T> action)

 

3) __match()

스트림의 데이터가 조건을 어느정도 만족하는지 확인.

boolean b = IntStream.of(1, 2, 3, 4, 5)
                  .allMatch(n -> n%2 == 0);

b = IntStream.of(1, 2, 3, 4, 5)
            .anyMatch(n -> n%2 == 0);

b = IntStream.of(1, 2, 3, 4, 5)
            .noneMatch(n -> n%2 == 0);

 

4) collect

collet() 메소드로 스트림에 있는 데이터를 저장할 수 있다.

// interface Stream<T>
<R> R collect(Supplier<R> supplier,
        BiConsumer<R, ? super T> accumulator,
        BiConsumer<R, R> combiner);
        
<R, A> R collect(Collector<? super T, A, R> collector);

// interface Collector<T, A, R
public interface Collector<T, A, R> {    
    Collector<T, ?, List<T>> toList() {
        return new Collectors.CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                (left, right) -> { left.addAll(right); return left; },
                CH_ID);
    }
}
List<String> ls = ss.filter(s -> s.length() < 5)
                  .collect(
                      () -> new ArrayList<>(),
                      (c, s) -> c.add(s),
                      (lst1, lst2) -> lst1.addAll(lst2));

ss.filter(s -> s.length() < 5)
        .collect(Collectors.toList());

collect() 의 첫 번째 인자의 리턴값이 두 번째 인자의 c 가 되고 스트림의 데이터가 s 가 된다.

동일한 작업을 Collectors.toList() 메소드를 사용할 수 있다.

 

collect() 메소드의 세 번째 인자는 병렬 스트림을 사용할 때 최종적으로 어떻게 취합할지를 결정한다.

List<String> ls = ss.parallel()
                  .filter(s -> s.length() < 5)                          
                  .collect(
                      () -> new ArrayList<>(),
                      (c, s) -> c.add(s),
                      (lst1, lst2) -> lst1.addAll(lst2));

ArrayList 를 만들고 저장하는 작업을 병렬로 처리한 다음 리스트의 addAll() 메소드를 통해 데이터를 취합한다.

 

 

 

 

 

출처

https://cafe.naver.com/cstudyjava 윤성우의 열혈 java

728x90