본 게시글은 대학 전공수업을 들으며 노션에 정리한 내용을 블로그로 옮긴 것으로, 노션 웹을 통해 최적화된 형태로 읽으시길 권장드립니다.(➡️ 노션 링크)
12강 - 컬렉션과 스트림
forEach()
외부반복
컬렉션이나 배열의 원소를 다룰 때, 원소의 반복처리를 프로그램에서 명시적으로 제어하는 방식
- 원소를 선언된 변수로 복사한 후 작업을 처리
- for, 향상된 for, while, do-while, Iterator 등을 이용한 반복이 외부반복!
내부반복과 forEach()
: 원소의 반복처리를, 컬렉션 또는 스트림과 같은 ‘자료구조 내부’에서 반복을 처리하는 방식.
- 스트림API를 이용해 반복처리를 위임, 처리코드만 람다식으로 작성
- ⇒ 코드 간결, 가독성 증대, 병렬처리, 성능최적화
List<String> names = Arrays.asList("Kim", "Lee", "Park");
//내부반복
names.forEach(item -> System.out.println("내부 반복: " + item));
- forEach메소드는 함수형 인터페이스인 Consumer 객체를 인자로 받는다!
스트림
: 컬렉션이나 배열과같은 데이터 소스로부터 만들어지는 원소의 시퀀스를 표현 → 간결하고 효율적인 처리방법 제공하는 인터페이스
- 내부 반복 및 함수형 프로그래밍 지원
- 멀티코어CPU 병렬처리 지원.
- 관련 클래스 및 인터페이스는 java.util.stream 패키지에 있음.
- 다양한 집합체 연산 메소드 지원(중간 처리/ 최종처리연산 )
- filter(), map(), sorted(), count(), collect(), anyMatch()
특성
데이터 원본으로부터 스트림을 생성 ⇒ 원본 데이터 변경x
- 원본 데이터를 변경하려면 새로운 스트림을 생성해야함.
스트림은 1회용으로, 스트림 연산을 파이프라인 형태로 연결할 수 있음.(메소드 체이닝처럼)
지연평가(lazy evaluation)를 통해 연산을 최적화
- 지연평가란? 중간연산을 미리 수행하지 않고, 최종 연산을 실행할 때 최적화를 계획하여 중간계산을 연산함으로써 효율적으로 연산함.(여러 메소드가 있을때, 무작정 일일히 돌리지 않는다)
스트림 인터페이스 구성도
숫자와 스트림
: IntStream, LongStream, DoubleStream 인터페이스
- 숫자형 원소로 이루어진 데이터를 다루기 위한 스트림
메소드
range(int, int) / rangeClosed(int, int)
public static void main(String[] args) {
System.out.println(IntStream.rangeClosed(1, 100).sum()); //5050
System.out.println(IntStream.rangeClosed(1, 100).average().getAsDouble()); //50.5
System.out.println(IntStream.rangeClosed(1, 100).min().getAsInt()); //1
System.out.println(IntStream.rangeClosed(1, 100).max().getAsInt()); //100
}
- System.out .println(IntStream.rangeClosed(1, 100).sum());
- : 1부터 100까지에 대하여 sum(), 합을 구함
배열과 스트림
: Arrays.Stream()을 사용해 배열로부터 스트림을 생성
Arrays클래스에서 제공되는 메소드.
- static DoubleStream stream(double[] array)
- static IntStream stream(int[] array)
- static Stream stream(T[])
- ⇒원소가 객체인 배열로부터 스트림(제네릭 인터페이스) 생성된다.
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class ArraysStream {
public static void main(String[] args) {
String[] strArray = {"홍길동", "이순신", "김유신"};
//원소가 String 객체인 Stream 객체 생성. 스트림은 제네릭이다!
Stream<String> strStream = Arrays.stream(strArray);
strStream.forEach(item -> System.out.println(item));
int[] intArray = {1, 2, 3};
//숫자의 경우 intStream을 사용
IntStream intStream = Arrays.stream(intArray);
intStream.forEach(item -> System.out.println(item));
}
}
파일과 스트림
Files.lines(Path)를 사용, 텍스트 파일로부터 행단위 문자열로 구성된 스트림 생성
- 예시
Path path = Paths.get("c:\\data\\data.txt"); //행 단위의 문자열 스트림 생성 Stream<String> fileStream = Files.lines(path); //출력 fileStream.forEach(line -> System.out .println(line)); fileStream.close();
컬렉션과 스트림
Collection 유형의 객체인 경우
- Collection 인터페이스에서, stream()과 parallelStream()(병렬처리 가능) 메소드가 기본 제공
- HashSet, ArrayList, LinkedList 객체등으로 부터 스트림을 생성할때 사용함.
HashMap 객체
- entrySet() : Set유형의 객체 생성 → 위의 두 메소드를 사용해 스트림 생성
- 코드
ArrayList 스트림public static void main(String[] args) { Set<Integer> set = new HashSet<>(); for (int i = 0; i < 10; i++) { set.add(i); } // 일반 스트림 사용 : 순서대로 출력 Stream<Integer> n_stream = set.stream(); n_stream.forEach(item -> System.out.print(item)); // 병렬 스트림 사용 : 쓰레드별로 처리하게됨 -> 랜덤출력 Stream<Integer> p_stream = set.parallelStream(); p_stream.forEach(item -> System.out.println(item + "(" + Thread.currentThread().getName() + ")")); } } /*출력결과 0123456789 //병렬처리 : 4개의 쓰레드가 각각 처리 8(main) 0(ForkJoinPool.commonPool-worker-4) 1(ForkJoinPool.commonPool-worker-4) 2(ForkJoinPool.commonPool-worker-2) 3(ForkJoinPool.commonPool-worker-2) 9(main) 4(ForkJoinPool.commonPool-worker-1) 6(ForkJoinPool.commonPool-worker-3) 5(ForkJoinPool.commonPool-worker-1) 7(ForkJoinPool.commonPool-worker-3) */
class People { String name; int age; People(String name, int age) { this.name = name; this.age = age; } public String toString() { return ("name : " + this.name + ", age : " + this.age); } } public class ArrayLIstStream { public static void main(String[] args) { List<People> list = new ArrayList<People>(); list.add(new People("홍길동", 30)); list.add(new People("이순신", 40)); list.add(new People("김유신", 50)); list.add(new People("유관순", 20)); // 일반 스트림 사용 System.out.println("일반 스트림 사용"); Stream<People> n_stream = list.stream(); n_stream.forEach(item -> System.out.println(item)); // 병렬 스트림 사용 System.out.println("병렬처리 스트림 사용"); Stream<People> p_stream = list.parallelStream(); p_stream.forEach(item -> System.out.println(item + " : " + Thread.currentThread().getName())); } } //출력결과 /* 일반 스트림 사용 name : 홍길동, age : 30 name : 이순신, age : 40 name : 김유신, age : 50 name : 유관순, age : 20 병렬처리 스트림 사용 name : 홍길동, age : 30 : ForkJoinPool.commonPool-worker-3 name : 이순신, age : 40 : ForkJoinPool.commonPool-worker-1 name : 유관순, age : 20 : ForkJoinPool.commonPool-worker-2 name : 김유신, age : 50 : main */
- HashSetStream
스트림 파이프라인 개요
생성된 스트림에 중간연산을 여러번 반복하는 것!
(마치 메소드 체이닝을 사용해 파이프라인을 구축하듯)
중간연산
데이터를 변환하여 새로운 스트림을 생성
중간연산을 통해 생성된 스트림은 다음 다음 연산으로 이어지기 때문에, ‘파이프라인’을 형성.
중간처리 메소드 : filter(), map(), sorted() 등…
종료연산(최종연산)
- 중간 연산을 거친 스트림에 대해, 최종 결과를 만들거나 동작을 수행!
- 마지막에 한 번만 수행됨
- foreach(), collect(), count(), anyMatch(), reduce() 등.
- 스트림 파이프라인 예시
import java.util.Arrays; import java.util.List; public class StreamPipeLine { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "cherry", "Avocado"); long count = words.stream() // 원본 스트림 .map(String::toUpperCase) // 중간 연산 .filter(word -> word.startsWith("A")) // 중간연산 .count(); // 최종연산 : long 반환 System.out.println(count); // 원소갯수, 2가 출력됨 words.stream() //위와 마찬가지의 연산 .map(String::toUpperCase) .filter(word -> word.startsWith("A")) .forEach(item -> System.out.println(item)); } } /* 출력결과 2 APPLE AVOCADO */
스트림 파이프라인 세부 내용
중간연산
- ⇒ 스트림을 리턴함 (최종 결과리턴 또는 동작 수행을 하지 않음.)
필터링메소드
: 중복을 제거하거나, 특정 조건을 만족하는 원소만 추출하여 새로운 스트림 생성.
- 중복 제거 : distince()
- 예시
import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data // getter, setter, toString, equals, hashCode 메소드를 자동으로 생성 @AllArgsConstructor @NoArgsConstructor class Book { private String title; private String author; private int price; } public class Distinct { public static void main(String[] args) { List<Book> books = new ArrayList<Book>(); books.add(new Book("JAVA 프로그래밍", "홍길동", 10000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("빅데이터 연구", "유관순", 12000)); books.add(new Book("커뮤니케이션 이론", "강감찬", 15000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("알고리즘", "권율", 17000)); books.stream() .distinct() .forEach(book -> System.out.println(book)); } } /* 출력결과: 'PHP프로그래밍' 중복이 제거! Book(title=JAVA 프로그래밍, author=홍길동, price=10000) Book(title=PHP 프로그래밍, author=이순신, price=20000) Book(title=빅데이터 연구, author=유관순, price=12000) Book(title=커뮤니케이션 이론, author=강감찬, price=15000) Book(title=알고리즘, author=권율, price=17000)*/
- 조건을 통한 걸러내기 : filter()
- 코드
package lecture12; import java.util.Arrays; import java.util.List; public class FilterStream { public static void main(String[] args) { String[] strArray = {"ABC", "BCD", "AFE", "CDE", "ABZ", "ACCZ"}; List<String> strList = Arrays.asList(strArray); strList.stream() .filter(item -> item.startsWith("A")) //A로 시작하는것 중 .filter(item -> item.endsWith("Z")) //Z로 끝나는것 중 .filter(item -> item.length() > 3) //3자 이상 중 .forEach(item -> System.out.println(item)); //출력 System.out.println("--------------------------------"); //다른방식으로 표현하면 strList.stream() .filter(item -> item.startsWith("A") & item.endsWith("Z") & item.length() > 3) .forEach(item -> System.out.println(item)); } } /* 출력 ACCZ -------------------------------- ACCZ*/
매핑
원소들을 다른 원소로 변환해 새로운 스트림 생성
Stream 에서
- map()→ Stream,
- mapToInt() → IntStream
- flatMap() / flatMapToInt() → 여러 스트림을 하나의 스트림으로 합침
- 코드mapTopInt, mapToObj
코드 - flatMap!
- 코드
- 코드 오른쪽의 형태를 참고!
정렬
원소들을 오름차순 / 내림차순으로 정렬해 새로운 스트림을 생성
Stream에서,
- sorted() → 정렬된 새로운 스트림을 리턴.
- 코드
class Book implements Comparable<Book> { private String title; private String author; private int price; public String toString() { return ("title:" + this.title + ", author:" + this.author + ", price:" + this.price); } public int compareTo(Book book) { return Integer.compare(this.price, book.price); } } public class SortedEx { public static void main(String[] args) { List<Book> books = new ArrayList<Book>(); books.add(new Book("JAVA 프로그래밍", "홍길동", 10000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("빅데이터 연구", "유관순", 12000)); books.add(new Book("커뮤니케이션 이론", "강감찬", 15000)); books.add(new Book("PHP 프로그래밍", "이순신", 20000)); books.add(new Book("알고리즘", "권율", 17000)); books.stream().sorted().forEach(book -> System.out.println(book)); } } /* title:JAVA 프로그래밍, author:홍길동, price:10000 title:빅데이터 연구, author:유관순, price:12000 title:커뮤니케이션 이론, author:강감찬, price:15000 title:알고리즘, author:권율, price:17000 title:PHP 프로그래밍, author:이순신, price:20000 title:PHP 프로그래밍, author:이순신, price:20000*/
루핑
원소들을 순회하며 반복적으로 처리하고 새로운 스트림 생성
peek() : 각 요소를 순회하며 주어진 동작(람다식)을 수행하는 중간연산.
public static void main(String[] args) {
List<Book> books = new ArrayList<Book>();
books.add(new Book("JAVA 프로그래밍", "홍길동", 10000));
books.add(new Book("PHP 프로그래밍", "이순신", 20000));
books.add(new Book("빅데이터 연구", "유관순", 12000));
books.add(new Book("커뮤니케이션 이론", "강감찬", 15000));
books.add(new Book("PHP 프로그래밍", "이순신", 20000));
books.add(new Book("알고리즘", "권율", 17000));
books.stream()
.peek(book -> System.out.println(book))
.anyMatch(m -> m.getPrice() > 20000);
}
종료연산
중간 처리를 거친 스트림에 대해 집계, 결과출력 등의 최종 처리를 수행
- 집계 : 합계 / 평균 / 최대 / 최소, 매칭, 수집 등
마지막 단계에서 사용되며 스트림을 다시 사용할 수 없음
lazy evaluation
: 종료연산이 수행되기 전까지 스트림 파이프라인이 실제로 실행되지 않음 → 효율
집계
count() : 개수
sum() : 합계
average() : 평균
max() : 최대값
min() : 최소값
- 코드
public class TerminalOperation { public static void main(String[] args) { int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; long count = Arrays.stream(intArray).filter(n -> n % 2 == 0).count(); System.out.println("2의 배수의 개수 : " + count); long sum = Arrays.stream(intArray).filter(n -> n % 2 == 0).sum(); System.out.println("2의 배수의 합 : " + sum); int min = Arrays.stream(intArray).min().getAsInt(); System.out.println("최소값 : " + min); double avg = Arrays.stream(intArray).average().getAsDouble(); System.out.println("평균 : " + avg); } } /* 의 배수의 개수 : 5 2의 배수의 합 : 30 최소값 : 1 평균 : 5.5 */
매칭
스트림의 원소들의 특정 조건을 만족하는지 확인
anyMatch() : 최소 하나의 요소가 조건을 만족할 때 true
allMatch() : 모든 원소가 조건을 만족할 때 true
noneMatch() : 모든 요소가 조건을 만족하지 못할 때 true
- 코드
package lecture12; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class Member { private String name; private int age; private String gender; } public class TerminalOperation { public static void main(String[] args) { System.out.println("=========수집=========="); List<Member> members = Arrays.asList( new Member("홍길동", 30, "남"), new Member("이순신", 45, "남"), new Member("유관순", 20, "여"), new Member("강감찬", 40, "남"), new Member("신사임당", 50, "여"), new Member("김유신", 25, "남"), new Member("윤봉길", 35, "남"), new Member("남궁옥분", 28, "여") ); //남자만 수집 -> 출력 List<Member> male_members = members.stream().filter(member -> member.getGender().equals("남")).collect(Collectors.toList()); male_members.stream().forEach(member -> System.out.println(member)); //여자만 수집 -> 출력 Set<Member> female_members2 = members.stream().filter(member -> member.getGender().equals("여")).collect(Collectors.toSet()); female_members2.stream().forEach(member -> System.out.println(member)); //여성만 수집한 배열에서 key는 이름, Value는 나이로 Map을 만든 뒤 출력 Map<String, Integer> ages = female_members2.stream().collect(Collectors.toMap(member -> member.getName(), member -> member.getAge())); System.out.println(ages); } } /* 출력결과 =========수집========== Member(name=홍길동, age=30, gender=남) Member(name=이순신, age=45, gender=남) Member(name=강감찬, age=40, gender=남) Member(name=김유신, age=25, gender=남) Member(name=윤봉길, age=35, gender=남) Member(name=신사임당, age=50, gender=여) Member(name=남궁옥분, age=28, gender=여) Member(name=유관순, age=20, gender=여) {신사임당=50, 남궁옥분=28, 유관순=20} */
'Back-End > Java' 카테고리의 다른 글
(전공 정리) 13강 - 멀티 스레드 프로그래밍 : 프로세스, 스레드, 스레드 제어 (0) | 2024.06.22 |
---|---|
(전공 정리) 11강 - 컬렉션 : JCF, Iterator, 자료구조, HashSet, ArrayList, LinkedList, HashMap (0) | 2024.06.22 |
(전공 정리) 9강 - Java.io : 입출력스트림, 파일 입출력 (0) | 2024.06.22 |
(전공 정리) 8강 - java.lang 패키지: Object, String, StringBuffer (0) | 2024.06.22 |
(전공 정리) 7강 - 패키지와 예외처리 (0) | 2024.06.22 |
(전공 정리) 6강 - 제네릭, 람다식 (0) | 2024.06.22 |