1. 스트림이란?


스트림은 자바8부터 추가된 컬렉션의 저장요소를 하나씩 참조해서 람다식으로 처리할수 있도록 해주는 반복자이다.


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
package streamTest;
 
import java.util.Arrays;
import java.util.List;
 
public class myStreamTest {
 
    public static void main(String[] args) {
        int count1 = 0;
         List<String> names = Arrays.asList("1""2""3""4""5");
         
         // 기존의 for문
        for (String w : names) {
            if(w.length() > 0) {
                count1++;
            }
        }
        
        // stream 방식
        long count2 = 
                names.stream()
                         .filter(a->a.length()>0)
                         .count();
        
        // 병렬 stream 방식
        long count3 = 
                names.parallelStream()
                         .filter(a->a.length()>0)
                         .count();
        
        System.out.println(count1);
        System.out.println(count2);
        System.out.println(count3);
    }
}
cs


count1, count2, count3의 결과는 같다. 

기존for문,스트림, 병렬스트림 을 사용해서 표현한 것을 확인할 수 있다.



2. 스트림의 특징


- 스트림을 활용하면 복잡한 코드를 간결하게 표현할 수 있다.

- 스트림은 컬렉션, 배열, 파일 등의 대량의 데이터를 가공 축소하여 데이터의 합계, 

평균값, 카운팅, 최대값, 최소값, 집계함수를 모두 표현할 수 있다.


- 오리지날 스트림에서 중간처리(필터, 매핑)를 하고 최종처리(집계 처리 결과물)를 하여 결과를 볼수 있다.

- 스트림은 iterator와 비슷한 역할을 하는 반복자이다.


- 스트림이 제공하는 요소처리 메서드는 함수적 인터페이스 타입이므로 람다식, 

메서드 참조를 이용하여 요소처리 내용을 인자로 전달할 수 있다.



3. 컬렉션과 스트림의 공통점과 차이점



공통점


- 연속된 요소형식의 값을 저장하는 자료구조 인터페이스를 제공한다.

- 둘다 순차적으로 요소에 접근한다.


차이점


컬렉션은


- 각 계산식을 만날 때마다 데이터가 계산된다. 

- 데이터의 접근, 읽기, 변경, 저장이 주요 관심사이다.

- 데이터에 접근하는 방법을 직접 작성해야한다.

- Iterator로 모든요소를 순환해야한다.

- 메모리에 모든 요소가 올라가 있는 상태에서 요소들을 누적시키며 결과를 계산한다.

- 메모리 사용량이 늘어난다.


스트림은


- 최종 연산이 실행 될 때에 데이터가 계산된다. 

- 계산식(람다)을 표현하는 것이 주요 관심사이다.

- 데이터에 접근하는 방법이 추상화되어있다.

- 계산식을 미리 적어두고 계산시에 람다식으로 JVM에 넘긴다.

- 내부에서 요소들을 어떻게 메모리에 올리는 지는 관심사가 아니다.

- 메모리 사용량이 줄어든다.



스트림을 활용해 코드를 개선해보자.





4. 스트림 생성방법 예제



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
package streamTest;
 
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.IntSupplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
 
public class MakingStreams {
 
    public static void main(String...args) throws Exception{
        
        // Stream.of
        Stream<String> stream = Stream.of("Java8""Lambdas""SangHyun");
        stream.map(String::toUpperCase).forEach(System.out::println);
        // result : UpperCase
        
        // Arrays.stream
        int[] numbers = {12345};
        System.out.println(Arrays.stream(numbers).sum());
        // result : 15
        
        // Stream.iterate
        Stream.iterate(0, n -> n + 2)
              .limit(5)
              .forEach(System.out::println);
        // result : 0 2 4 6 8
        
        // random stream of doubles with Stream.generate
        Stream.generate(Math::random)
              .limit(10)
              .forEach(System.out::println);
        // result : random data
        
        // stream of 1s with Stream.generate
        IntStream.generate(() -> 1)
                 .limit(5)
                 .forEach(System.out::println);
        // result : 1 1 1 1 1 
        
        IntStream.generate(new IntSupplier(){
            public int getAsInt(){
                return 2;
            }
        }).limit(5)
          .forEach(System.out::println);
        // result : 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);
         // result : 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
         
         // 유니크 단어 카운트
         long uniqueWords = Files.lines(Paths.get("data2.txt"), Charset.defaultCharset())
                                 .flatMap(line -> Arrays.stream(line.split(" ")))
                                 .distinct()
                                 .count();
 
         System.out.println("There are " + uniqueWords + " unique words in data2.txt");
    }
}
cs




5. 스트림 활용하여 문제 풀기



5.1 Developer 모델



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
package streamTest;
 
public  class Developer{
    
    private String name;
    private String title;
 
    public Developer(String n, String c){
        this.name = n;
        this.title = c;
    }
 
    public String getName(){
        return this.name;
    }
 
    public String getTitle(){
        return this.title;
    }
 
    public void setTitle(String newTitle){
        this.title = newTitle;
    }
 
    public String toString(){
        return "Developer:"+this.name + " , " + this.title;
    }
}
cs




5.2 BiddingCompany 모델



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
package streamTest;
 
public class BiddingCompany{
 
    private Developer developer;
    private String companyName;
    private int year;
    private int salary;
 
