-
스트림
-
스트림이 제공하는 기능
-
스트림의 특징
-
스트림 만들기
-
컬렉션
-
배열
-
임의의 수
-
특정 범위의 정수
-
람다식
-
파일
-
비어있는 스트림
-
두 스트림의 연결
-
스트림의 연산
-
중간 연산
-
Optional<T>
-
Optinal<T>객체 생성
-
Optional<T> 객체의 값 가져오기
-
OptionalInt, OptionalLong, OptionalDouble
-
최종 연산
-
forEach(), forEachOrdered()
-
조건 검사
-
reduce()
-
collect()
-
스트림을 컬렉션, 배열로 변환
-
스트림의 통계
-
스트림을 리듀싱
-
스트림의 그룹화와 분할
-
스트림의 변환
스트림
- 다양한 데이터 소스(컬렉션, 배열 등)를 표준화된 방법으로 다루기 위한 것으로, 데이터 소스를 추상화하고 데이터를 다루는데 자주 사용되는 메서드들을 정의해 놓았다.
- 데이터 소스를 추상화했다는 것은 데이터 소스가 무엇이든 간에 같은 방식으로 다룰 수 있게 되었다는 것과 코드의 재사용성이 높아졌다는 것이다.
String[] strARr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrays.asList(strARr);
스트림 생성
Stream<String> strStream1 = strList.stream(); // 스트림을 생성
Stream<String> strStream2 = Arrays.stream(strArr); // 스트림을 생성
데이터를 읽어서 정렬하고 출력
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
스트림을 사용하지 않을 시
Arrays.sort(strArr);
Collections.sort(strList);
for(String str : strArr)
System.out.println(str);
for(String str : strList)
System.out.println(str);
스트림을 사용하면 정렬하고 출력하는 방법이 완전히 동일해진다.
스트림이 제공하는 기능
중간 연산과 최종 연산
중간 연산 : 연산 결과가 스트림인 연산. 반복적으로 적용 가능
최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한 번만 가능

스트림의 특징
데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다.
List<Integer> list = Arrays.asList(3, 1, 5, 4, 2);
List<Integer> sortedList = list.stream().sorted() // list를 정렬해서
.collect(Collectors.toList()); // 새로운 List에 저장
System.out.println(list); //[3,1,5,4,2]
System.out.println(sortedList); //[1,2,3,4,5]
Iterator처럼 일회용이다.(필요하면 다시 스트림을 생성해야 함)
strStream.forEach(System.out::println); // 모든 요소를 화면에 출력(최종연산)
int numOfStr = strStream.count(); // 에러. 스트림이 이미 닫혔음
최종 연산 전까지 중간연산이 수행되지 않는다.(지연된 연산)
IntStream intStream = new Random().ints(1, 46); // 1~45범위의 무한 스트림
intStream.distinct().limit(6).sorted() // 중간 연산
.forEach(i-> System.out.println(i+",")); // 최종 연산
작업을 내부 반복으로 처리한다.


작업을 병렬로 처리(병렬 스트림)
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
int sum = strStream.parallel() // 병렬 스트림으로 전환(속성만 변경)
.mapToInt(s -> s.length()).sum(); // 모든 문자열의 길이의 합
기본형 스트림 (IntStream, LongStream, DoubleStream 등)
- 오토박싱 & 언박싱의 비효율이 제거됨(Stream<Integer> 대신 IntStream사용)
- 숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공
스트림 만들기
컬렉션
Collection인터페이스 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream()
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream(); // list를 데이터 소스로 하는 새로운 스트림 생성
배열
Stream<T> Stream.of(T... values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive) // 배열 일부만
Stream<String> stgrStream = Stream.of("a", "b", "c");
Stream<String> stgrStream = Stream.of(new String[]{"a","b","c"});
Stream<String> stgrStream = Arryas.stream(new String[]{"a","b","c"});
Stream<String> stgrStream = Arryas.stream(new String[]{"a","b","c"}, 0, 3);
기본형 배열로부터 스트림 생성하기
IntStream IntStream.of(int... values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)
임의의 수
난수를 요소로 갖는 스트림 생성
IntStream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력
IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환
지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드(Random클래스)

