37일차

17 스트림

컬렉션 element들을 반복적으로 처리하는 일이 많다.
이런것을 스트림 api메소드가 코드를 간결하게 해준다.

17.7 중간처리

최종연산을 해야 중간처리 연산이 한번에 최종적으로 이루어진다.
특별한 일이 없으면 메소드 체이닝으로 하는게 낫다.
코드가 읽기 어려울때 나눠서 작성하면 좋다고 생각되면 나눠서 작성하면된다.
최종연산 실행시 중간연산이 한번에 실행된다.
최종처리 이전에 중간에 값을 넣으면 값을 넣고 중간연산이 한꺼번에 일어나게 된다.

리턴타입 : Stream
limit : 크기 제한
distinct : 중복 제거
peek : 중간 처리 연산 (foreach 와 비교됨)
filter : 필터링(솎아내기)
map : 매핑 (다른 원소로 매핑)
flatmap : 매핑 (다른 원소들로 매핑)
boxed : 기본타입스트림 -> 참조타입스트림 변환
sorted : 정렬

17.7.1 limit 중간 연산 : 크기 제한

스트림에 흘러오는 element의 크기(개수)를 제한한다.

public class C02Limit {
    public static void main(String[] args) {
        List<String> list = List.of("java", "css", "html", "spring", "jsp");

        Stream<String> stream1 = list.stream();
        Stream<String> stream2 = stream1.limit(3); // 스트림 원소 중 3개로 제한

        long count = stream2.count();

        System.out.println(count);

        long count2 = list.stream()
                .limit(3)
                .count();

        System.out.println(count2);
    }
}

17.7.2 distinct : 중복제거

스트림에서 중복을 제거한다.

public class C03Distinct {
    public static void main(String[] args) {
        // limit 중간연산 : 크기제한
        List<Integer> list = List.of(3, 1, 2, 0, 1, 2, 3);

        long count = list.stream()
                .count();

        System.out.println(count); // 7

        long count2 = list.stream()
                .distinct()
                .count();

        System.out.println(count2); // 4
    }
}

17.7.3 distinct - 2

1~45개의 6개 숫자 랜덤으로 뽑아서 로또번호를 만드는 프로그램을 만들어보자.
숫자 생성 무한히 만들어내는 코드 작성
여기서 중복제거 -> 6개제한

public class C05Distinct {
    public static void main(String[] args) {
        // 1~45 로또번호 뽑기
        Stream<Integer> stream = Stream.generate(() -> (int) (Math.random() * 45) + 1);

        stream
                .distinct()
                .limit(6)
                .forEach(System.out::println);

        // Random Class 사용
        Random random = new Random();
        Stream.generate(() -> random.nextInt(45) + 1)
                .distinct()
                .limit(6)
                .forEach(System.out::println);
    }
}

17.7.4 peek()

foreach와 유사한 기능을한다.
스트림을 리턴한다.
Consumer타입의 인터페이스를 파라미터로 받는다.
함수형인터페이스로 accept(T t)파라미터 하나받고 하나를 처리하는 추상메소드를 가진다.

forEach는 최종연산을 처리하기 위한 것이다.
peek는 중간에 일이 잘 처리되고 있는지 눈으로 확인하고 싶을때 버그잡으려고 활용하는 경우가 많다.
디버깅용도로 많이 사용한다.

public class C06Peek {
    public static void main(String[] args) {
        // peek
        // 랜덤한 인티저 값 생성
        // 한꺼번에 일하기 때문에 2번씩 5번 출력
        new Random().ints(5)
                .peek(System.out::println)
                .forEach(System.out::println);
    }
}

17.7.5 filter()

자주 사용한다. 걸러내고 솎아내는 일을 한다.
Stream으 리턴한다 파라미터로 Predicate인터페이스를 받는다.
test(T t) 추상메소드를 가지고 있다.
파라미터를 하나씩받고 각 원소에 따라 boolean타입을 리턴한다.
true면 살아남고 false면 걸러진다.