    public BiddingCompany(Developer developer, String companyName, int year, int salary)
    {
        this.developer = developer;
        this.companyName = companyName;
        this.year = year;
        this.salary = salary;
    }
 
    public Developer getDeveloper(){ 
        return this.developer;
    }
    
    public String getCompanyName() {
        return companyName;
    }
 
    public int getYear(){
        return this.year;
    }
 
    public int getSalary(){
        return this.salary;
    }
    
    public String toString(){
        return "{" + this.developer + ", " +
               "companyName: "+this.companyName+", " +
               "year: "+this.year+", " +
               "salary:" + this.salary +"}";
    }
}
cs



5.3 StreamTestCode Main 코드



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
111
112
113
114
115
116
117
package streamTest;
 
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
 
public class StreamTestCode {
    public static void main(String ...args){    
        // 개발자의 성명과 직급
        Developer sanghyun = new Developer("SangHyun""대리");
        Developer hyunYoung = new Developer("HyunYoung","사원");
        Developer jiSook = new Developer("JiSook","주임");
        Developer choa = new Developer("Choa","대리");
        
        // 입찰회사에서 개발자 채용을 위해 연봉제시 
        // 입찰한 개발자 이름, 참여한 회사이름, 설립연도 , 제시한연봉 
        List<BiddingCompany> company = Arrays.asList(
            new BiddingCompany(sanghyun, "쿠팡",20104000), 
            new BiddingCompany(sanghyun, "위메프",20115000), 
            new BiddingCompany(sanghyun, "sk플래닛",20127000), 
            new BiddingCompany(hyunYoung, "위메프",20102000),  
            new BiddingCompany(hyunYoung, "sk플래닛",20123000), 
            new BiddingCompany(jiSook, "쿠팡",20103500),
            new BiddingCompany(choa, "위메프",20113800
        );    
        
        // 쿼리 1 : 설립일이 2012년인 입찰회사를 모두 찾아서 연봉이 작은값부터 큰값으로 출력해라
        // 2012년이라는 특정한 조건이 있으므로 filter사용, 작은값부터 큰값 정렬 sorted 사용
        
        List<BiddingCompany> t1 = 
                company.stream()
                             .filter(a->a.getYear()==2012)
                             .sorted(Comparator.comparing(b->b.getSalary()))
                             .collect(Collectors.toList());
        
        t1.forEach(System.out::println);
        /* result : {Developer:HyunYoung , 사원, companyName: sk플래닛, year: 2012, salary:3000}
                        {Developer:SangHyun , 대리, companyName: sk플래닛, year: 2012, salary:7000} */
        
        // 쿼리 2 : 입찰회사 개발자 목록에서 개발자의 중복되지 않는 직급만 출력해라.
        // 특정한 조건이 딱 주어지지 않았으므로 map사용, 중복제거 distinct사용
        
        List<String>  t2_1 =     
                company.stream()
                             .map(BiddingCompany::getDeveloper)
                             .map(Developer::getTitle)
                             .distinct()
                             .collect(Collectors.toList());
 
        System.out.println("map 2번 사용");
        t2_1.forEach(System.out::println);
        /* result : 대리 사원 주임 */
        
        List<String>  t2_2 =         
                company.stream()
                             .map(tx -> tx.getDeveloper().getTitle()) 
                             .distinct()
                             .collect(Collectors.toList());
        
        System.out.println("map 1번 사용");
        t2_2.forEach(System.out::println);
        /* result : 대리 사원 주임 */
        
        // 쿼리 3: 개발자 중에서 대리직급을 찾아 이름순으로 정렬해라.
        
        company.stream()
                     .map(a->a.getDeveloper())
                     .filter(b->b.getTitle().equals("대리"))
                     .distinct()
                     .sorted(Comparator.comparing(c->c.getName()))
                     .forEach(System.out::println);
        /* result : Developer:Choa , 대리 
                        Developer:SangHyun , 대리 */
        
        // 쿼리 4: 알파벳 순서대로 정렬하여 개발자 이름만 출력해라
        
        String names = 
                company.stream()
                             .map(a->a.getDeveloper().getName())
                             .distinct()
                             .sorted()
                             .reduce("", (a1,a2) -> a1+a2+" ");
        
        System.out.println(names);
        /* result : Choa HyunYoung JiSook SangHyun */
        
        // 쿼리 5 : 사원 직급의 개발자가 있는가?
        
        boolean 사원 = 
                company.stream()
                             .anyMatch(tx -> tx.getDeveloper().getTitle().equals("사원"));
                                    
        System.out.println(사원);
        /* result : true */
        
        // 쿼리 6 : 대리직급의 개발자를 과장직급으로 업데이트 해라
        
        System.out.println("업데이트 전: "+company);
        company.stream()
                     .map(a->a.getDeveloper())
                     .filter(a->a.getTitle().equals("대리"))
                     .forEach(a->a.setTitle("과장"));
        System.out.println("업데이트 후: "+company);
        
        // 쿼리 7: 입찰한 모든회사 중에 가장 높은 연봉을 제시한 값은 얼마인가?
        
        int highSalary = 
                company.stream()
                             .mapToInt(a->a.getSalary())
                             .max()
                             .getAsInt();
        
        System.out.println(highSalary);
        /* result : 7000 */
    }
}
cs


소스 코드 첨부 :


myLamdaTest.zip


+ Recent posts