특정 범위의 정수
특정 범위의 정수를 요소로 갖는 스트림 생성(IntStream, LongStream)
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5); // 1,2,3,4,5
람다식
람다식을 소스로 하는 스트림 생성
// 이 둘은 무한스트림
static <T> Stream<T> iterate(T seed, UnaryOperator<t> f) // 이전 요소에 종속적
static <T> Stream<T> generate(Supplier<T> s) // 이전 요소에 독립적
iterate()는 이전 요소를 seed로 해서 다음 요소를 계산한다.
Stream<Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6, ...

generate()는 seed를 사용하지 않는다.
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(()->1); // 1, 1, 1, 1, ...
파일
Stream<Path> Files.list(Path dir) // Path는 파일 또는 디렉토리
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // BufferedReader클래스의 메서드
파일 내용을 라인 단위로 읽어서 스트림으로 생성
비어있는 스트림
Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다.
long count = emptyStream.count(); // count의 값은 0
두 스트림의 연결
static메서드인 concat()을 사용하여 두 스트림을 하나로 연결할 수 있다. 두 스트림은 같은 타입이어야 한다.
String[] str1 = {"123", "456", "789"};
String[] str2 = {"ABC", "abc", "DEF"};
String<String> strs1 = Stream.of(str1);
String<String> strs2 = Stream.of(str2);
String<String> strs3 = Stream.concat(strs1, strs2);
스트림의 연산
중간 연산
연산결과가 스트림인 연산이며 반복적으로 적용 가능하다.

스트림 자르기
skip(), limit()
Stream<T> skip(long n) // 앞에서부터 n개 건너뛰기
Stream<T> limit(long maxSize) // maxSize 이후의 요소는 잘라냄
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print); // 45578
스트림 요소 걸러내기
filter(), distinct()
Stream<T> filter(Predicate<? super T> predicate) // 조건에 맞지 않는 요소 제거
Stream<T> distinct() // 중복제거
IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 5, 5, 6);
intStream.distinct().forEach(System.out::print); // 123456
IntStream intStream = IntStream.rangeClosed(1, 10); //12345678910
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
intStream.filter(i->i%2!=0 && i%3!=0).forEach(System.out::print); // 246810
intStream.filter(i->i%2!=0).filter(i->i%3!=0).forEach(System.out::print); // 246810
스트림 정렬하기
sorted()
Stream<T> sorted() // 스트림 요소의 기본 정렬(Comparable)로 정렬
Stream<T> sorted(Comparator<? super T> comparator) // 지정된 Comparator로 정렬

Comparator의 comparing()으로 정렬 기준을 제공
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyCOmparator)
studentStream.sorted(Comparator.comparing(Student::getBan)) // 반별로 정렬
.forEach(System.out::println);
추가 정렬 기준을 제공할 때는 thenComparing()을 사용
thenComparing(Comparator<T> other)
thenComparing(Function<T, U> keyExtractor)
thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
.thenComparing(Student::getTotalScore) // 총점별로 정렬
.thenComparing(Student::getName)) // 이름별로 정렬
.forEach(System.out::println);
스트림 요소 변환하기
map()
Stream<R> map(Function<? super T, ? extends R> mapper) //Stream<T> -> Stream<R>
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1")
, new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
Stream<String> filenameStream = fileStream.map(file::getName);
filenameStream.forEach(System.out::println); // 스트림의 모든 파일의 이름을 출력
예) 파일 스트림에서 파일 확장자(대문자)를 중복 없이 뽑아내기
filenameStream.map(File::getName) // Stream<FIle> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.map(s->s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복제거
.forEach(System.out::print); //JAVABAKTXT
기본형 스트림으로 변환할 때는 mapToInt(), mapToLong(), mapToDouble()을 사용한다.
스트림 요소를 소비하지 않고 엿보기
peek()
Stream<T> peek(Consumer<? super T> action) // 중간 연산(스트림을 소비x)
void forEach(Consumer<? super T> action) // 최종 연산(스트림을 소비o)
filenameStream.map(File::getName) // Stream<FIle> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.peek(s->System.out.printf("filename=%s%n"), s) //파일명을 추출
.map(s->s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.peek(s->System.out.printf("filename=%s%n"), s) // 확장자를 추출
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복제거
.forEach(System.out::print); //JAVABAKTXT
스트림의 스트림을 스트림으로 변환
flatMap()
Stream<String[]> strArrStrm = Stream.of(new String[]{"abc", "def", "ghi"},
new String[]{"ABC", "GHI", "JKLMN"});
Stream<Stream<String>> strStrStrm = strARrStrm.map(Arrays::stream);

의도는 아래 그림처럼 되는 것일 때

Stream<String> strStrStrm = strArrStrm.flatMap(Arrays::stream); // Arrays.stream(T[])
Optional<T>
T 타입 객체의 래퍼클래스
public final class Optional<T> {
private final T value; // T타입의 참조변수
...
}
어떤 타입이든지 다 저장할 수 있다.
Optional은 아래 두 문제를 해결할 수 있다.
- null을 직접 다루는 것은 위험하다. NullPointException
- if문을 통해 null을 체크해야 해서 코드가 지저분해진다.
Optinal<T>객체 생성
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null); // NullPointException 발생
Optional<String> optVal = Optional.of(str); // OK
null이 될 수 있는 값은 빈 Optional<T>객체를 사용하자
Optional<String> optVal = null; // 널로 초기화. 바람직하지 않음
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화. <String> 생략가능
NullPointException을 최소화하자
Optional<T> 객체의 값 가져오기
get(), orElse(), orElseGet(), orElseThrow()
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일 때는, ""를 반환
String str3 = optVal.orElseGet(String::new); // 람다식 사용가능 ()->new String()
String str4 = optVal.orElseThrow(NullPointException::new); // null이면 예외발생
orElse(), orElseGet()을 주로 사용
isPresent()는 Optional객체의 값이 null이면 false, 아니면 true를 반환한다.