public class C07Filter {
    public static void main(String[] args) {
        // filter : 걸러내기
        List<Integer> list = List.of(3, 9, -1, 0, 2, -3, -6, 7, 8);

        System.out.println("양수만##");
        list.stream()
                .filter(s -> s > 0)
                .forEach(System.out::println);

        System.out.println("짝수만##");
        list.stream()
                .filter(s -> s % 2 == 0)
                .forEach(System.out::println);

        System.out.println("홀수만##");
        list.stream()
                .filter(s -> s % 2 != 0)
                .forEach(System.out::println);
    }
}

17.7.6 확인문제 5번

list에 Stream으로 작성되어잇다.
대소문자 상관없이 java가 포함되어 있으면 필터링해서 출력하자.
1.고전 for문으로 작성해보기
2.스트림api사용해보기

public class Example {
    public static void main(String[] args) {
        List<String> list = Arrays.asList(
                "This is a java book",
                "Lambda Expressions",
                "Java8 supprots lambda expressions");

        // 고전 for
        for (String s : list) {
            if (s.toLowerCase().contains("java")) {
                System.out.println(s);
            }
        }

        //stream
        list.stream()
                .filter(s -> s.toLowerCase().contains("java"))
                .forEach(s -> System.out.println(s));
    }
}

17.7.7 map()

어떤 element를 다른 element로 매핑(변환)하는 작업이다.
Function인터페이스를 파라미터로 받는다.
apply(T t) 추상메소드를 가지고 있다.
파라미터를 하나 받고 리턴값을 주는데 파라미터의 타입과 리턴타입이 다르다.
T타입을 받아서 R타입을 리턴한다.
파라미터로 들어가는 element는 스트림의 element이고 리턴하는 것은 다음으로 사용할 스트림으로 리턴하는 것이다.

매핑이된 스트림이 다음 스트림에서 사용이 되는 것이다.

public class C08Map {
    public static void main(String[] args) {
        // map : 매핑(변환)
        List<Integer> list = List.of(2, 1, 0, 3, 9);

        System.out.println("0으로 매핑 ####");
        list.stream()
                .map(e -> 0)
                .forEach(System.out::println);

        System.out.println("*2(짝수)로 매핑 ####");
        list.stream()
                .map(e -> e * 2)
                .forEach(System.out::println);
    }
}

17.7.7 map() -2

원래 스트림이 string이면 매핑도 string 이어야하나 -> 아니다.
function이 리턴하는 것은 다른 리턴타입을 리턴해도 된다.
원래 원소와 같은 타입이 아니어도 된다.

스트링 스트림을 인티저 스트림으로 변환할 수 있다.

public class C10Map {
    public static void main(String[] args) {
        // map : 매핑(변환)
        List<String> list = List.of("java", "html", "css", "spring");

        Stream<String> stream1 = list.stream();

        // 다른 타입으로 매핑
        Stream<Integer> stream2 = stream1.map(s -> s.length());
        stream2.forEach(System.out::println);

        System.out.println("짝수 길이 스트링만 출력###");
        long count = list.stream()
                .map(s -> s.length())
                .filter(s -> s % 2 == 0)
                .count();
        System.out.println(count); //3
    }
}

17.7.8 map() -3

intStream에 값을 reduce할 수 있는 메소드들이 이미 있다.
어렵게 reduce를 정의해서 사용하지 않고 그냥 바로 사용하고 싶다면
스트림을 인트스트림으로 변경할 수 있다.

mapToInt()하면된다.
applyAsInt추상메소드를 가진 인터페이스를 파라미터로 받는데 int를 리턴한다.
그래서 intStream으로 변경할 수 있게 된다.

Mapxxx()의 메소드를 가지고 있다.
즉 참조타입 스트림이아니라 기본타입 스트림으로 변경할 수 있다.

public class C11Map {
    public static void main(String[] args) {
        List<Book> list = List.of(
                new Book("salmdunk", 3000),
                new Book("avatar", 4000),
                new Book("theh glory", 5000),
                new Book("avengers", 2000));

        int sum = list.stream()
                .map(b -> b.getPrice())
                .reduce((a, b) -> a + b)
                .get();

        System.out.println(sum); //14000

        double avg = list.stream()
                .mapToInt(Book::getPrice)
                .average()
                .getAsDouble();

        System.out.println(avg); //3500.0
    }
}

