JAVA8 스트림 예제로 익숙해지기
- Coding/Java
- 2020. 11. 28.
반응형
728x90
반응형
소스코드 : https://github.com/westssun/moderninjava8/tree/master/src/main/java/ModernInJava8/ch4_5_6_stream
예제 실행을 위한 DTO
@Data
public class SampleDto {
private int idx;
private String name;
private String gender;
}
1) 스트림 기본
/**
* 기존 Java7 코드와 Java8 코드를 비교해보자.
*/
public class BasicStream {
public static void main(String[] args) {
java7Code();
java8Code();
}
/**
* java7
*/
static void java7Code() {
/* java7 */
List<SampleDto> list = new ArrayList<>();
List<SampleDto> sampleDtoList = new ArrayList<>();
for (SampleDto sampleDto : sampleDtoList) {
if (sampleDto.getIdx() < 10) {
list.add(sampleDto);
}
}
/* 익명클래스 */
Collections.sort(list, new Comparator<SampleDto>() {
@Override
public int compare(SampleDto o1, SampleDto o2) {
return Integer.compare(o1.getIdx(), o2.getIdx());
}
});
/* 정렬된 리스트 중 sampleDto의 getName 셋팅 */
List<String> stringList = new ArrayList<>();
for (SampleDto sampleDto : list) {
stringList.add(sampleDto.getName());
}
}
/**
* java8
*/
static void java8Code() {
/* java8 */
List<SampleDto> sampleDtoList = new ArrayList<>();
List<String> list = sampleDtoList.stream()
/** 람다를 인수로 받아, 스트림에서 특정 요소를 제외시킨다. 아래는 idx가 10 이상인 데이터를 선택한다. */
.filter(d -> d.getIdx() < 10) // idx가 10보다 작은 데이터 선택
.sorted(Comparator.comparing(SampleDto::getIdx)) // idx 순서로 정렬
/** 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출한다. */
.map(SampleDto::getName) // 이름 추출
//.limit(3) // 선착순 3개만 선택
.collect(Collectors.toList()); // 리스트로 저장
/* 병렬 */
List<String> parallelList = sampleDtoList.parallelStream()
.filter(d -> d.getIdx() < 10) // idx가 10보다 작은 데이터 선택
.sorted(Comparator.comparing(SampleDto::getIdx)) // idx 순서로 정렬
.map(SampleDto::getName) // 이름 추출
.collect(Collectors.toList()); // 리스트로 저장
}
/**
* 스트림은 단 한번만 소비 가능하다.
*/
static void impossibleNewStream() {
List<String> title = Arrays.asList("A", "B", "C");
Stream<String> s = title.stream();
s.forEach(System.out::println); // A, B, C 출력
/** java.lang.IllegalStateException:스트림이 이미 소비되었거나 닫힘 에러 발생 */
s.forEach(System.out::println);
}
}
2) Stream filter
public class StreamFilter {
public static void main(String[] args) {
}
/**
* 중복 필터링
*/
static void distinct() {
/* 내부반복 */
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct() /* 중복 필터링 */
.forEach(System.out::println);
}
}
3) Stream 외부반복/내부반복
/**
* 외부반복/내부반복
외부반복 : 사용자가 직접 요소를 반복 (for-each 등)
내부반복 : 스트림 라이브러리 -반복을 알아서 처리하고 결과 스트림값을 어딘가에 저장해준다
*/
public class StreamIteration {
public static void main(String[] args) {
forEachCode();
}
/**
* 내부반복/외부반복 예제
*/
static void forEachCode() {
/* 내부반복 */
List<String> names = new ArrayList<>();
List<SampleDto> sampleDtoList = new ArrayList<>();
for (SampleDto sampleDto : sampleDtoList) {
names.add(sampleDto.getName());
}
/* 외부반복 */
List<String> streamNames = sampleDtoList.stream()
.map(SampleDto::getName)
.collect(Collectors.toList());
}
}
4) Stream Mapping
public class StreamMapping {
public static void main(String[] args) {
}
/**
* Map
*/
static void useMap() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 1) Map
* 함수를 인수로 받는 메서드
* 인수로 제공된 함수는 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.
* (map은 '고친다'라는 의미 보다는 '새로운 버전을 만든다' 라는 개념에 가까우므로 매핑이라는 단어를 사용한다)
*/
List<Integer> list = sampleDtoList.stream()
.map(SampleDto::getIdx) /* idx만의 새로운 버전 생성 */
.collect(Collectors.toList());
/**
* idx(Integer -> Long)
* map 메서드의 연결
*/
List<Long> list2 = sampleDtoList.stream()
.map(SampleDto::getIdx) /* idx만의 새로운 버전 생성 */
.map(Integer::longValue)
.collect(Collectors.toList());
}
}
5) Stream Match
public class StreamMatch {
public static void main(String[] args) {
}
/**
* anyMatch
*/
static void anyMatch() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 프레디케이트가 주어진 스트림에서 적어도 한 요소와 일치하는지 확인
*/
boolean isChekced = sampleDtoList.stream()
.anyMatch(dto -> dto.getIdx() > 10);
}
/**
* allMatch
*/
static void allMatch() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 모든 요소가 주어진 프레디케이트와 일치하는지 확인
*/
boolean isChekced = sampleDtoList.stream()
.allMatch(dto -> dto.getIdx() > 10);
}
/**
* noneMatch
*/
static void noneMatch() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 모든 요소가 프레디케이트와 일치하지않는지 확인
*/
boolean isChekced = sampleDtoList.stream()
.noneMatch(dto -> dto.getIdx() > 10);
}
/**
* findFirst
*/
static void useFindFirst() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
/**
* 첫번째 값을 반환한다
*/
Optional<Integer> first = numbers.stream()
.map(n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst();
}
}
6) 숫자스트림
public class StreamNumberic {
public static void main(String[] args) {
}
static void basic() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 문제점. 아래 코드에는 박싱 비용이 숨어있다. Integer->int로 언박싱해야한다.
*/
int idxSum = sampleDtoList.stream()
.map(SampleDto::getIdx)
.reduce(0, Integer::sum);
/**
* 아래처럼 sum 메소드를 호출할 수 있다면, 위 언박싱 비용이 없어진다.
* 하지만 .map을 호출한 이후의 결과물은 Stream<T>를 생성하기 때문에 sum 메서드를 호출할 수 없다.
* 이를 해결하기 위해 '기본형 특화 스트림'을 제공한다.
*/
/*
int idxSum2 = sampleDtoList.stream()
.map(SampleDto::getIdx)
.sum();
*/
/**
* 숫자 스트림으로 매핑하기
*/
int idxSum3 = sampleDtoList.stream()
.mapToInt(SampleDto::getIdx) /* 결과를 int type */
.sum(); /* 만약 스트림이 비어있다면 0 (기본값)을 반환 */
}
}
7) 스트림 연산
public class StreamOperation {
public static void main(String[] args) {
operation();
}
/**
* 스트림 연산
*/
static void operation() {
/* java8 */
List<SampleDto> sampleDtoList = new ArrayList<>();
List<String> list = sampleDtoList.stream()
/** 중간연산 시작 ----------------------- */
.filter(d -> d.getIdx() < 10) // idx가 10보다 작은 데이터 선택
.sorted(Comparator.comparing(SampleDto::getIdx)) // idx 순서로 정렬
.map(SampleDto::getName) // 이름 추출
.limit(3) // 선착순 3개만 선택
/** 최종연산 시작 ----------------------- */
.collect(Collectors.toList()); // 리스트로 저장
}
/**
* 스트림 중간연산에 코드 넣기
*/
static void opretion2() {
/* java8 */
List<SampleDto> sampleDtoList = new ArrayList<>();
List<String> list = sampleDtoList.stream()
/** 중간연산 시작 ----------------------- */
.filter(d -> {
System.out.println(d.getName());
return d.getIdx() < 10; // return 변경
})
.sorted(Comparator.comparing(SampleDto::getIdx))
.map(d -> {
System.out.println(d.getName());
return d.getName(); // return 변경
})
.limit(3)
/** 최종연산 시작 ----------------------- */
.collect(Collectors.toList());
}
}
8) Stream Reduce
public class StreamReduce {
public static void main(String[] args) {
}
/**
* reduce 연산
*/
static void useReduce() {
/* before */
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
int sum = 0;
for (int x : numbers) {
sum += x;
}
/* java 8 */
/**
* reduce
* identity 의미 = 초기값 : 0
*/
// int sum2 = numbers.stream().reduce(0, (a, b) -> a + b);
int sum2 = numbers.stream().reduce(0, Integer::sum);
/**
* 만약 초기값을 설정하지 않는다면? Optional 로 받자.
* 스트림에 아무 요소가 없을 경우 Optional 객체로 감싼 객체를 반환한다
*/
Optional<Integer> sum3 = numbers.stream().reduce(Integer::sum);
/**
* max, min
*/
int getMax = numbers.stream().reduce(0, Integer::max);
int getMin = numbers.stream().reduce(0, Integer::min);
}
}
9) Stream Slice
public class StreamSlice {
public static void main(String[] args) {
}
/**
* filter
*/
static void useFilter() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* 1) filter
* filter 메서드를 사용하면 전체 스트림을 반복하면서 각 요소에 프레디케이틑 적용하게된다.
* 만약 idx가 정렬된 리스트일 경우, idx가 10보다 큰 경우부터는 반복 작업을 중단할 수 있다.
* 이런 경우에는 useTakeWile을 사용해보자. 아래 예제를 보자.
*/
List<SampleDto> list = sampleDtoList.stream()
.filter(dto -> dto.getIdx() < 10)
.collect(Collectors.toList());
}
/**
* takeWhile
*/
static void useTakeWile() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* filter 메서드와는 다르게, 조건문을 만족하지 않은 경우 반복 작업을 중단한다.
*/
List<SampleDto> list = sampleDtoList.stream()
.takeWhile(dto -> dto.getIdx() < 10)
.collect(Collectors.toList());
}
/**
* dropWhile
*/
static void useDropWhile() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* takeWhile과 정반대의 작업 수행
* 프레디케이트가 처음으로 거짓이 되는 지점까지 발견된 요소를 제거한다.
* -> idx가 10보다 큰 요소를 탐색한다.
*/
List<SampleDto> list = sampleDtoList.stream()
.dropWhile(dto -> dto.getIdx() < 10)
.collect(Collectors.toList());
}
/**
* limit
*/
static void useLimit() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* limit
*/
List<SampleDto> list = sampleDtoList.stream()
.filter(dto -> dto.getIdx() < 10)
.limit(3) /* 최대 요소 3개 반환 */
.collect(Collectors.toList());
}
/**
* skip
*/
static void useSkip() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* skip
*/
List<SampleDto> list = sampleDtoList.stream()
.filter(dto -> dto.getIdx() < 10)
.skip(2) /* 처음 두개의 idx를 건너뛴 후 나머지 idx를 반환한다 */
.collect(Collectors.toList());
}
}
10) Stream flatMap
public class StreamSplitMap {
public static void main(String[] args) {
}
/**
* split map
*/
static void splitMap() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "AAAAAAAA", "AA"),
new SampleDto(2, "BBBBBBBB", "BB")
);
/**
* 결과타입 : List<String[]>
* split("") 실행 이후 우리가 기대한 결과 타입은 Stream<String>이다.
*/
List<String[]> list = sampleDtoList.stream()
.map(dto -> dto.getName().split(""))
.distinct()
.collect(Collectors.toList());
/**
* 결과타입 : List<Stream<String>>
* 이마저도 우리가 기대한 Stream<String> 결과타입이 아니다.
* 문제 해결을 위해, 먼저 각 단어를 개별 문자열로 이루어진 배열로 만든 다음에 각 배열을 별도의 스트림으로 만들어야한다.
*/
String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamofWords = Arrays.stream(arrayOfWords);
/* 위 Arrays.stream 메서드 호출 추가 */
List<Stream<String>> list2 = sampleDtoList.stream()
.map(dto -> dto.getName().split("")) /* 각 단어를 개별 문자열 배열로 변환 */
.map(Arrays::stream) /* 각 배열을 별도의 스트림으로 생성 */
.distinct()
.collect(Collectors.toList());
}
/**
* 위 이슈 해결: flatMap 사용
*/
static void userFlatmap() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "AAAAAAAA", "AA"),
new SampleDto(2, "BBBBBBBB", "BB")
);
/* 위 Arrays.stream 메서드 호출 추가 */
List<String> list3 = sampleDtoList.stream()
.map(dto -> dto.getName().split("")) /* 각 단어를 개별 문자열 배열로 변환 */
.flatMap(Arrays::stream) /* 생성된 스트림을 하나의 스트림으로 평면화 */
.distinct()
.collect(Collectors.toList());
}
}
11) IntStream
public class UseIntStream {
public static void main(String[] args) {
}
static void intStream() {
/* 내부반복 */
List<SampleDto> sampleDtoList = Arrays.asList(
new SampleDto(1, "A", "AA"),
new SampleDto(2, "B", "BB")
);
/**
* IntStream (기본형의 정수값만 만들 수 있는 숫자 스트림)
*/
IntStream intStream = sampleDtoList.stream()
.mapToInt(SampleDto::getIdx);
Stream<Integer> integerStream = intStream.boxed(); /* 숫자 스트림을 스트림으로 변환 */
/**
* IntStream에서 0이라는 기본값이 나온다면, 혹시나 잘못된 결과를 도출할 수 있다.
* 스트림에 요소가 없는 상황과 실제 최대값이 0인 경우를 구별해내야한다.
* 이를 구분하기위해 OptionalInt 스트림을 사용하자.
*/
OptionalInt maxIdx = sampleDtoList.stream()
.mapToInt(SampleDto::getIdx)
.max();
/* orElse : 값이 없을 경우 기본 최대값을 명시적으로 설정 */
int orElseMaxIdx = maxIdx.orElse(1);
}
}
12) 스트림 생성
public class newStream {
public static void main(String[] args) {
}
static void newStream() {
/**
* 스트림 생성
*/
Stream<String> stream = Stream.of("A", "b", "C", "d");
stream.map(String::toUpperCase)
.forEach(System.out::println);
}
}
13) 무한스트림
public class InfiniteStream {
public static void main(String[] args) {
}
/**
* iterate
*/
static void useIterate() {
/**
* 무한 스트림 생성
* 종료시점이 명시되어야한다. -> takeWhile 사용
*/
IntStream.iterate(0, n -> n + 2)
.takeWhile(n -> n < 100)
.forEach(System.out::println);
}
/**
* generate
*/
static void userGeneriate() {
/**
* 무한 스트림 생성
* iterate와 다른점 : 생산된 각 값을 연속적으로 계산하지 않는다.
* Supplier<T>를 인수로 받아서 새로운 값을 생성한다.
* 종료시점 명시 -> limit
*/
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
}
}
참고 교재 : 모던 인 자바8
반응형
'Coding > Java' 카테고리의 다른 글
자바에서 call by value, call by reference 이해하기 (0) | 2021.02.06 |
---|---|
함수형 인터페이스 기본 (Consumer, Function, Predicate, Supplier, Operator) (0) | 2020.12.09 |
자바 List의 null 체크 (with isEmpty()) (0) | 2020.11.07 |
java8에서의 날짜/시간 API (LcalDate/LocalTime) (0) | 2020.11.06 |
자바8의 default 메서드 등장 (0) | 2020.11.06 |