OptionalInt, OptionalLong, OptionalDouble
기본형 값을 감싸는 래퍼클래스
값 가져오기

빈 Optional객체와의 비교
OptionalInt opt - OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt - OptionalInt.empty(); // OptionalInt에 0을 저장
첫 번째 코드는 0을 저장했고, 두 번째 코드는 아무것도 저장하지 않았다. 이 둘을 구별하기 위해 isPresent가 있다.

최종 연산

forEach(), forEachOrdered()
스트림의 모든 요소에 지정된 작업을 수행
void forEach(Consumer<? super T> action) // 병렬스트림인 경우 순서가 보장되지 않음
void forEachOrdered(Consumer<? super T> action) // 병렬스트림인 경우에도 순서가 보장됨


조건 검사
allMatch(), anyMatch(), noneMatch()
boolean allMatch (Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키면 true
boolean anyMatch (Predicate<? super T> predicate) // 한 요소라도 조건을 만족시키면 true
boolean noneMatch (Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키지 않으면 true

findFirst(), findAny()
조건에 일치하는 요소 찾기
Optional<T> findFirst() // 첫 번째 요소를 반환. 순차 스트림에 사용
Optional<T> findAny() // 아무거나 하나를 반환, 병렬 스트림에 사용

reduce()
스트림의 요소를 하나씩 줄여가며 누적연산 수행
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, Bifunction<U, T, U> accumulator, BinaryOperator<U> combiner)
첫 번째와 두 번째 메서드가 핵심이다.


collect()
Collector를 매개변수로 하는 스트림의 최종연산
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiCOnsumer combiner) // 잘 안쓰임
collect()는 그룹별로 리듀싱(reduce())를 하는 것이다.


스트림을 컬렉션, 배열로 변환
컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()
List<String> names = stuStream.map(Student::getName) // Stream<Student> -> Stream<String>
.collect(Collectors.toList()); // Stream<String> -> List<String>
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new)); // Stream<String> -> ArrayList<String>
Map<String, Person> map = personStream
.collect(Collectors.toMap(p -> p.getRegId(), p -> p)); // Stream<Person> -> Map<String,Person>
배열로 변환 - toArray()
Student[] stuNames = studentStream.toArray(Student{}::new); // OK
Student[] stuNames = studentStream.toArray(); // 에러. 자동 형변환이 안됨
Student[] stuNames = (Student[])studentStream.toArray(); // Ok
Object[] stuNames = studentStream.toArray(); // Ok
스트림의 통계
통계정보 제공 - counting(), summingInt(), maxBy(), minBy(), ...

스트림을 리듀싱
reducing()

joining()
문자열 스트림의 요소를 모두 연결

스트림의 그룹화와 분할
partitioningBy() - 스트림을 2분할
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)

groupingBy() - 스트림을 n분할(그룹화)
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)

스트림의 변환


