[JAVA] 스트림(Stream)이란?

DANI·2023년 12월 13일
0

JAVA를 공부해보자

목록 보기
23/29
post-thumbnail

📕 스트림(Stream)이란?

데이터 소스가 무엇(컬렉션프레임워크, 배열...)이든간에 같은 방식으로 다룰 수 있게 데이터를 추상화 하고 데이터를 다루는데 자주 사용되는 메서드들을 정의해 놓은 것



📑 스트림(Stream)의 특징

  • 데이터 소스가 무엇이든 간에 같은 방식으로 다룰 수 있게 되면서 코드의 재사용성이 높아진다.
  • 배열이나, 컬렉션뿐만 아니라 파일에 저장된 데이터도 모두 같은 방식으로 다룰 수 있다.
  • 스트림은 데이터 소스로 부터 데이터를 읽기만할 뿐, 데이터 소스를 변경하지 않는다.
  • 스트림은 일회용이다. 즉, 다시 사용하려면 스트림을 다시 생성해야 한다.
  • 스트림은 작업을 내부 반복으로 처리한다.(내부 반복이라는 것은 반복문을 메서드 내부에 숨길 수 있다는 것을 의미)


📑 스트림의 연산

✅ 중간연산

연산 결과가 스트림인 연산, 스트림에 연속해서 중간 연산할 수 있다.

✅ 최종연산

연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능

✅ 지연된연산

  • 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다.
  • 중간 연산을 호출하는 것은 단순히 어떤 작업이 수행되어야하는 지를 지정해주는 것이다.
  • 최종 연산이 수행되어야 스트림의 요소들이 중간 연산을 거처 최종 연산에서 소모된다.

✅ 기본자료형을 다루는 스트림

요소의 타입이 T인 스트림은 기본적으로 Stream인데, 기본 자료형을 다루려면 오토박싱&언박싱이 발생하여 비효율성이 증가한다(예 - Integer <-> int) 비효율성을 줄이기 위해 데이터 소스의 요소를 기본형으로 다루는 스트림이 제공된다. (int[] -> IntStream, long[] -> LongStream, double[] -> DoubleStream)



📑 스트림의 중간연산

람다식 - iterate(), generate()


람다식을 매개변수로 받아서 람다식에 의해 계산되는 값들을 요소로 하는 무한 스트림을 생성

💾 API

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static<T> Stream<T> generate(Supplier<T> s)
🔴 예시
Stream<Integer> iterate = Stream.iterate(2, x -> x * 2); // 이전 값을 이용하여 다음 요소 계산, 무한스트림
Stream<Integer> generate = Stream.generate(() -> 1); // 이전요소 없이 다음 요소 계산

🔴 예시1 - (iterate)
int[] array = IntStream.iterate(10, x -> x + 2).limit(5).toArray();
System.out.println(Arrays.toString(array)); // [10, 12, 14, 16, 18]

🔴 예시2 - (generate)
int[] array1 = IntStream.generate(() -> 1).limit(10).toArray();
System.out.println(Arrays.toString(array1)); // [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

두 스트림의 연결 - concat()


String[] str = {"123", "456"};
String[] str1 = {"AB", "DET"};
Stream<String> str2 = Stream.of(str);
Stream<String> str3 = Stream.of(str1);
Stream<String> concat = Stream.concat(str2, str3);

스트림 자르기 - skip(), limit()


💾 API

Stream<T> skip(long n)   // n만큼 건너뛰기
Stream<T> limit(long maxSize) // maxSize 만큼 자르기
🔴 예시
IntStream intStream1 = IntStream.rangeClosed(1, 10);
intStream1.skip(4).limit(2).forEach(System.out::println); // skip -> 건너뛰기, limit -> 개수 제한

스트림 요소 걸러내기 - filter(), distinct()


💾 API

Stream<T> filter(Predicate<? super T> predicate)
Stream<T> distinct()
🔴 예시
IntStream intStream5 = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 6);
intStream5.distinct().forEach(System.out::println);
IntStream intStream6 = IntStream.range(1, 10);
intStream6.filter(x->x>5).forEach(System.out::println);

변환 - map()


스트림의 요소에서 저장된 값 중에서 원하는 필드만 뽑아내거나 특정 형태로 변환해야 하는 경우

💾 API

Stream<R> map(Function<? super T, ? extends R> mapper)
🔴 예시
public class StreamMain1 {
    public static void main(String[] args) {
List<Member> members = new ArrayList<>();
        members.add(new Member("홍길동", 20));
        members.add(new Member("유관순", 40));
        members.add(new Member("이순신", 60));
        List<Integer> collect = members.stream().map(x -> x.getAge()).collect(Collectors.toList());
        collect.stream().forEach(System.out::println);

    }
        static class Member{
        private String name;
        private int age;

            public Member(String name, int age) {
                this.name = name;
                this.age = age;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }
        }
}

🔵 결과

20
40
60


정렬 - sorted()


