본문 바로가기
개발/JAVA

JAVA8 IN Action Chapter5 요점정리

by 밥버검 2018. 10. 24.
반응형

스트림 활용


필터링과 슬라이싱


프레디케이트로 필터링

스트림 인터페이스는 filter 메서드를 지원함


filter 메서드는 프레디케이트(불린반환 함수)


List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());


고유 요소 필터링

스트림은 고유 요소로 이루어진 스트림을 반환하는 distinct 메서드 지원
고유 여부는 스트림에서 만든 객체의 hashCode,equals로 결정됨

1
2
3
4
5
List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);
numbers.stream()
        .filter(i -> i % 2 == 0)
        .distinct()
        .forEach(System.out::println);
cs


스트림 축소

스트림은 주어진 사이즈 이하의 크기를 갖는 새로운 스트림을 반환하는 limit(n) 메서드를 지원함

1
2
3
4
List<Dish> dishes = menu.stream()
                    .filter(d -> d.getCalories() > 300)
                    .limit(3)
                    .collect(toList());
cs

요소 건너뛰기

처음 n개 요소를 제외한 스트림을 반환하는 skip(n) 메서드를 지원
1
2
3
4
5
6
//처음 두 요리를 건너뛴 다음에 300칼로리가 넘는 나머지 요리 반환 예제
List<Dish> dishes = menu.stream()
                        .filter(d -> d.getCalories() > 300)
                        .skip(2)
                        .collect(toList());
 
cs

매핑

스트림은 함수를 인수로 받는 map 메서드 지원함

1
2
3
4
5
6
7
8
9
10
11
12
 
//단어의 길이를 List<Integer>로 매핑( 새로운 버전을 만듬 )
List<String> words = Arrays.asList("Java8","Lambdas","In","Action");
List<Integer> wordLengths = words.stream()    
                                 .map(String::length)
                                 .collect(toList());
 
//각요리명을 추출후에 각요리명의 길이?
List<Integer> dishNameLengths = menu.stream()
                                    .map(Dish::getName)
                                    .map(String::length)
                                    .collect(toList());
cs

map과 Arrays.stream 활용
배열 스트림 대신 문자열 스트림 -> 문자열을 받아 스트림을 만드는 Arrays.stream() 메서드
1
2
3
 
String[] arrayOfWords = {"Goodbye","World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
cs
flatMap 각 단어를 개별 문자열로 이루어진 배열로 만든다음에 각 배열을 별도의 스트림으로


flatMap은 하나의 평면화된 스트림을 반환
1
2
3
4
5
6
// flatMap
        words.stream()
                 .flatMap((String line) -> Arrays.stream(line.split("")))
                 .distinct()
                 .forEach(System.out::println);
 
cs


1
2
3
4
5
6
  List<String> uniqueCharacters =
                words.stream()
                    .map(w -> w.split("")) //각 단어를 개별문자로 포함하는 배열로 변환
                    .flatMap(Arrays::stream) // 생성된 스트림을 하나의 스트림으로 평면화
                    .distinct()
                    .collect(Collectors.toList());
cs
5
6




검색과 매칭


특정 속성이 데이터 집합에 있는지 여부를 검색하는 데이터 처리에 자주 사용
allMatch,anyMatch,noneMatch,findFirst,findAny


프레디게이트가 주어진 스트림에서 적어도 한 요소와 일치하는지 확인할 때 anyMatch 메서드를 이용
1
2
3
if(menu.stream().anyMatch(Dish::isVegetarian)) {
    System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
cs


anyMatch 불린을 반환 최종연산

allMatch 프레디게이트가 모든 요소와 일치하는지 검사
스트림의 모든 요소가 주어진 프레디케이트와 일치하는지 검사
1
2
3
 
boolean isHealthy = menu.stream()
                        .allMatch(d -> d.getCalories() < 1000);
cs

noneMatch allMatch와 반대 연산 즉, noneMatch는 주어진 프레디케이트와 일치하는 요소가 없는지 확인
모든 요소들이 파라미터로 주어진 Predicate의 조건을 만족하지 않는지 조사
1
2
3
4
 
boolean isHealthy = menu.stream()
                        .noneMatch(d -> d.getCalories() >= 1000);
 
cs


anyMatch,allMatch,noneMatch 세가지 메서드는 스트림 쇼트서킷 기법 = 자바의 &&,|| 같은 연산 활용

쇼트서킷 = 표현식에서 하나라도 거짓이라는 결과가 나오면 나머지 표현식의 결과와 상관없이 전체 결과도 거짓


findAny 요소 검색 = 현재 스트림에서 임의의 요소를 반환
1
2
3
4
5
Optional<Dish> dish =
    menu.stream()
        .filter(Dish::isVegetarian)
        .findAny();
 
cs

Optional이란?
Optional<T> 클래스(java.util.Optional)는 값의 존재나 부재 여부를 표현하는 컨테이너 클래스
1
2
3
 
Optional<Dish> dish = findVegetarianDish();
        dish.ifPresent(d -> System.out.println(d.getName()));
cs


첫 번째 요소 찾기
1
2
3
4
5
6
7
 
List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDivisibleByThree =
    someNumbers.stream()
               .map(x -> x * x)
               .filter(x -> x % 3 == 0)
               .findFirst(); //9
cs


리듀싱 연산 = 모든 스트림 요소를 처리해서 값으로 도출
프로그래밍 언어 용어 = 폴드(fold)

요소의 합 : 15
1
2
3
        List<Integer> numbers = Arrays.asList(3,4,5,1,2);
        int sum = numbers.stream().reduce(0, (a, b) -> a + b);
        System.out.println(sum);
cs


메서드 레퍼런스를 이용해서 구현 Integer::sum
1
2
3
4
5
6
7
8
int sum2 = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum2); //15
 
 
//Integer::sum
public static int sum(int a, int b) {
        return a + b;
}
cs

초깃값 없음
스트림에 아무 요소도 없는 상황이면 이런식으로 구현
1
2
3
 
//reduce는 Optional 객체를 반환
Optional<Integer> sum = numbers.stream().reduce((a,b) -> (a+b));
cs


최대값과 최소값

1
2
3
4
 
List<Integer> numbers = Arrays.asList(3,4,5,1,2);
int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b));  //5
 