'JAVA' 카테고리의 다른 글
[지네릭스] 열거형 (0) | 2023.03.15 |
---|---|
[지네릭스] 지네릭스 (0) | 2023.03.15 |
람다(Lambda) (0) | 2023.03.13 |
[java.math] BigInteger, BigDecimal (0) | 2023.03.09 |
[java.util] Objects, Random, 정규식 (0) | 2023.03.09 |
스트림
- 다양한 데이터 소스(컬렉션, 배열 등)를 표준화된 방법으로 다루기 위한 것으로, 데이터 소스를 추상화하고 데이터를 다루는데 자주 사용되는 메서드들을 정의해 놓았다.
- 데이터 소스를 추상화했다는 것은 데이터 소스가 무엇이든 간에 같은 방식으로 다룰 수 있게 되었다는 것과 코드의 재사용성이 높아졌다는 것이다.
String[] strARr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrays.asList(strARr);
스트림 생성
Stream<String> strStream1 = strList.stream(); // 스트림을 생성
Stream<String> strStream2 = Arrays.stream(strArr); // 스트림을 생성
데이터를 읽어서 정렬하고 출력
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
스트림을 사용하지 않을 시
Arrays.sort(strArr);
Collections.sort(strList);
for(String str : strArr)
System.out.println(str);
for(String str : strList)
System.out.println(str);
스트림을 사용하면 정렬하고 출력하는 방법이 완전히 동일해진다.
스트림이 제공하는 기능
중간 연산과 최종 연산
중간 연산 : 연산 결과가 스트림인 연산. 반복적으로 적용 가능
최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한 번만 가능

스트림의 특징
데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다.
List<Integer> list = Arrays.asList(3, 1, 5, 4, 2);
List<Integer> sortedList = list.stream().sorted() // list를 정렬해서
.collect(Collectors.toList()); // 새로운 List에 저장
System.out.println(list); //[3,1,5,4,2]
System.out.println(sortedList); //[1,2,3,4,5]
Iterator처럼 일회용이다.(필요하면 다시 스트림을 생성해야 함)
strStream.forEach(System.out::println); // 모든 요소를 화면에 출력(최종연산)
int numOfStr = strStream.count(); // 에러. 스트림이 이미 닫혔음
최종 연산 전까지 중간연산이 수행되지 않는다.(지연된 연산)
IntStream intStream = new Random().ints(1, 46); // 1~45범위의 무한 스트림
intStream.distinct().limit(6).sorted() // 중간 연산
.forEach(i-> System.out.println(i+",")); // 최종 연산
작업을 내부 반복으로 처리한다.


작업을 병렬로 처리(병렬 스트림)
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
int sum = strStream.parallel() // 병렬 스트림으로 전환(속성만 변경)
.mapToInt(s -> s.length()).sum(); // 모든 문자열의 길이의 합
기본형 스트림 (IntStream, LongStream, DoubleStream 등)
- 오토박싱 & 언박싱의 비효율이 제거됨(Stream<Integer> 대신 IntStream사용)
- 숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공
스트림 만들기
컬렉션
Collection인터페이스 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream()
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream(); // list를 데이터 소스로 하는 새로운 스트림 생성
배열
Stream<T> Stream.of(T... values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive) // 배열 일부만
Stream<String> stgrStream = Stream.of("a", "b", "c");
Stream<String> stgrStream = Stream.of(new String[]{"a","b","c"});
Stream<String> stgrStream = Arryas.stream(new String[]{"a","b","c"});
Stream<String> stgrStream = Arryas.stream(new String[]{"a","b","c"}, 0, 3);
기본형 배열로부터 스트림 생성하기
IntStream IntStream.of(int... values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)
임의의 수
난수를 요소로 갖는 스트림 생성
IntStream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력
IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환
지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드(Random클래스)

특정 범위의 정수
특정 범위의 정수를 요소로 갖는 스트림 생성(IntStream, LongStream)
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5); // 1,2,3,4,5
람다식
람다식을 소스로 하는 스트림 생성
// 이 둘은 무한스트림
static <T> Stream<T> iterate(T seed, UnaryOperator<t> f) // 이전 요소에 종속적
static <T> Stream<T> generate(Supplier<T> s) // 이전 요소에 독립적
iterate()는 이전 요소를 seed로 해서 다음 요소를 계산한다.
Stream<Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6, ...

