리팩토링을 하며, 스트림의 필요성을 알게되었다.
오늘은 스트림에 대해서 공부를 해볼 것이다.
List, Set, Map과 같은 Java Collection을 똑같은 방식으로 다루기 위한 객체
다음 코드 두개로 바로 이해해보자.
ArrayList -> 중복 제거 -> 정렬 -> 출력
기존 Java Code
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(10);
arr.add(5);
arr.add(6);
arr.add(5);
//중복 제거 로직
//for() ~~~
//정렬 로직
//for() ~~~
//출력 로직
//for() ~~~
Stream을 이용한 Java Code
List<String> arr = new List<String>();
arr.add("o");
arr.add("c");
arr.add("a");
arr.add("o");
Stream<String> listStream = arr.stream();
listStream.distinct().sorted().forEach(System.Out::println);
// = arr.stream().distinck().sorted()...
단 한 줄로도 많은 로직을 대체 할 수가 있다.
먼저 어떠한 타입을 읽고 스트림으로 생성해야한다. 코드로 바로 살펴보자.
// ----------- 객체로부터 스트림 생성----------
Stream<String> strStream = Stream.of("a", "b", "c"); //생성
int[] intArr = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.steam(intArr); //생성
굳이 2개로 나눈 이유가 무엇일까?
거기에 대한 답은 다음과 같다.
IntStream은 int 타입이라는 것을 알고 있다. 그래서 average, sum과 같은 숫자를 다루는 함수가 추가적으로 제공된다.
Stream -> IntStream으로 변경하는 함수는 찾아보니까 제공되지 않는 것 같다..
//-------------랜덤 스트림 생성 --------------//
IntStream intStream = new Random().ints(int begin, int end); //난수 무한스트림 생성
IntStream intStream = new Random().ints(int streamSize, int begin, int end); // 난수 유한스트림 생성
//LongStream, DoubleStream도 있다. - longs(), doubles()
자주 쓰이는 일을 없을 듯 하지만, 알아두도록 하자!
// -------------범위로 생성 -----------------
IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4
IntStream intStream = IntStream.ranged(1, 5); //1, 2, 3, 4, 5
// ----------람다식으로 스트림 생성-----------
Stream<Integer> evenStream = Stream.iterate(0, n->n+2) // 0, 2, 4, 6 ~
Stream<Double> randomStream = Stream.generate(Math::random);//랜덤
Stream<Integer> oneStream = Stream.generate(() -> 1);
// 1, 1, 1, 1, ~
가장 많이 쓰이는 내용이 될 듯하다.
스트림은 위의 코드와 같이 생성할 수 있다.
더 많은 기능이 있는데, 필요하다면 찾아보도록 하자!
스트림의 흐름은 다음과 같다.
생성 -> 중간 연산(n번 반복) -> 최종 연산 -> 스트림 소멸
중간 연산은 주로 Stream으로 리턴이 된다.
최종 연산은 주로 void, Optional, 기본 타입으로 리턴이 된다.
중간 연산에 대해서 알아보도록 하자.
중간 연산은 생성된 스트림에 임의의 조작을 하는 것이다.
위에서 보았던 "distinct", "sorted"와 같다.
중간 연산이 어느 것이 있는지 살펴보자.
리턴 | 함수 명 | 파라미터 | 설명 |
---|---|---|---|
Stream <T> | distinct | - | 중복 제거 |
Stream <T> | filter | Predicate<T> predicate | 조건에 맞지 않는 요소 제거 |
Stream <T> | limit | long maxSize | maxSize 이후의 요소 삭제 |
Stream <T> | skip | long n | n만큼 뛰어넘음 |
Stream <T> | sorted | - & Comparator<T> comparator | 스트림의 요소를 정렬(없으면 오름차순) |
Stream <T> | map | Function<T,R> mapper | 스트림의 요소를 변환한다. |
헷갈릴만한 것만 골라서 보자!
//-----------filter-----------
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
IntStream intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i->i%2==0 && i%3!=0) // 논리 연산자도 가능
// = intStream.filter(i->i%2==0).filter(i->i%3==0);
//-----------sorted-----------
studentStream.sorted(Comparator.comparint(Student::getBan));/// 반별로 정렬
studentStream.sorted(Comparator.comparint(Student::getBan))//반별로 정렬
.thenComparing(Student::getTotalScore)// 총점으로 정렬
.thenComparing(Student::getName)) //이름으로 정렬
.forEach(System.out::println);
//------------map-------------
Stream<File> fileStream = Stream.of(new File("Ex1.java"),
new File("Ex1"),
new File("Ex1.bak"),
new File("Ex2.java"),
new File("Ex1.txt"));
fileStream.map(File::getName) //Stream<File> -> Stream<String>
.filter(s->s.indexOf('.') != -1) //확장자 없으면 제거
.map(s->s.substring(s.intexOf('.')+1)) //Stream<String>(이름 전체) -> Stream<String>(확장자만)
.map(String::toUpperCase) // Stream<String> -> Stream<String> 대문자로
.distinct() //중복 제거
.forEach(System.out::println); // JAVABAKTXT
// File Stream을 name만 뽑아서 String Stream으로 변환해주었다.
다음은 최종 연산을 보자.
리턴 | 함수 명 | 파라미터 | 설명 |
---|---|---|---|
void | forEach | Consumer<? super T> action) | 각 요소에 지정된 작업 수행 |
void | forEachOrdered | Consumer<? super T> action | 병렬용 |
long | count | - | 스트림 요소의 개수 반환 |
Optional<T> | max/min | Comparator&#? super T> comparator | 스트림의 최대값/최소값을 반환 |
Optional<T> | findAny/findFirst | - | 스트림의 요소를 랜덤/하나를 반환 |
boolean | allMatch | Predicate<T> p | 주어진 조건을 모두 만족하는지 확인 |
Object[] | toArray | - | 스트림의 모든 요소를 배열로 반환 |
위의 최종 연산이 수행되면 해당 스트림은 소멸한다.
최종적으로 코드를 보자. 위에서 보았던 것이랑 같다.
List<String> arr = new List<String>();
arr.add("o");
arr.add("c");
arr.add("a");
arr.add("o");
Stream<String> listStream = arr.stream();
listStream.skip(1).distinct().sorted().forEach(System.Out::println);
// o,c 스킵 -> o, o 중복 제거 -> 오름차 순 정리 -> 반복 출력
스트림을 자주 사용하여 코드를 아주 짧게 쓰고, 보기 편하게 만드는 습관을 들이도록 하자!!
오늘은 여기까지 알아보도록 하자!