Comparator를 지정하지 않으면 스트림 요소의 기본 정렬 기준(Comparable)으로 정렬한다. 단, 스트림의 요소가 Comparable을 구현한 클래스가 아니면 예외가 발생한다.

💾 API

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

변환 - .boxed()


기본 자료형 -> Stream<Integer>로 변환해준다.

  • IntStream -> boxed() -> Stream<Integer>
  • LongStream -> boxed() -> Stream<Long>
  • DoubleStream -> boxed() -> Stream<Double>

💾 API

Stream<Integer> boxed()
// Returns a Stream consisting of the elements of this stream, each boxed to an Integer.
🔴 예시
public class StreamEx9 {
    public static void main(String[] args) {
        int[] nums = {11, 5, 3, 22, 15, 75};

        // IntStream -> .boxex() -> Stream<Integer>
        int[] array = Arrays.stream(nums).boxed().sorted(Comparator.reverseOrder()).mapToInt(x -> x).toArray();
        System.out.println(Arrays.toString(array));
    }
}

🔵 결과

[75, 22, 15, 11, 5, 3]

조회 - peek()


forEach와 비슷하나 스트림의 요소를 소모하지 않는 중간 연산으로 연산 사이에 여러번 넣어도 된다.


mapToInt(), mapToLong(), mapToDouble()


map()은 연산결과로 Stream 타입의 스트림을 반환하지만 기본자료형인 int, long, double으로 반환해 주는 기본 스트림을 반환

일반 -> 기본자료형 : mapToInt, mapToLong, mapToDouble

  • List<Integer> -> Stream<Integer> -> mapToInt(...) -> IntStream // 정수 관련 특수 기능을 사용하기 위해서(연산의 효율성을 위해서)
  • Set<Long> -> Stream<Long> -> mapToLong(...) -> LongStream

💾 API

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)

flatMap() - Stream<T[]>를 Stream로 변환


스트림의 타입이 Stream<T[]>인 경우 Stream로 변환해 준다.

🔴 예시
Stream<String> strStrm = Stream.of("abc", "def", "jklmn");
Stream<String> strStrm2 = Stream.of("ABC", "GHI", "JKLMN");

Stream<Stream<String>> strmStrm = Stream.of(strStrm, strStrm2);
Stream<String> strStream = strmStrm.map(s -> s.toArray(String[]::new)) // Stream<Stream<String>> -> Stream<String[]>
												.flatMap(Arrays::stream); // Stream<String[]> -> Stream<String>



📑 스트림의 최종연산

forEach()


forEach()는 peek()와 달리 스트림의 요소를 소모하는 최종연산으로 반환 타입이 void이므로 스트림의 요소를 출력하는 용도로 사용된다.

💾 API

void forEach(Consumer<? super T> action)

조건 검사 - allMatch(), anyMatch(), noneMatch(), findFirst(), findAny()


  • allMatch() : 지정된 조건에 모든 요소가 일치하는지
  • anyMatch() : 지정된 조건에 일부 요소가 일치하는지
  • noneMatch() : 지정된 조건에 어떤 요소도 일치하지 않는지
  • findFirst() : 지정된 조건에 일치하는 첫 번째 것을 반환
  • findAny() : 지정된 조건에 일치하는 첫 번째 것을 반환(병렬 스트림에서 사용)

💾 API

boolean allMatch(Predicate<? super T> predicate)
boolean anyMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)

통계 - count(), sum(), average(), max(), min()


  • IntStream과 같은 기본형 스트림에는 스트림의 요소들에 대한 통계 정보를 얻을 수 있는 메서드들이 있다.
    = 기본형 스트림이 아닌 경우에는 통계 관련 메서드가 3개만 있다(count(), max(), min())


🔍 collect()


스트림의 최종연산, 매개변수로 컬렉터를 필요로 한다.

💾 API

Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로

스트림 컬렉션과 배열로 변환 - toList(), toSet(), toMap(), toCollection(), toArray()


🔴 예시
List<String> names = stuStream.map(Student::getName).collect(Collectors.toList());
ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));
Student[] stuNames = studentStream.toArray(Student[]::new);  // OK
Object[] stuNames = studentStream.toArray(); // OK

문자열 결합 - joining()


  • 문자열 스트림의 모든 요소를 하나의 문자열로 연결해서 반한다.
  • 구분자를 지정할 수 있고, 접두사와 접미사도 지정가능하다.
  • 스트림의 요소가 String이나 StringBuffer 처럼 CharSequence의 하위 인터페이스인 경우에만 결합이 가능하다
  • 스트림의 요소가 문자열이 아닌 경우에는 먼저 map()을 이용해서 스트림의 요소를 문자열로 변환해야 한다.
🔴 예시
String studentNames = stuStream.map(Student::getName).collect(Collectors.joining());
String studentNames = stuStream.map(Student::getName).collect(Collectors.joining(","));
String studentNames = stuStream.map(Student::getName).collect(Collectors.joining(",","[", "]"));

변환

0개의 댓글