cs


최소값
1
Optional<Integer> min = numbers.stream().reduce(Integer::min); //1
cs


map과 reduce를 연결하는 기법 맵 리듀스(map-reduce) 패턴 쉽게 병렬화하는 특징
1
2
3
int count = menu.stream()
                        .map(d -> 1)
                        .reduce(0, (a,b) -> a+b); //9
cs

문제들
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package lambdasinaction.chap5;
 
import lambdasinaction.chap5.*;
 
import java.util.*;
 
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
 
public class PuttingIntoPractice{
    public static void main(String ...args){    
        Trader raoul = new Trader("Raoul""Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");
        
        List<Transaction> transactions = Arrays.asList(
            new Transaction(brian, 2011300), 
            new Transaction(raoul, 20121000),
            new Transaction(raoul, 2011400),
            new Transaction(mario, 2012710),    
            new Transaction(mario, 2012700),
            new Transaction(alan, 2012950)
        );    
 
        //2011년에 일어난 모든 트랜잭션을 찾아서 값을 오름차순으로 정렬
        List<Transaction> tr2011 = transactions.stream()
                                               .filter(transaction -> transaction.getYear() == 2011)
                                               .sorted(comparing(Transaction::getValue))
                                               .collect(toList());
        System.out.println(tr2011);
        //[{Trader:Brian in Cambridge, year: 2011, value:300}, {Trader:Raoul in Cambridge, year: 2011, value:400}]
        
        //거래자가 근무하는 모든 도시를 중복 없이 나열
        List<String> cities = 
            transactions.stream()
                        .map(transaction -> transaction.getTrader().getCity())
                        .distinct()
                        .collect(toList());
        System.out.println(cities);
        //[Cambridge, Milan]
 
        //케임브리지에서 근무하는 모든 거래자를 찾아서 이름순으로 정렬
        List<Trader> traders = 
            transactions.stream()
                        .map(Transaction::getTrader)
                        .filter(trader -> trader.getCity().equals("Cambridge"))
                        .distinct()
                        .sorted(comparing(Trader::getName))
                        .collect(toList());
        System.out.println(traders);
        //[Trader:Alan in Cambridge, Trader:Brian in Cambridge, Trader:Raoul in Cambridge]
        
        //모든 거래자의 이름을 알파벳순으로 정렬해서 반환
        String traderStr = 
            transactions.stream()
                        .map(transaction -> transaction.getTrader().getName())
                        .distinct()
                        .sorted()
                        .reduce("", (n1, n2) -> n1 + n2);
        System.out.println(traderStr);
        //AlanBrianMarioRaoul
 
 
        //밀라노에 거래자가 있는가?
        boolean milanBased =
            transactions.stream() //anyMatch에 프레디케이트를 전달해서 밀라노에 거래자가 있는지 확인
                        .anyMatch(transaction -> transaction.getTrader()
                                                            .getCity()
                                                            .equals("Milan")
                                 );
        System.out.println(milanBased);
        //true
        
        
        //케임브리지에 거주하는 거래자의 모든 트랜잭션값을 출력
        transactions.stream()
                    .map(Transaction::getTrader)
                    .filter(trader -> trader.getCity().equals("Milan"))
                    .forEach(trader -> trader.setCity("Cambridge"));
        System.out.println(transactions);
        //[{Trader:Brian in Cambridge, year: 2011, value:300}, {Trader:Raoul in Cambridge, year: 2012, value:1000}, {Trader:Raoul in Cambridge, year: 2011, value:400}, {Trader:Mario in Cambridge, year: 2012, value:710}, {Trader:Mario in Cambridge, year: 2012, value:700}, {Trader:Alan in Cambridge, year: 2012, value:950}]
        
        
        //전체 트랜잭션 중 최대값은 얼마?
        int highestValue = 
            transactions.stream()
                        .map(Transaction::getValue)
                        .reduce(0, Integer::max);
        System.out.println(highestValue);
        //1000
 
        //전체 트랜잭션 중 최솟값?
        Optional<Transaction> smallestTransaction =
                transactions.stream()
                            .reduce((t1,t2) ->
                                    t1.getValue() < t2.getValue() ? t1 : t2);
 
        System.out.println(smallestTransaction.get().getValue());
        //300
 
 
        Optional<Transaction> smallestTransaction2 =
                transactions.stream().min(comparing(Transaction::getValue));
 
        System.out.println(smallestTransaction2.get().getValue());
        //300
 
    }
}
cs



