Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 자바의 정석 기초편 ch9
- 2024 정보처리기사 수제비 실기
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch3
- 게시글 목록 api
- 자바의 정석 기초편 ch14
- @Aspect
- 스프링 mvc1 - 스프링 mvc
- 2024 정보처리기사 시나공 필기
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch5
- 자바의 정석 기초편 ch11
- 자바의 정석 기초편 ch13
- 스프링 db2 - 데이터 접근 기술
- 스프링 고급 - 스프링 aop
- 타임리프 - 기본기능
- 스프링 db1 - 스프링과 문제 해결
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch6
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch4
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch7
- jpa - 객체지향 쿼리 언어
- 스프링 입문(무료)
- 자바의 정석 기초편 ch8
- 코드로 시작하는 자바 첫걸음
Archives
- Today
- Total
나구리의 개발공부기록
자바의 정석 기초편 ch14 - 23 ~ 34[스트림의 연산들, 스트림의 중간연산] 본문
1) 스트림의 연산들
(1) 스트림의 중간연산 요약
중간연산 | 설명 | |
Stream<T> distinct() | 중복을 제거 | |
Stream<T> filter(Predicate<T> predicate) (조건식 입력) | 조건에 안 맞은 요소를 제외 | |
Stream<T> limit(long maxSize) | 스트림의 일부를 잘라냄 | |
Stream<T> skip(long n) | 스트림의 일부를 건너뜀 | |
Stream<T> peek (Consumer<T> action) (forEach 와 비슷) | 스트림의 요소에 작업수행 | |
Stream<T> sorted() (기본정렬) Stream<T> sorted(Comparator<T> comparator) (정렬기준을 입력) |
스트림의 요소를 정렬 | |
Stream<R> DoubleStream Int Stream LongStream |
map(Function<T,R> mapper) mapToDouble(ToDoubleFunction<T> mapper) mapToInt(ToIntFunction<T> mapper) mapToLong(ToLongFunction<T> mapper) flatMap(Function<T,Stream<R>> mapper) flatMapToDouble(Function<T, DoubleStream> m) flatMapToInt(Function<T,IntStream> m) flatMapToLong(Function<T, LongStream> m) |
스트림의 요소를 변환 (중간연산의 핵심) |
(2) 스트림의 최종연산 요약
최종연산 | 설명 |
void forEach(Consumer<? super T> action) void forEachOrdered(Consumer<? super T> action) (순서유지(병렬스트림)) |
각 요소에 지정된 작업 수행 |
long count() | 스트림의 요소의 개수 반환 |
Optional<T> max(Comparator<? super T> comparator) Optional<T> min(Comparator<? super T> comparator) |
스크림의 최대/최소값을 반환 |
Optional<T> findAny() (아무거나 하나(병렬)) OPtional<T> findFirst() (첫 번째 요소(직렬 - 병렬이 아닐 때)) (filter()와 같이 쓰임, Optional<T> = 래퍼클래스) |
스트림의 요소 하나를 반환 |
boolean allMatch(predicate<T> p) (모두 만족하는지) boolean anyMatch(predicate<T> p) (하나라도 만족하는지) boolean noneMatch(predicate<T> p) (모두 만족하지 않는지) |
주어진 조건을 모든 요소가 만족시키는지, 만족하지 않는지 확인 |
Object[] toArray() A[] toArray(IntFunction<A[]> generator) |
스트림의 모든 요소를 배열로 반환 |
Optional<T> reduce(BinaryOperator<T> accumulater) T reduce(T iidentity, BinaryOperator<T> accumulator) U reduce(U identity, Bifunction<U,T,U> accumulator, binaryOperator<U> combiner) |
스트림의 요소를 하나씩 줄여가면서(리듀싱) 계산 (최종연산의 핵심) |
R collect(Collector<T,A,R> collector) R collect(Supplier<R> supplier, Biconsumer<R,T> accumulater, BiConsumer<R,R> combiner) |
스트림의 요소를 수집 주로 요소를 그룹화, 분할한 결과를 컬렉션에 담아 반환하는데 사용 (최종연산의 핵심) |
2) 스트림의 중간연산 설명
(1) 스트림 자르기 - skip(), limit()
- skip(): 앞에서부터 인수의 개수만큼 건너뜀
- limit(): 인수의 길이까지만 반환
Stream<T> skip(long n) // 앞에서부터 n개 건너뛰기
Stream<T> limit(long maxSize) // maxSize 이후의 요소는 잘라냄
IntStream skip(long n)
IntStream limit(long maxSize)
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print); // 45678
(2) 스트림의 요소 걸러내기 - filter(), distinct()
- 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
// 2의 배수만 출력
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
// 필터의 연속사용(2의 배수가 아니고 3의 배수가 아닌것 출력)
intStream.filter(i->i%2!=0 && i%3!=0).forEach(System.out::print);
intStream.filter(i->i%2!=0).filter(i->i%3!=0).forEach(System.out::print);
(3) 스트림 정렬하기 - sorted()
- 다양한 정렬 기준을 사용할 수 있으며 대부분의 경우 메서드의 체인 메서드로.reversed()를 호출하면 역순정렬이 됨
- comparing()메서드로 정렬기준을 입력할 수 있음
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
// 기본 정렬 사용
String result1 = strStream.sorted().collect(Collectors.joining());
System.out.println(result1); // CCaaaabccdd
// 기본 정렬 (명시적으로 Comparator.naturalOrder() 사용)
result1 = strStream.sorted(Comparator.naturalOrder()).collect(Collectors.joining());
System.out.println(result1); // CCaaaabccdd
// 람다식으로 정렬 기준을 입력
result1 = strStream.sorted((s1, s2) -> s1.compareTo(s2)).collect(Collectors.joining());
System.out.println(result1); // CCaaaabccdd
// 메서드 참조로 기본 정렬을 사용
result1 = strStream.sorted(String::compareTo).collect(Collectors.joining());
System.out.println(result1); // CCaaaabccdd
// Comparator의 reverseOrder()를 사용하여 역순 정렬
result1 = strStream.sorted(Comparator.reverseOrder()).collect(Collectors.joining());
System.out.println(result1); // ddccbaaaCС
// 위 문장과 동일함
result1 = strStream.sorted(Comparator.<String>naturalOrder().reversed()).collect(Collectors.joining());
System.out.println(result1); // ddccbaaaCС
/**
* String 클래스 내부에는 이미 Comparator를 만들어서 static 멤버로 가지고 있음
* 즉, String클래스의 정렬 기준도 호출하여 사용할 수 있음
*/
static Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
// String클래스의 CASE_INSENSITIVE_ORDER 정렬기준을 사용 - 대소문자 구분한하는 기본정렬
result1 = strStream.sorted(String.CASE_INSENSITIVE_ORDER).collect(Collectors.joining());
System.out.println(result1); // aaabCCccdd
// 위 기능의 역순정렬 즉, 대소문자 구분안함(동일 순서일때는 대문자가 먼저 출력됨) + 역순
result1 = strStream.sorted(String.CASE_INSENSITIVE_ORDER.reversed()).collect(Collectors.joining());
System.out.println(result1); // ddCCccbaaa
// 길이 순 정렬 (메서드 참조로 인수의 길이를 입력)
result1 = strStream.sorted(Comparator.comparing(String::length)).collect(Collectors.joining());
System.out.println(result1); // bddCCccaaa
// 길이 순 정렬 (메서드 참조 대신 람다식 사용)
result1 = strStream.sorted(Comparator.comparingInt(s -> s.length())).collect(Collectors.joining());
System.out.println(result1); // bddCCccaaa
// 길이 순 정렬의 역순
result1 = strStream.sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.joining());
System.out.println(result1); // aaaddCCccb
(4) 정렬 대상, 정렬 기준을 제공 - comparing(), thenComparing()
- sorted()이 인수로 Comparator의 comparing()메서드를 사용하여 정렬 대상과 정렬기준을 입력할 수 있음
- 추가 정렬 기준을 제공하고자 할 경우 .thenComparing()을 사용하여 인수에 정렬기준을 추가로 입력할 수 있음
Stream<T> sorted() // 스트림 요소의 기본 정렬(Comparable)로 정렬
Stream<T> sorted(Comparator<? super T> comparator) //지정된 Comparator로 정렬
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)
// 정렬 기준이 3개, thenComparing으로 정렬기순을 추가로 제공
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
.thenComparing(Student::getTotalScore) // 총점별로 정렬
.thenComparing(Student::getName) // 이름별로 정렬
.forEach(Sysetm.out::println);
(5) 예제1
- 스트림의 요소 중 반을 기준으로 기본 정렬을 수행후 출력
import java.util.*;
import java.util.stream.*;
class Ex14_5 {
public static void main(String[] args) {
Stream<Student> studentStream = Stream.of(
new Student("이자바", 3, 300),
new Student("김자바", 1, 200),
new Student("안자바", 2, 100),
new Student("박자바", 2, 150),
new Student("소자바", 1, 200),
new Student("나자바", 3, 290),
new Student("감자바", 3, 180)
);
// 역순 정렬을 하고 싶은 곳에는 .reverse()를 중간에 넣으면 됨
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별 정렬
// 위 코드를 람다식으로 변경
// studentStream.sorted(Comparator.comparing((Student s) -> s.getBan())
.thenComparing(Comparator.naturalOrder())) // 기본 정렬
.forEach(System.out::println);
}
}
class Student implements Comparable<Student> {
String name;
int ban;
int totalScore;
Student(String name, int ban, int totalScore) {
this.name =name;
this.ban =ban;
this.totalScore =totalScore;
}
public String toString() {
return String.format("[%s, %d, %d]", name, ban, totalScore);
}
String getName() { return name;}
int getBan() { return ban;}
int getTotalScore() { return totalScore;}
// 총점 내림차순을 기본 정렬로 한다.
public int compareTo(Student s) {
return s.totalScore - this.totalScore;
}
}
/*
출력결과
[김자바, 1, 200]
[소자바, 1, 200]
[박자바, 2, 150]
[안자바, 2, 100]
[이자바, 3, 300]
[나자바, 3, 290]
[감자바, 3, 180]
*/
(6) 스트림의 요소를 변환 - map()
- map()메서드로 Stream에 저장된 요소의 타입을 변환함
- 인수로 매퍼로 동작할 코드를 입력
// 사용 예시
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); // 스트림의 모든 파일의 이름을 출력
// 파일 스트림에서 파일 확장자를 대문자로 중복을 제거하여 뽑아내기
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.indexOf('.') + 1)) // Stream<String> -> Stream<String>
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복 제거
.forEach(System.out::print); // JAVABAKTXT
}
(7) peek()
- 스트림의 요소를 소비하지 않고 확인할 수 있음
- 중간 작업결과를 확인하는 용도 즉, 디버깅을 목적으로 사용함
Stream<T> peek(Consumer<? super T> action) // 중간 연산(스트림을 소비X)
void forEach(Consumer<? super T> action) // 최종 연산(스트림을 소비O)
fileStream.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)) // 확장자만 추출
.peek(s->System.out.printf("extension=%s%n", s)) // 확장자를 출력
.forEach(System.out::println); // 최종연산 스트림을 소비
(8) 예제
- 파일명.확장자 -> 확장자만 추출(중복제거)
import java.io.*;
import java.util.stream.*;
class Ex14_6 {
public static void main(String[] args) {
File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"),
new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
};
Stream<File> fileStream = Stream.of(fileArr);
// map()으로 Stream<File>을 Stream<String>으로 변환
// Stream<String> filenameStream = fileStream.map(File::getName);
Stream<String> filenameStream = fileStream.map((f) -> f.getName());// 람다식으로 변환
filenameStream.forEach(System.out::println); // 모든 파일의 이름을 출력
fileStream = Stream.of(fileArr); // 스트림을 다시 생성
fileStream.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)) // 확장자만 추출
.map(String::toUpperCase) // 모두 대문자로 변환
.distinct() // 중복 제거
.forEach(System.out::println); // JAVABAKTXT
System.out.println();
}
}
(9) 스트림의 스트림을 스트림(하나의 스트림)으로 변환 - flatMap()
- 스트림 배열을 스트림의 스트림(2차원 스트림)이 아닌 하나의 스트림으로 변환하여 1차원 배열처럼 사용할 수 있도록 변환
- 스트림의 스트림은 내부의 스트림의 요소에 직접 접근이 불가능한데 flatMap()으로 변환하면 각 스트림의 요소에 직접 접근할 수 있음
// 스트링 배열로 스트림을 생성하여 각 요소에 스트링배열을 생성
Stream<String[]> strArrStrm = Stream.of(new String[]{"abc", "def", "ghi" },
new String[]{"ABC", "GHI", "JKLMN"});
// 스트림배열을 스트림의스트림으로(2차원 배열구조) 전환 - map
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);
// 스트림배열을 스트림으로 전환 - flatMap
Stream<String> strStrStrm = strArrStrm.flatMap(Arrays::stream); // Arrays.stream(T[])
(10) 예제
- 스트림의 스트림을 스트림으로 변환 후 요소 중복제거, 기본정렬, 소문자변환하여 출력
- 스트링 배열의 요소를 스트림으로 변환 후 하나이상의 공백(정규식으로 표현)으로 분할하여 중복제거, 소문자변환, 기본정렬하여 출력
import java.util.*;
import java.util.stream.*;
class Ex14_7 {
public static void main(String[] args) {
Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc", "def", "jkl"},
new String[]{"ABC", "GHI", "JKL"}
);
// 스트림의 스트림으로 변경하면 직접 접근이 불가능
// Stream<Stream<String>> strStrmStrm = strArrStrm.map(Arrays::stream);
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream); // 스트림으로 변환
strStrm.map(String::toLowerCase) // 스트림요소를 소문자로 모두 변환
.distinct() // 중복제거
.sorted() //정렬
.forEach(System.out::println);
System.out.println();
// 문장의 단어를 스트림의 요소로 변환
String[] lineArr = {
"Believe or not It is true",
"Do or do not There is no try",
};
Stream<String> lineStream = Arrays.stream(lineArr);
// " +" -> 하나이상의 공백(정규식 중 하나)
// 문자열 -> 문자열 배열로 변환
lineStream.flatMap(line -> Stream.of(line.split(" +")))
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
}
}
** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브