generate()는 seed를 사용하지 않는다.
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(()->1); // 1, 1, 1, 1, ...
파일
Stream<Path> Files.list(Path dir) // Path는 파일 또는 디렉토리
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // BufferedReader클래스의 메서드
파일 내용을 라인 단위로 읽어서 스트림으로 생성
비어있는 스트림
Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다.
long count = emptyStream.count(); // count의 값은 0
두 스트림의 연결
static메서드인 concat()을 사용하여 두 스트림을 하나로 연결할 수 있다. 두 스트림은 같은 타입이어야 한다.
String[] str1 = {"123", "456", "789"};
String[] str2 = {"ABC", "abc", "DEF"};
String<String> strs1 = Stream.of(str1);
String<String> strs2 = Stream.of(str2);
String<String> strs3 = Stream.concat(strs1, strs2);
스트림의 연산
중간 연산
연산결과가 스트림인 연산이며 반복적으로 적용 가능하다.

스트림 자르기
skip(), limit()
Stream<T> skip(long n) // 앞에서부터 n개 건너뛰기
Stream<T> limit(long maxSize) // maxSize 이후의 요소는 잘라냄
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print); // 45578
스트림 요소 걸러내기
filter(), distinct()
Stream<T> filter(Predicate<? super T> predicate) // 조건에 맞지 않는 요소 제거
Stream<T> distinct() // 중복제거
IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 5, 5, 6);
intStream.distinct().forEach(System.out::print); // 123456
IntStream intStream = IntStream.rangeClosed(1, 10); //12345678910
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
intStream.filter(i->i%2!=0 && i%3!=0).forEach(System.out::print); // 246810
intStream.filter(i->i%2!=0).filter(i->i%3!=0).forEach(System.out::print); // 246810
스트림 정렬하기
sorted()
Stream<T> sorted() // 스트림 요소의 기본 정렬(Comparable)로 정렬
Stream<T> sorted(Comparator<? super T> comparator) // 지정된 Comparator로 정렬

Comparator의 comparing()으로 정렬 기준을 제공
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyCOmparator)
studentStream.sorted(Comparator.comparing(Student::getBan)) // 반별로 정렬
.forEach(System.out::println);
추가 정렬 기준을 제공할 때는 thenComparing()을 사용
thenComparing(Comparator<T> other)
thenComparing(Function<T, U> keyExtractor)
thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
.thenComparing(Student::getTotalScore) // 총점별로 정렬
.thenComparing(Student::getName)) // 이름별로 정렬
.forEach(System.out::println);
스트림 요소 변환하기
map()
Stream<R> map(Function<? super T, ? extends R> mapper) //Stream<T> -> Stream<R>
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1")
, new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
Stream<String> filenameStream = fileStream.map(file::getName);
filenameStream.forEach(System.out::println); // 스트림의 모든 파일의 이름을 출력
예) 파일 스트림에서 파일 확장자(대문자)를 중복 없이 뽑아내기
filenameStream.map(File::getName) // Stream<FIle> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.map(s->s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복제거
.forEach(System.out::print); //JAVABAKTXT
기본형 스트림으로 변환할 때는 mapToInt(), mapToLong(), mapToDouble()을 사용한다.
스트림 요소를 소비하지 않고 엿보기
peek()
Stream<T> peek(Consumer<? super T> action) // 중간 연산(스트림을 소비x)
void forEach(Consumer<? super T> action) // 최종 연산(스트림을 소비o)
filenameStream.map(File::getName) // Stream<FIle> -> Stream<String>
.filter(s->s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.peek(s->System.out.printf("filename=%s%n"), s) //파일명을 추출
.map(s->s.substring(s.indexOf('.')+1)) // Stream<String> -> Stream<String>
.peek(s->System.out.printf("filename=%s%n"), s) // 확장자를 추출
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복제거
.forEach(System.out::print); //JAVABAKTXT
스트림의 스트림을 스트림으로 변환
flatMap()
Stream<String[]> strArrStrm = Stream.of(new String[]{"abc", "def", "ghi"},
new String[]{"ABC", "GHI", "JKLMN"});
Stream<Stream<String>> strStrStrm = strARrStrm.map(Arrays::stream);

의도는 아래 그림처럼 되는 것일 때

Stream<String> strStrStrm = strArrStrm.flatMap(Arrays::stream); // Arrays.stream(T[])
Optional<T>
T 타입 객체의 래퍼클래스
public final class Optional<T> {
private final T value; // T타입의 참조변수
...
}
어떤 타입이든지 다 저장할 수 있다.
Optional은 아래 두 문제를 해결할 수 있다.
- null을 직접 다루는 것은 위험하다. NullPointException
- if문을 통해 null을 체크해야 해서 코드가 지저분해진다.
Optinal<T>객체 생성
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null); // NullPointException 발생
Optional<String> optVal = Optional.of(str); // OK
null이 될 수 있는 값은 빈 Optional<T>객체를 사용하자
Optional<String> optVal = null; // 널로 초기화. 바람직하지 않음
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화. <String> 생략가능
NullPointException을 최소화하자
Optional<T> 객체의 값 가져오기
get(), orElse(), orElseGet(), orElseThrow()
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일 때는, ""를 반환
String str3 = optVal.orElseGet(String::new); // 람다식 사용가능 ()->new String()
String str4 = optVal.orElseThrow(NullPointException::new); // null이면 예외발생
orElse(), orElseGet()을 주로 사용
isPresent()는 Optional객체의 값이 null이면 false, 아니면 true를 반환한다.