17.7.9 map() -4

mapToInt 연습
최대점수 최소점수 총합 평균
summaryStatistics()메소드는 IntSummaryStatistics객체를 리턴한다.
IntStream의 count, sum, min, avg, max을 저장하는 객체이다.

public class MapExample {
    public static void main(String[] args) {
        // List컬렉션 생성
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("홍길동", 85));
        studentList.add(new Student("홍길동", 92));
        studentList.add(new Student("홍길동", 87));

        // Student를 score 스트림으로 변환
        studentList.stream()
                .mapToInt(s -> s.getScore())
                .forEach(score -> System.out.println(score));

        int max = studentList.stream()
                .mapToInt(s -> s.getScore())
                .max()
                .getAsInt();

        int min = studentList.stream()
                .mapToInt(s -> s.getScore())
                .min()
                .getAsInt();

        int sum = studentList.stream()
                .mapToInt(s -> s.getScore())
                .sum();

        double avg = studentList.stream()
                .mapToInt(s -> s.getScore())
                .average()
                .getAsDouble();

        System.out.printf("최대점수: %d, 최소점수: %d, 총합: %d, 평균: %f\n", max, min, sum, avg);
        //최대점수: 92, 최소점수: 85, 총합: 264, 평균: 88.000000

        // summaryStatistics() : 통계
        IntSummaryStatistics summary = studentList.stream()
        .mapToInt(s -> s.getScore())
        .summaryStatistics();

        System.out.println(summary);
        //IntSummaryStatistics{count=3, sum=264, min=85, average=88.000000, max=92}
    }
}

결론적으로
같은 타입으로 매핑할수도 잇고
아예 다른 타입의 참조타입으로 매핑할 수도 있다.
또한 참조타입을 기본타입 스트림으로 매핑할 수도 있다.

17.7.10 확인문제 6번

List에 있는 Member의 평균 나이 출력하기

public class Example {
    public static void main(String[] args) {
        List<Member> list = Arrays.asList(
                new Member("홍길동", 30),
                new Member("신용권", 40),
                new Member("감자바", 26));

        // 고전for
        System.out.println("고전for문 사용");
        int sum = 0;
        for (Member m : list) {
            sum += m.getAge();
        }
        double avg = (double) sum / list.size();
        System.out.println("평균나이: " + avg); // 32.0

        // stream
        System.out.println("IntStream, average 사용###");
        avg = list.stream()
                .mapToInt(m -> m.getAge())
                .average()
                .getAsDouble();
        System.out.println("평균나이: " + avg); // 32.0

        System.out.println("stream, collect 사용###");
        avg = list.stream()
                .collect(Collectors.averagingDouble(Member::getAge));
        System.out.println("평균나이: " + avg);
    }
}

17.7.11 map - 5

참조타입 -> 기본타입 의 매핑을 해봣다.
반대로 기본타입 -> 참조타입 매핑을 해보자.
mapToObj()메소드
기본타입 int를 받아서 참조타입으로 리턴해준다.

기본타입을 참조타입으로 변환하는 일은 종종있다.
mapToObj()는 아무 참조타입으로 변경할 수 있다.

public class C12Map {
    public static void main(String[] args) {
        //기본타입 int가 원소 스트림
        IntStream stream1 = IntStream.range(11, 20);

        //참조타입 Intger가 원소인 스트림으로 변환
        Stream<Integer> stream2 = stream1.mapToObj(Integer::valueOf);
        long count = stream2.count();
        System.out.println(count);
    }
}

그런데 특별히 Integer로 변경하고 싶다면 boxed()를 사용하면된다.
Stream<Integer>를 리턴한다.

