컬렉션(배열)의 저장 요소들을 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.
내부 반복자를 사용해서 병렬 처리와 중간 처리, 최종 처리 작업을 수행할 수 있다.
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable { public interface Stream <> extends BaseStream<T, Stream<> { } public interface IntStream extends BaseStream<Integer, IntStream> { } public interface LongStream extends BaseStream<Long, LongStream> { } public interface DoubleStream extends BaseStream<Double, DoubleStream> { }
// 숫자 범위로 스트림을 생성하는 방법 public void method1() { int sum = 0; IntStream stream = null; // 첫 번째 매개값 ~ 두 번째 매개값 이전까지의 값을 요소로 가지는 스트림 객체 생성 // stream = IntStream.range(1,10); // 첫 번째 매개값 ~ 두 번째 매개값까지의 값을 요소로 가지는 스트림 객체 생성 stream = IntStream.rangeClosed(1,10); // sum = stream.sum(); //sum = stream.peek( value -> System.out.println(value)).sum(); sum = stream.peek(System.out::println).sum(); System.out.printf("sum = %d\n", sum); System.out.println(); }
- range(1,10) : 1~9 까지의 원소와 그 원소의 합인 45 출력
- rangeCloser(1,10) : 1~10 까지의 원소와 그 원소의 합인 55 출력
Arrays.stream(배열) 메소드를 이용해서 배열로부터 스트림을 생성
// 배열로부터 스트림을 생성하는 방법 public void method2() { String[] names = {"홍길동", "이몽룡", "성춘향", "임꺽정", "성춘향"}; // for 문을 사용하여 출력 for(String name : names) { System.out.println(name + " "); } System.out.println(); // 스트림을 사용하여 출력 // Stream<String> stream = Arrays.stream(names); Stream<String> stream = Arrays.<String>stream(names); //stream.forEach(str -> System.out.print(str + " ")); // 중복제거 //stream.distinct().forEach(str -> System.out.print(str + " ")); stream.parallel().forEach(str -> System.out.print(str + " ")); System.out.println(); }
- for문 사용하여 출력하는 것과 동일한 결과
- Stream을 사용하여 훨씬 더 간결하게 코드 작성 가능하다.
public void method3() { List<String> names = Arrays.<String>asList("홍길동", "이몽룡", "성춘향", "임꺽정", "성춘향"); // for문을 사용하여 출력 for (int i = 0; i < names.size(); i++) { System.out.print(names.get(i) + " "); } System.out.println(); // Stream을 사용하여 출력 Stream<String> stream = names.stream(); //stream.forEach( name-> System.out.print(name + " ")); stream.forEach(System.out::println); System.out.println(); } }
스트림은 데이터의 필터링, 정렬, 매핑 등의 처리를 할 수 있는 중간 처리 메소드를 제공한다.
리턴 타입이 스트림이라면 중간 처리 메소드이다.
//필터 메소드 테스트 public void method2() { List<Student> students = Arrays.asList( new Student("홍길동",24,"남자",80,50), new Student("김철수",29,"남자",50,50), new Student("김영희",27,"여자",90,90), new Student("홍길동",29,"남자",80,50), new Student("이몽룡",26,"남자",88,80) ); // 성별이 여자인 학생만 출력 students.stream() .distinct().filter((s -> s.getGender().equals("여자"))).forEach(System.out::println); // 수학 영어 점수가 둘다 60점 이상인 학생만 출력 students.stream() .distinct() .filter((s -> s.getMath() >=60 && s.getEnglish() >= 60 )) .forEach(System.out::println); System.out.println(); }
// 요소가 기본 자료형일 때 public void method1() { // 오름차순 정렬 IntStream.of(3, 5, 1, 4, 2) .sorted() .forEach(value -> System.out.printf(value + " ")); System.out.println(); // 내림차순 정렬 IntStream.of(3, 5, 1, 4, 2) .boxed() .sorted(Comparator.reverseOrder()) // .mapToInt(Integer::intValue) .forEach(value -> System.out.printf(value + " ")); }
- Java의 스트림 API에는 기본형 스트림과 객체 스트림이 존재한다.
- 내림차순으로 종렬할 때는 기본형 스트림(IntStream)에서 sorted(Comparator) 같은 메소드를 사용 불가능하다.
- 따라서 기본형을 래퍼 클래스(Integer)로 변환해야 하는데, 이를 boxed()를 사용하여 구현한다.
2) 요소가 기본 객체형
// 요소가 객체일 때 public void method2() { List<Student> students = Arrays.asList( new Student("홍길동",24,"남자",80,50), new Student("김철수",29,"남자",50,50), new Student("김영희",27,"여자",90,90), new Student("홍길동",29,"남자",80,50), new Student("이몽룡",26,"남자",88,80) ); // 오름차순 정렬 students.stream() .distinct() .sorted((o1,o2) -> o1.getName().compareTo(o2.getName())) .forEach(System.out::println); System.out.println(); // 내림차순 정렬 students.stream() .distinct() //.sorted(COmparator.reverseOrder) //.sorted((o1,o2) -> o2.compareTo(o1)) .sorted((o1,o2) -> o2.getName().compareTo(o1.getName())) .forEach(System.out::println); System.out.println(); }
- Comparable을 활용하여 원하는 객체를 정렬 가능하다.
public void method2() { int[] iNumber = {1, 2, 3, 4, 5}; double[] dNumber = {1.1, 2.2, 3.3, 4.4, 5.5}; Arrays.stream(iNumber) .asLongStream() .forEach(System.out::println); // 최종 처리 메소드 System.out.println(); // 중간 처리 메소드는 최종 처리 메소드가 호출되어야 동작된다. double sum = Arrays.stream(dNumber) // 내림차순 정렬을 사용하기 위해 객체 스트림로 변경 .boxed() .sorted(Comparator.reverseOrder()) // 객체 스트림은 sum 같은 함수를 지원하지 않는다 // 따라서 기본형 스트림으로 다시 변경 //.mapToDouble(number ->number) //.mapToDouble(number -> number.doubleValue()) .mapToDouble(Double::doubleValue) .peek(System.out::println) // 중간 처리 메소드 .sum(); System.out.println(sum); System.out.println(); }
+) 매핑
최종 처리 단계에서 요소들이 특정 조건에 만족하는지 조사하는 역할을 한다.
allMatch(Predicate)
모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지 조사한다.
anyMatch(Predicate)
최소한 한 개의 요소가 매개값으로 주어진 Predicate의 조건을 만족하는지 조사한다.
noneMatch(Predicate)
모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하지 않는지 조사한다.
1) 기본 집계
최종 처리 기능으로 요소들의 개수, 합계, 평균값, 최대값, 최소값 등과 같이 하나의 값으로 산출하는 역할을 한다.
- count()
- sum()
- average()
- max()
- min()
- findFirst()
public void method1() { int[] numbers = {1, 2, 3, 4, 5, 6}; long count = Arrays.stream(numbers) .filter(number -> number % 2 == 0).count(); System.out.printf("2의 배수의 개수 : %d\n", count); System.out.println(); int sum = Arrays.stream(numbers) .filter(number -> number % 2 == 0).sum(); System.out.printf("2의 배수의 합 : %d\n", sum); System.out.println(); OptionalDouble optionalDouble = Arrays.stream(numbers) .filter(number -> number % 2 == 0).average(); }
2) 커스텀 집계
기본 집계 이외의 다양한 집계 결과물을 산출하는 역할
- reduce()
Arrays.stream(numbers) .filter(number -> number % 2 == 0) .reduce((left, right) -> left * right) .ifPresent(value -> System.out.printf("2의 배수의 곱 : %d\n", value)); System.out.println(); // 조건에 맞는 요소가 없는경우 reduce() 첫 번째 파라미터인 identity값 반환 int result = Arrays.stream(numbers) .filter(number -> number % 2 == 0) .reduce(1,(left, right) -> left * right); System.out.println(result);
3) Optional 클래스
스트림의 최종 결과 값을 저장하는 객체
단순히 값만 저장하는 것이 아니라, 값의 존재 여부를 확인하고 값이 존재하지 않을 경우
기본 값을 설정할 수 있는 객체이다.
최종 처리 기능으로 필터링 또는 매핑한 요소들을 새로운 컬렉션으로 담아서 리턴 받을 수 있다.
public void method1() { List<Student> students = Arrays.asList( new Student("홍길동", 24, "남자", 80, 45), new Student("김철수", 20, "남자", 50, 50), new Student("김영희", 20, "여자", 90, 90), new Student("이몽룡", 26, "남자", 80, 85), new Student("성춘향", 20, "여자", 100, 100) ); //학생들의 이름만 List 컬렉션으로 추출 List<String> names = students.stream() // .map(student .map(Student::getName) .collect(Collectors.toList()); System.out.println(names); System.out.println(); // 남학생들만 List 컬렉션으로 추출 List<Student> list = students.stream() .filter(student -> student.getGender().equals("남자")) .toList(); list.forEach(System.out::println); System.out.println(); // 여학생들만 Set 컬렉션으로 추출 Set<Student> set = students.stream() .filter(student -> student.getGender().equals("여자")) .collect(Collectors.toSet()); set.forEach(System.out::println); System.out.println(); // Map 컬렉션으로 수정 (Key : 이름, value : Student 객체) // 두 번째 파라미터는 자기 자신의 객체를 가리켜 value값을 가져온다 Map<String, Student> map = students.stream() .collect(Collectors.toMap(s -> s.getName(), s -> s)); // map.entrySet().forEach(entry -> {System.out.println(entry.getKey() + " " + entry.getValue());}); map.forEach((key,value) -> System.out.println(key + " " + value)); System.out.println(); // 참고사항 groupingBy Map<String,List<Student>> listMap = students.stream() .collect(Collectors.groupingBy(Student::getGender)); listMap.forEach((s,studentList) -> { System.out.println(s); studentList.forEach(System.out::println); System.out.println(); } ); } }