스트림(Stream)

이용만·2023년 3월 8일
0

🔎스트림 : 배열 및 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 하는 반복자

스트림을 사용하면, 데이터 소스가 무엇이냐에 관계없이 같은 방식으로 데이터를 가공/처리 할 수 있습니다. 다른 말로, 배열이냐 컬렉션이냐에 관계없이 하나의 통합된 방식으로 데이터를 다룰 수 있게 되었다는 뜻이다.

public class StreamOperator {
    public static void main(String[] args) {

        // ArrayList
        List<String> fruitList = new ArrayList<>();
        fruitList.add("바나나 ");
        fruitList.add("사과 ");
        fruitList.add("오렌지 ");

        // 배열
        String[] fruitArray = {"바나나 ", "사과 ", "오렌지 "};

        // 각각 스트림 생성
        Stream<String> ListStream = fruitList.stream();
        Stream<String> ArrayStream = Arrays.stream(fruitArray);

        // 출력
        ListStream.forEach(System.out::print);
        ArrayStream.forEach(System.out::print);
    }
}

//출력값
바나나 사과 오렌지 바나나 사과 오렌지

1)스트림 만들기

2)중간 연산(0~n번) : 연산 결과가 스트림인 연산, 반복 허용

3)최종 연산(0~1번) : 연산 결과가 스트림이 아닌 연산, 단 한번만 적용 가능

중간연산
distinct() : 중복제거
limit() : 자르기
sorted() : 정렬

최종 연산
forEach(System.out::println) : forEach가 스트림의 요소를 하나씩 꺼내 출력함

🔎스트림의 특징

  1. 스트림 처리 과정은 생성, 중간 연산, 최종 연산 세 단계의 파이프라인으로 구성된다.
  2. 스트림은 원본 데이터 소스를 변경하지 않는다.
  3. 스트림은 일회용이다.
  4. 스트림은 내부 반복자이다.
  1. 원본은 건들지 않는다.

3.스트림은 Iterator처럼 일회용이다.

4.스트림은 내부 반복자이다.

위에서 확인할 수 있는 것처럼, 외부 반복자의 경우 요소가 필요할 때마다 순차적으로 컬렉션에서 필요한 요소들을 불러오는 반면, 내부반복자는 데이터 처리 코드만 컬렉션 내부로 주입해줘서 그 안에서 모든 데이터 처리가 이뤄지도록 한다.

5.기본형 스트림 - IntStream, LongStream, DoubleStram
왜 사용? 오토박싱&언박싱의 비효율이 제거.(오토박싱 : 객체로 변환, 언박싱 : 오토박싱 반대)
sum, average,count,min,max 대표적인 메서드가 있다.

public class StreamCreator {

    public static void main(String[] args) {
        // int형 배열로 스트림 생성
        int[] intArr = {1,2,3,4,5,6,7};
        IntStream intStream = Arrays.stream(intArr);
        // 숫자와 관련된 경우 intStream을 사용하는 것을 권장
        System.out.println("sum=" + intStream.sum());
    }
}
//출력값
sum=28

🔎스트림의 생성

✒️배열 스트림 생성

  • Arrays.stream()
public class StreamCreator {
   
       public static void main(String[] args) {
           // 문자열 배열 선언 및 할당
           String[] arr = new String[]{"김코딩", "이자바", "박해커"};
   
           // 문자열 스트림 생성
           Stream<String> stream = Arrays.stream(arr);
   
           // 출력
           stream.forEach(System.out::println);
   
       }
   }
   
   // 출력값
   김코딩
   이자바
   박해커
  • Stream.of()
public class StreamCreator {
    public static void main(String[] args) {
        String[] arr = new String[]{"김코딩", "이자바", "박해커"};
        Stream<String> stream = Stream.of(arr);
        stream.forEach(System.out::println);
    }
}

Arrays.stream() 와 Stream.of() 메서드 모두 동일한 값을 출력하고 있기에
배열을 스트림을 생성할 때 편한거 쓰시면 된다.

✒️컬렉션 스트림 생성

List,Set 컬렉션 타입의 경우 최상위 클래스인 Collection에 정의된 stream() 메서드를 사용해서 스트림을 생성할 수 있다.

public class StreamCreator {
    public static void main(String[] args) {
        // asList()를 사용해서 List 객체를 만듬
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
        Stream<Integer> stream = list.stream();
        stream.forEach(System.out::println); ->람다식 (i) -> System.out.println(i)
    }
}

🔎스트림의 중간 연산

빈번하게 사용되는 필터링, 매핑, 정렬등 중심으로 설명하겠다.

✏️필터링(filter(), distinct()) : 조건에 맞는 데이터들만을 정제하는 역할

  • distinct() : 중복 제거
  • filter() : 조건에 맞는 데이터만을 정제. 조건이 참이 되는 요소만 필터링.
public class FilteringExample {
    public static void main(String[] args) {

        List<String> names = Arrays.asList("김코딩", "이자바", "박해커", "김코딩", "박해커");
        
        names.stream()
                .distinct()
                .forEach(element -> System.out.print(element + " "));
        System.out.println();

        names.stream()
//                .filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링
                .filter(element -> element.contains("김")) // contains 비교 메서드 이용 가능
                .forEach(element -> System.out.print(element + " "));
        System.out.println();

        names.stream()
                .distinct()
                .filter(element -> element.contains("김"))
                .forEach(element -> System.out.println(element + " "));
    }
}

✏️매핑(map()) : 원하는 필드만 추출하거나 특정 형태로 변환