OptionalInt, OptionalLong, OptionalDouble
기본형 값을 감싸는 래퍼클래스
값 가져오기

빈 Optional객체와의 비교
OptionalInt opt - OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt - OptionalInt.empty(); // OptionalInt에 0을 저장
첫 번째 코드는 0을 저장했고, 두 번째 코드는 아무것도 저장하지 않았다. 이 둘을 구별하기 위해 isPresent가 있다.

최종 연산

forEach(), forEachOrdered()
스트림의 모든 요소에 지정된 작업을 수행
void forEach(Consumer<? super T> action) // 병렬스트림인 경우 순서가 보장되지 않음
void forEachOrdered(Consumer<? super T> action) // 병렬스트림인 경우에도 순서가 보장됨


조건 검사
allMatch(), anyMatch(), noneMatch()
boolean allMatch (Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키면 true
boolean anyMatch (Predicate<? super T> predicate) // 한 요소라도 조건을 만족시키면 true
boolean noneMatch (Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키지 않으면 true

findFirst(), findAny()
조건에 일치하는 요소 찾기
Optional<T> findFirst() // 첫 번째 요소를 반환. 순차 스트림에 사용
Optional<T> findAny() // 아무거나 하나를 반환, 병렬 스트림에 사용

reduce()
스트림의 요소를 하나씩 줄여가며 누적연산 수행
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, Bifunction<U, T, U> accumulator, BinaryOperator<U> combiner)
첫 번째와 두 번째 메서드가 핵심이다.


collect()
Collector를 매개변수로 하는 스트림의 최종연산
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiCOnsumer combiner) // 잘 안쓰임
collect()는 그룹별로 리듀싱(reduce())를 하는 것이다.


스트림을 컬렉션, 배열로 변환
컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()
List<String> names = stuStream.map(Student::getName) // Stream<Student> -> Stream<String>
.collect(Collectors.toList()); // Stream<String> -> List<String>
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new)); // Stream<String> -> ArrayList<String>
Map<String, Person> map = personStream
.collect(Collectors.toMap(p -> p.getRegId(), p -> p)); // Stream<Person> -> Map<String,Person>
배열로 변환 - toArray()
Student[] stuNames = studentStream.toArray(Student{}::new); // OK
Student[] stuNames = studentStream.toArray(); // 에러. 자동 형변환이 안됨
Student[] stuNames = (Student[])studentStream.toArray(); // Ok
Object[] stuNames = studentStream.toArray(); // Ok
스트림의 통계
통계정보 제공 - counting(), summingInt(), maxBy(), minBy(), ...

스트림을 리듀싱
reducing()

joining()
문자열 스트림의 요소를 모두 연결

스트림의 그룹화와 분할
partitioningBy() - 스트림을 2분할
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)

groupingBy() - 스트림을 n분할(그룹화)
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)

스트림의 변환


'JAVA' 카테고리의 다른 글
[지네릭스] 열거형 (0) | 2023.03.15 |
---|---|
[지네릭스] 지네릭스 (0) | 2023.03.15 |
람다(Lambda) (0) | 2023.03.13 |
[java.math] BigInteger, BigDecimal (0) | 2023.03.09 |
[java.util] Objects, Random, 정규식 (0) | 2023.03.09 |