숫자형 스트림

숫자 스트림으로 매핑
스트림을 특화 스트림으로 변환할 때 mapToInt,mapToDouble,mapToLong = map과 정확히 같은 기능을 수행하지만
Stream<T> 대신 특화된 스트림 반환 (InteStream)

1
2
3
4
int calories = menu.stream()
                           .mapToInt(Dish::getCalories)
                           .sum();
        System.out.println("Number of calories:" + calories); //4300
cs


객체 스트림으로 복원하기
1
2
 IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
        Stream<Integer> stream = intStream.boxed(); //숫자 스트림을 스트림으로 변환
cs


기본값: OptionalInt
1
2
3
4
5
6
7
8
        OptionalInt maxCalories = menu.stream()                                                      
                                      .mapToInt(Dish::getCalories)
                                      .max(); //800
 
 
        int max;
 
        max = maxCalories.orElse(1); //값이 없을 때 기본 최대값을 명시적으로 설정
cs

숫자 범위
자바8의 IntStream과 LongStream에서 range와 rangeClosed 두가지 정적 메서드 제공
두 메서드 첫 번째 인수로 시작값, 두번째 인수로 종료값


//1부터 100까지의 짝수 스트림 -> 50개

1
2
3
4
IntStream evenNumbers = IntStream.rangeClosed(1100)
                                 .filter(n -> n % 2 == 0);
 
        System.out.println(evenNumbers.count());
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Stream<int[]> pythagoreanTriples =
               IntStream.rangeClosed(1100).boxed() 
                        //flatmap 생성된 각각의 스트림을 하나의 평준화된 스트림으로 만들어준다
                        .flatMap(a -> IntStream.rangeClosed(a, 100)  
                                               .filter(b -> Math.sqrt(a*+ b*b) % 1 == 0).boxed()
                                               .map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)}));       
 
        pythagoreanTriples.limit(5).forEach(t -> System.out.println(t[0+ ", " + t[1+ ", " + t[2]));
 
//
345
51213
6810
72425
81517
cs



값으로 스트림 만들기
임의의 수를 인수로 받는 정적 메서드 Stream.of를 이용
//예제 Stream.of로 문자열 스트림 만드는 예제
1
2
3
4
5
6
7
8
9
// Stream.of
        Stream<String> stream = Stream.of("Java 8""Lambdas""In""Action");
        stream.map(String::toUpperCase).forEach(System.out::println);
 
//
JAVA 8
LAMBDAS
IN
ACTION
cs

empty 메서드를 이용 스트림 비울수 있음
1
2
3
// Stream.empty
Stream<String> emptyStream = Stream.empty();
 
cs

배열로 스트림 만들기
배열을 인수로 받는 정적 메서드 Arrays.stream을 이용해서 스트림을 만들 수 있음

1
2
3
// Arrays.stream
int[] numbers = {23571113};
System.out.println(Arrays.stream(numbers).sum());  //41
cs


파일로 스트림 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
long uniqueWords = 0;
try {
 
    Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) { //스트림은 자원을 자동으로 해제할수 있는 AutoClosable
        uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) //단어 스트림 생성
                           .distinct() //중복 제거
                           .count(); //고유 단어 수 계산
    }
 
catch(IOException e) { //파일을 열다가 예외가 발생하면 처리 
 
}
 
cs



함수로 무한 스트림 만들기


스트림 API는 함수에서 스트림을 만들수 있는 두 개의 정적 메서드
Stream.iterate,Stream.generate 제공 , 두연산 이용 무한스트림
고정된 컬렉션에서 고정된 크기의 스트림 아닌 크기가 고정되지 않은 스트림을 만들 수 있음

무한스트림 -> 언바운드 스트림

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//iterate
 
Stream.iterate(0, n -> n + 2)
      .limit(10)
      .forEach(System.out::println);
//
0
2
4
6
8
10
12
14
16
18
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// fibonnaci with iterate
//피보나치 수열 0,1,1,2,3,5,8,13,21,34,55 .. 수열은 0,1로 시작 이후의 숫자는 이전 두 숫자를 더한 값
        Stream.iterate(new int[]{01}, t -> new int[]{t[1],t[0+ t[1]})
              .limit(10)
              .forEach(t -> System.out.println("(" + t[0+ ", " + t[1+ ")"));
 
(01)
(11)
(12)
(23)
(35)
(58)
(813)
(1321)
(2134)
(3455)
 
 
Stream.iterate(new int[]{01}, t -> new int[]{t[1],t[0+ t[1]})
              .limit(10)
              . map(t -> t[0])  
              .forEach(System.out::println);
 
0
1
1
2
3
5
8
13
21
34
cs


generate
iterate와 비슷하게 generate도 요구할 때 값을 계산하는 무한 스트림 만들수 있음
iterate와 달리 generate는 생산된 각 값을 연속적으로 계산하지 않음


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// random stream of doubles with Stream.generate
Stream.generate(Math::random)
              .limit(10)
              .forEach(System.out::println);
 
//결과
0.45439035506715
0.6853459484535649
0.8658401960545284
0.5503893827405103
0.05879337081744085
0.17644180893979233
0.7766261445389754
0.3645132613836499
0.16948893721073588
0.9746557126985628
 
 
// stream of 1s with Stream.generate
IntStream.generate(() -> 1)
                 .limit(5)
                 .forEach(System.out::println);
 
//결과
1
1
1
1
1
 
 
 
 
IntStream.generate(new IntSupplier(){
  public int getAsInt(){
      return 2;
  }
}).limit(5)
  .forEach(System.out::println);
   
//결과
2
2
2
2
2
 
 
IntSupplier fib = new IntSupplier(){
                  private int previous = 0;
                  private int current = 1;
                  public int getAsInt(){
                      int nextValue = this.previous + this.current;
                      this.previous = this.current;
                      this.current = nextValue;
                      return this.previous;
                  }
              };
IntStream.generate(fib).limit(10).forEach(System.out::println);
 
//결과
1
1
2
3
5
8
13
21
34
55
 
cs

요약

1.filter,distinct,skip,limit 메서드로 스트림을 필터링하거나 자를 수 있다
2.map,flatMap 메서드로 스트림의 요소를 추출하거나 변환할수 있다
3.findFirst,findAny 메서드로 스트림의 요소를 검색할 수 있다
allMatch,noneMatch,anyMatch 메서드를 이용해서 주어진 프레디케이트와 일치하는 요소를 스트림에서 검색할 수 있음
이들 메서드는 쇼트서킷 -> 결과를 찾는 즉시 반환, 전체 스트림을 처리하지 않음

4.reduce 메서드로 스트림의 모든 요소를 반복 조합하며 값을 도출할 수 있다
예) 스트림의 최대값이나 모든 요소의 합계 구할수 있음

5.filter,map 등은 상태를 저장하지 않는 상태 없는 연산
reduce 같은 연산은 값을 계산하는데 필요한 상태를 저장

6.IntStream,DoubleStream,LongStream 기본형 특화 스트림 -> 각각의 기본형에 맞게 특화
7.컬렉션뿐 아니라 값,배열,파일, iterate와 generate 같은 메서드로도 스트림을 만들수 있음
8.크기가 정해지지 않은 스트림을 무한 스트림이라고 함






반응형

댓글