filter()메서드와 마찬가지로 값을 변환하기 위한 조건을 람다식으로 정의함.

public class IntermediateOperationExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("kimcoding", "javalee", "hackerna", "luckyguy");
        names.stream()
                .map(element -> element.toUpperCase()) //요소들을 하나씩 대문자로 변환
                .forEach(element -> System.out.println(element + " "));
    }
}

-> 특정 형태로 변환

public class IntermediateOperationExample {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,3,6,9);
        list.stream()
                .map(num -> num*3)
                .forEach(num -> System.out.print(num + " "));
    }
}

flatMap()

public class IntermediateOperationExample {
    public static void main(String[] args) {
        String[][] namesArray = new String[][]{
                {"박해커","이자바"} , {"김코딩","나박사"}
        };
        Arrays.stream(namesArray)
                .map(inner -> Arrays.stream(inner))
                .forEach(element -> System.out.println(element + " "));
    }
}

객체값을 출력한다. 이유는 map() 메서드는 Stream<String[]> 즉 문자열 배열들의 스트림을 반환하기 때문이다. '박해커, 이자바, 김코딩, 나박사'로 나오려면 문자열 배열이 아닌 Stream(String)이 되어야 한다.

이때 flatMap() 메서드를 사용한다.

public class IntermediateOperationExample {
    public static void main(String[] args) {
        String[][] namesArray = new String[][]{
                {"박해커","이자바"} , {"김코딩","나박사"}
        };
        Arrays.stream(namesArray)
                .flatMap(Arrays::stream)
                .forEach(element -> System.out.println(element + " "));
    }
}

flatMap()은 중첩 구조를 제거하고 단일 컬렉션(Stream(string))으로 만들어주는 역할을 한다.

✏️정렬(sorted()) : 말 그대로 정렬

  • 기본정렬
public class IntermediateOperationExample {
    public static void main(String[] args) {
        List<String> animals = Arrays.asList("Tiger","Lion","Monkey","Duck","Horse","Cow");
        animals.stream()
                .sorted()
                .forEach(element -> System.out.print(element + " "));
    }
}

  • 역순정렬
public class IntermediateOperationExample {
    public static void main(String[] args) {
        List<String> animals = Arrays.asList("Tiger","Lion","Monkey","Duck","Horse","Cow");
        animals.stream()
                .sorted(Comparator.reverseOrder())
                .forEach(element -> System.out.print(element + " "));
    }
}


Comparator 인터페이스 안에 reverseOrder() 메서드를 통해 역순 정렬 한다.

✏️기타

🔻skip() - 스트림의 일부 요소들을 건너뜀

public class IntermediateOperationExample {
        public static void main(String[] args) {
            // 1~10 범위의 정수로 구성된 스트림 생성
            IntStream intStream = IntStream.rangeClosed(1, 10);
            // 앞의 5개의 숫자를 건너뛰고 숫자 6부터 출력
            intStream.skip(5).forEach(System.out::println);
        }
    }

🔻limit() - 스트림의 일부를 자름

 public class IntermediateOperationExample {
        public static void main(String[] args) {
            // 1~10 범위의 정수로 구성된 스트림 생성
            IntStream intStream = IntStream.rangeClosed(1, 10);
            // 앞에서부터 5개의 숫자만 출력
            intStream.limit(5).forEach(System.out::println);
        }
    }

🔻peek() - forEach() 와 마찬가지로, 요소들을 순회하며 특정 작업을 수행합니다. forEach() 와의 핵심적인 차이는 중간 연산자인지의 여부

public class IntermediateOperationExample {
    public static void main(String[] args) {
        // 요소들을 사용하여 IntStream 생성
        IntStream intStream3 = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 7, 7, 7, 8);
        // 짝수만 필터링하여 합계 구하기
        int sum = intStream3.filter(element -> element % 2 == 0)
                .peek(System.out::println)
                .sum();
        System.out.println("합계 = " + sum);
    }
}
// 출력값
2
2
4
8
합계 = 16

🔎스트림의 최종 연산

✏️기본 집계(sum() , count() , average(), max() , min())

public class TerminalOperationExample {
    public static void main(String[] args) {
        int[] intArray = {1,2,3,4,5};

        //카운팅
        long count = Arrays.stream(intArray)
                .count();
        System.out.println("카운트 개수 : " + count);

        //합계
        long sum = Arrays.stream(intArray)
                .sum();
        System.out.println("합계 : " + sum);

        //평균
        double average = Arrays.stream(intArray)
                .average()
                //average값을 더블형 변수에 넣기 위해 추출하는 메서드
                .getAsDouble();
        System.out.println("평균값 : " + average);

        //최대값
        int max = Arrays.stream(intArray)
                .max()
                .getAsInt();
        System.out.println("최대값 : " + max);

        //최소값
        int min = Arrays.stream(intArray)
                .min()
                .getAsInt();
        System.out.println("최소값 : " + min);

        //배열의 첫번째 요소
        int first = Arrays.stream(intArray)
                .findFirst()
                .getAsInt();
        System.out.println("배열의 첫번째 요소 : " + first);
    }
}

✏️매칭(allMatch(), anyMatch(), noneMatch() )

  • allMatch() - 모든 요소들이 조건을 만족하는 지 여부를 판단합니다.
  • noneMatch() - 모든 요소들이 조건을 만족하지 않는 지 여부를 판단합니다.
  • anyMatch() - 하나라도 조건을 만족하는 요소가 있는 지 여부를 판단합니다.

그룹

분할

profile
성장하는 개발자가 되고자 합니다.

0개의 댓글