public class C13Map {
    public static void main(String[] args) {
        // boxed :
        // 기본타입을 원소로 갖는 stream
        // --> 참조타입을 원소로 갖는 stream 변환
        IntStream stream1 = IntStream.range(1, 10);
        Stream<Integer> stream2 = stream1.boxed();

        DoubleStream stream3 = DoubleStream.of(3.14, 9.99);
        Stream<Double> stream4 = stream3.boxed();
    }
}

의문점?
기본타입스트림으로도 count등등 다 사용이 가능하다.
그러데 boxed()를 사용해서 굳이 담는 이유가 무엇일까?
int 자체로는 Collection에 못 담기 때문에
Integer 클래스로 변환하여 List<Integer> 로 담기 위해 등의 기능을 수행하기 위해 존재한다.

intStream = Arrays.stream(intArray);
List<Integer> r1 = intStream.boxed()
        .collect(Collectors.toList());
System.out.println(r1);

17.7.12 flatMap()

매핑과 비슷하다. map 원래 원소의 1대1매핑이다.
flatMap은 원래 원소를 다른 원소들로 1대n매핑하는 것이다.
flatMap()은 파라미터로 Function인터페이스를 받는다.
map처럼 인풋1개 리턴1개를 리턴한다.
리턴은 스트림 혹은 그 하위타입이어야한다. 즉 스트림을 리턴하는 중간처리라는 것이다.

하나 받아서 여러 element가 있는 스트림으로 만든다.

public class C14FlatMap {
    public static void main(String[] args) {
        // flatMap:
        // 원래 원소를 다른 원소 '들'로 매핑

        List<Integer> list = List.of(3, 4, 5);

        // map: 1 대 1
        long count1 = list.stream()
                .map(e -> e)
                .count();
        System.out.println(count1); // 3

        // flatMap : 1 대 다(0이상)
        long count2 = list.stream()
                .flatMap(e -> Stream.of(0, 0)) // 각 값이 들어와서 두개짜리 stream으로 만듬
                .count();
        System.out.println(count2); //6
    }
}

17.7.12 flatMap() - 2

스페이스로 구분된 단어의 개수를 구해보고 싶다.
첫번째 원소를 배열로 만들고 스트림을 만들고
두번째 원소를 배열로 만들고 스트림으로 만들고
flatMap이 이 스트림들을 flat하게 펼쳐서 하나의 스트림으로 만들어준다.

public class C15FlatMap {
    public static void main(String[] args) {
        List<String> list = List.of(
                "hello world",
                "java program",
                "css html js");

        long count = list.stream()
                .flatMap(s -> Arrays.stream(s.split(" ")))
                .count();
        System.out.println(count); // 7
    }
}

flatMap은 리턴하는 스트림이 정해주기 나름이다.
flatMapToInt는 인트스트림을 flatMapToDouble은 더블 스트림을 리턴한다.

List<String> list2 = Arrays.asList("10, 20, 30", "40, 50");
list2.stream()
        .flatMapToInt(data -> {
            //Steam배열을 int배열로 만듬
            String[] strArr = data.split(",");
            int[] intArr = new int[strArr.length];
            for (int i = 0; i < strArr.length; i++) {
                intArr[i] = Integer.parseInt(strArr[i].trim());
            }
            //Arrays.stream()메소드는 주어진 인트배열을 IntStream으로 만듬
            return Arrays.stream(intArr);
        })

list2.stream()
        .flatMap(data -> Arrays.stream(data.split(",")))
        .map(s -> s.trim())
        .mapToInt(s -> Integer.parseInt(s))
        .forEach(System.out::println);

17.7.13 sorted

sorteds는 정렬된 Stream을 리턴한다.
natural order로 정렬된 스트림을 리턴한다.

public class C16Sorted {
    public static void main(String[] args) {
        // sorted : 정렬된 Stream return
        List<String> list = List.of(
                "chisoo",
                "backho",
                "taewoong",
                "taesup",
                "daeman");

        list.stream()
                .sorted()
                .forEach(System.out::println);
        // backho chisoo daeman taesup taewoong
    }
}

17.7.14 sorted - 2

