🧐 왜 이 글을 작성하게 됐을까?
반복문, 조건문이 필요할 때마다 기초 교과서처럼 하면, 가독성이 떨어지는 점을 개선하기 위해 작성하게 되었다.
✔️ Stream이란?
✔️ Stream을 왜 사용하는걸까?
불필요한 코딩(for, if) 없이 구현할 수 있으며, 직관적이기 때문에 가독성이 좋아진다.
💡 참고
가독성 좋은 코드 : 코드를 읽었을 때 이해하기 쉬운 코드
✔️ Stream은 어떤 것에 적용가능할까?
Stream은 주로 Collection, Arrays에서 쓰인다.
List<String> names = Arrays.asList("kyoung", "chang", "java");
names.stream(); // Collection에서 스트림 생성
Double[] dArray = {3.1, 3.2, 3.3};
Arrays.stream(dArray); // 배열로 스트림 생성
Stream<Integer> str = Stream.of(1, 2); // 스트림 직접 생성
✔️ Stream의 구조
🤔 Stream의 실제 사용법
Collections 객체 집합.스트림생성()
.중개연산()
.최종연산()
- 파이프라인 : 계속 .으로 연계할 수 있게 하는 방법
✔️ 배열과 객체를 입력받을 때
Arrays.stream(배열)
객체.stream()
List<String> names = Arrays.asList("kyoung", "chang", "java");
names.stream(); // Collection에서 스트림 생성
Double[] dArray = {3.1, 3.2, 3.3};
Arrays.stream(dArray); // 배열로 스트림 생성 : IntStream이 반환
Stream<Integer> str = Stream.of(1, 2); // 스트림 직접 생성
✔️ Filter
조건에 맞는 것만 찾아서 Stream을 반환한다.
List<String> names = Arrays.asList("kyoung", "chang", "java");
Stream<String> s = names.stream().filter(x -> x.contains("o"));
// 결과 : kyoung
✔️ Map
Stream의 각 요소들에 대해 함수가 적용된 결과의 새로운 요소로 매핑해준다.
List<String> names = Arrays.asList("chang", "lee");
names.stream().map(x -> x.concat("abc"))
.forEach(x -> System.out.println(x));
// 결과
// changabc
// leeabc
✔️ 이외
sorted()
: Stream의 요소들을 정렬limit(a)
: Stream의 개수를 a개로 제한한다.distinct()
: Stream 요소에서 중복을 제거한다.skip(a)
: 첫 a개의 요소는 제외하고 나머지 요소들로 새로운 Stream 반환mapToInt(Integer::intValue), mapToLong(Long::longValue), mapToDouble(Double::doubleValue), mapToObj(String::valueOf)
: 해당 타입의 Stream으로 바꿔준다.boxed()
: IntStream
을 Stream<Integer>
로 변환mapToInt()
: Stream<Integer>
을 IntStream
으로 변환flatMap<List::stream>
: 변환된 Stream을 하나의 단일 Stream으로 병합한다.chars()
: IntStream으로 변환해준다. (문자열을 IntStream으로 변환) H e l l o ⇒ 72 101 108 108 111
📑 Stream에서 자주 사용되는 메서드
✔️ 갯수, 최솟값, 최댓값, 합계, 평균
count(), min(), max(), sum(), average()를 함수를 활용할 시, Stream에 있는 요소들에서 해당 값을 반환해준다.
✔️ reduce()
누적된 값을 계산하는 함수
List<Integer> ages = new ArrayList<Integer>();
ages.add(1);ages.add(2);ages.add(3);//1,2,3
System.out.println(ages.stream().reduce((b,c) -> b+c).get());//1+2+3=6
✔️ forEach()
List<Integer> ages = new ArrayList<Integer>();
ages.add(1);ages.add(2);ages.add(3);//1,2,3
Set<Integer> set = ages.stream().collect(Collectors.toSet());
set.forEach(x-> System.out.println(x));//1,2,3
✔️ collect()
Stream의 결과를 Collection으로 바꿔준다.
toMap(), toSet(), toList() 함수를 활용한다.
문자열로 반환하고 싶을 때는 Collectors의 joining()으로 가능하다.
String s = set.stream().map(String::valueOf).collect(Collectors.joining());
✔️ iterator
Stream의 값을 얻고 싶을 때 주로 사용한다.
List<String> names = Arrays.asList("jeong", "pro", "jdk", "java");
Iterator<String> iter = names.stream().iterator();
while(iter.hasNext()) {
System.out.println(iter.next());//jeong, pro, jdk, java
}
✔️ noneMatch, anyMatch, allMatch
noneMatch : 최종적으로 얻은 Stream 결과에서 모든 요소들이 조건을 만족하지 않는지 판단해서 boolean을 return
anyMatch : Stream의 요소들 중에서 하나라도 조건을 만족한다면 true를 return
allMatch : Stream의 모든 요소들이 조건을 만족한다면 true를 return
✔️ Stream은 재사용이 불가능하다.
재사용할 시 발생한 오류 : stream has already been operated upon or closed.
Stream<String> a = names.stream().filter(x -> x.contains("o"));
count = a.count();
List<String> lists = a.collect(Collectors.toList());
✔️ 병렬 스트림을 만들고 싶다면 parallelStream()을 사용하자
Stream을 생성하지 않고 병렬 스트림을 만들 수 있다.
names.parallelStream().filter(x -> x.contains("o")).count();
✔️ 중개 연산은 최종 연산이 호출될 때까지 대기한다.
데이터를 처리하기 위한 연산을 정의한 중개 연산은 지연 연산(Lazy Evaluation)의 특성을 가진다.
이는, 중개 연산은 다른 중개 연산을 호출할 때 바로 실행되지 않고 최종 연산이 호출될 때 실행된다.
✔️ 두 배열 교집합 구하기
int[] lost = {2, 4, 3, 5};
int[] reserve = {1, 3, 5};
Arrays.sort(lost);
Arrays.sort(reserve);
Set<Integer> owns = Arrays.stream(lost)
.boxed()
.collect(Collectors.toSet());
owns.retainAll(Arrays.stream(reserve)
.boxed()
.collect(Collectors.toSet()));
for (Integer own : owns) {
System.out.println(own);
}
// 결과
// 3
// 5
✔️ 배열의 각 문자열을 문자 단위로 분할하고, 중복 없는 문자들을 포함하는 구조로 변환하기
["ABC", "BCD", "DEF"] -> [["A", "B", "C"], ["B", "C", "D"], ["D", "E", "F"]]
Arrays.stream(orders)
.map(String::chars)
.map(charStream -> charStream
.mapToObj(menu -> String.valueOf((char) menu)) // char값을 String으로 변경한다.
.collect(Collectors.toSet()))
.collect(Collectors.toList());
map(String::chars)
: map 메서드를 사용하여 각 문자열을 문자 스트림으로 변환charStream.mapToObj(menu -> String.valueOf((char) menu))
: 각 문자의 코드 포인트를 문자로 변환하여, 새로운 문자열 스트림을 생성 → IntStream을 Stream<String>
으로 변환
✔️ Set<Character>
을 String으로 변환하기
Set<Character> set = key.chars().mapToObj(c -> (char)c).collect(Collectors.toSet());
String s = set.stream().map(String::valueOf).collect(Collectors.joining());
✔️ Set<Character>
에 순서 유지해서 저장하기
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String key = reader.readLine();
Set<Character> set = key.chars().mapToObj(c -> (char)c).collect(Collectors.toCollection(LinkedHashSet::new));
참고자료