sorted(comparator)
Comparator 인터페이스를 파라미터로 받는 메소드도 있다.
sroted()에 파라미터로 람다식으로 Comparator를 구현해주면된다.

public class C17Sorted {
    public static void main(String[] args) {
        List<Movie> movie = List.of(
                new Movie("avavtar", 10000),
                new Movie("slamdunk", 9000),
                new Movie("avengers", 7000),
                new Movie("titanic", 15000));

        movie.stream()
                .sorted((m1, m2) -> m1.getPrice() - m2.getPrice())
                .forEach(System.out::println);

        movie.stream()
        .sorted((m1, m2) -> m1.getTitle().compareTo(m2.getTitle()))
        .forEach(System.out::println);
    }
}

17.7.14 sorted - 3

nautral order가 정의되어 있어도 Comparator를 구현하면 Comparator가 우선된다.

public class C18Sorted {
    public static void main(String[] args) {
        List<Car> list = List.of(
                new Car("genesis", 7000),
                new Car("avante", 2500),
                new Car("tesla", 5000),
                new Car("morning", 1500));

        list.stream()
                .sorted()
                .forEach(System.out::println);

        System.out.println();

        list.stream()
                .sorted((a, b) -> a.getName().compareTo(b.getName()))
                .forEach(System.out::println);
    }
}

역정렬도 재정의 해주면되는데 역정렬하는 경우가 많아서 따로 메소드가 있다.
sorted(Comparator.reverseOrder())하면 natural order의 반대로 정렬해준다.
이걸사용하려면 comparable을 구현하고 있어야한다. natural order의 반대로 뒤집기때문

list.stream()
        .sorted((a, b) -> b.getPrice() - a.getPrice())
        .forEach(System.out::println);

list.stream()
        .sorted(Comparator.reverseOrder())
        .forEach(System.out::println);

17.7.15 sorted - 4

IntStream.range(1, 6)을 반대로 정렬해보자.
IntStream.range(1, 6).sorted((a, b) -> Integer.compare(b, a))
하고 싶어도 intStream의 sorted는 Comparator를 구현할 수 없다.
기본타입 int는 상속받을 수 없기때문이다.

그래서 기본타입 스트림을 참조타입 스트림으로 매핑해주면된다.

public class C19Sorted {
    public static void main(String[] args) {
        // 5~1 출력
        IntStream.range(1, 6)
                .boxed() // .mapToObj(a -> a)
                .sorted((a, b) -> Integer.compare(b, a))
                // .sorted(Comparator.reverseOrder())
                .forEach(System.out::println);
    }
}

17.7.16 확인문제 7번

직업이 개발자인 사람을 모아서 리스트에 담기

public class Example {
    public static void main(String[] args) {
        List<Member> list = Arrays.asList(
                new Member("홍길동", "개발자"),
                new Member("김나리", "디자이너"),
                new Member("신용권", "개발자"));

        // 고전적 for
        System.out.println("고전 for ####");
        List<Member> devlopers1 = new ArrayList<>();
        for (Member member : list) {
            if (member.getJob().equals("개발자")) {
                devlopers1.add(member);
            }
        }
        for (Member member : devlopers1) {
            System.out.println(member.getName());
        }

        // stream
        System.out.println("stream ####");
        List<Member> devlopers2 = list.stream()
                .filter(m -> m.getJob().equals("개발자"))
                .collect(Collectors.toList());

        devlopers2.stream()
                .forEach(m -> System.out.println(m.getName()));
    }
}

2023.03.20 후기

스트림을 다 나갔다. 앞으로 코드를 짤때 한번씩 의도적으로 사용하려고 노력해봐야 잊지 않을 것 같다.
사용법 자체는 많이 어렵지는 않은데 잊게 된다.

'국비 > Java' 카테고리의 다른 글

2023.03.22 39일차 Java  (0) 2023.03.22
2023.03.21 38일차 Java  (0) 2023.03.21
2023.03.17 36일차 Java  (0) 2023.03.19
2023.03.16 35일차 Java  (0) 2023.03.16
2023.03.15 34일차 Java  (0) 2023.03.15

+ Recent posts