[Java] Stream이란?
2022. 11. 15. 20:57ㆍProgramming/Java
자바의 기능을 살펴보면 Stream이란 것이 존재한다. 그럼 이 Stream은 무엇일까?
Stream
배열이나 컬렉션에 담긴 데이터를 다룰 때, 반복문이나, iterator를 사용시 코드가 길어지고 가독성이 떨어진다. 이러한 문제점이 존재하기에 Stream이 등장한 것이다.
Stream
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
- 데이터의 흐름
- collection을 소스로 가지고 처리를 하는 것
- 람다를 활용하여 효율적인 코드를 작성할 수 있다
Stream의 특징
- 데이터를 담고 있는 컬랙션이 아니다
- 컬렉션과 연관되어 데이터를 1회성으로 처리하는 역할이다
- 한번 컬렉션 요소에 맞춰 function 및 역할을 수행하고 나면 재사용이 불가하다
- 필요한 경우마다 재사용해야 한다
- 스트림은 처리하는 데이터 원본에 대해 변경이 이뤄지지 않는다
- 무제한일 수도 있다.
- 중개 오퍼레이션은 근본적으로 느리다
- 손쉽게 병렬 처리가 가능하다
- 병렬 처리로 빠른 데이터 처리가 가능하지만, 때때로 직렬로 데이터를 처리하는 경우가 더 빠를 때도 존재
- 상황에 맞춰서 판단
Stream의 구조
스트림은 세 가지 단계로 나눠진다.
- 데이터를 받아오는 collection과 스트림 생성 부분
- Stream <T> Collection.stream()을 이용하여 해당하는 컬렉션을 기반으로 하는 스트림 생성
- 중간에서 데이터를 가공하는 중간 오퍼레이션
- Stream을 return 할 수 있다.
- 스트림을 리턴할 수 있다는 점에서 0 ~ n개의 중간 오퍼레이션이 존재할 수 있다.
- stateless / stateful 연산으로 상세하게 구분이 가능하다
- 대부분 stateless의 메서드지만 stateful 메서드도 존재
- Stream을 return 할 수 있다.
- stream을 리턴하지 않고 해당 가공된 데이터에 대해 값을 리턴하는 종료 오퍼레이션이 존재한다.
- 최종 연산 후 스트림은 리턴되지 않는다. 스트림은 그대로 종료가 되고 사용불가이다.
- 최종 연산의 결괏값은 하나의 값일 수도 혹은 또 다른 컬렉션으로 생성할 수도 있다.
- 스트림의 데이터 소스는 오직 종료 오퍼레이션을 실행할 때만 처리가 된다.
- 하나의 종료 오퍼레이션이 존재한다.
Stream의 API
강의 및 학습하면서 자주 사용이 되는 메서드들을 정리해보려 한다.
- 걸러내기
- filter
- 중간 오퍼레이션
- 입력받은 function을 적용하여 필터링 후 결괏값을 새로운 스트림으로 만들어서 반환
- distinct
- 중간 오퍼레이션
- 중복이 되는 값을 모두 제거하고 새로운 스트림으로 반환한다.
- filter
- 변경하기
- map
- 기존의 stream 요소들을 function에 맞게 변환하여 새로운 stream 만들어내는 연산
- 기존의 stream이 객체로 이루어진 stream이었다면 원하는 요소로만 이루어진 stream으로 변환
- flatmap
- List <Stream <Type>> 같은 컬렉션 안의 컬렉션의 경우를 Type의 스트림으로 변환
- map
// List 선언
List<Integer> in = new ArrayList<>();
for(int i =0; i<10;i++){
in.add(i);
}
// List 내부의 요소를 각각 10씩 곱해서 출력
in.stream().map(i->i*10).forEach(System.out::println);
// List 내부의 요소 중 5보다 큰 값을 출력
in.stream().filter(i -> i > 5).forEach(System.out::println);
// map으로 값을 변경 후 filter로 50보다 큰 수를 필터링
in.stream().map(i->i*10)
.filter(i->i>50)
.forEach(System.out::println);
- 제한하기
- limit(n)
- 최대 n개의 요소가 담긴 스트림을 반환
- skip(n)
- 해당 스트림을 진행할 때 앞에서 n개를 뺀 나머지를 반환
- limit(n)
- 정렬하기
- sorted : 정렬 메서드
- Stream<T> sorted();
- Stream<T> sorted(Comparator < ? super T> comparator);
- sorted : 정렬 메서드
public class Car {
int number;
String name;
public Car(int number, String name) {
this.number = number;
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"number=" + number +
", name='" + name + '\'' +
'}';
}
public int getNumber() {
return number;
}
public String getName() {
return name;
}
}
public class Car {
int number;
String name;
public Car(int number, String name) {
this.number = number;
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"number=" + number +
", name='" + name + '\'' +
'}';
}
public int getNumber() {
return number;
}
public String getName() {
return name;
}
public static void main(String[] args) {
// 리스트 생성
List<Car> car = new ArrayList<>();
for(int i = 1 ; i <= 5; i++){
Car c = new Car(i, "car " + String.valueOf(i));
car.add(c);
}
// 스트림 활용 예제
// car의 번호를 getNumber를 통해 번호를 가져오고 출력하는 코드
car.stream().map(i -> i.getNumber()*10 - i.getNumber()).forEach(System.out::println);
// map을 통해 getName으로 차량 이름을 가져온뒤, filter를 사용하여 해당 문자가 존재하는 객체를 모아다가 리스트로 만들기
List<String> r1 = car.stream().map(Car::getName).filter(s -> s.contains("r 1")).collect(Collectors.toList());
// map을 통해 getName으로 차량 이름을 가져온뒤, filter를 사용하여 해당 문자가 존재하는 객체를 모아다가 리스트로 만들기
List<String> ar = car.stream().map(Car::getName).filter(s -> s.contains("ar")).collect(Collectors.toList());
// map을 통해 getNumber로 차량 번호를 가져온뒤, sorted를 사용하여 번호를 기준으로 역정렬 후 Integer를 모아다가 리스트로 만들기
List<Integer> collect = car.stream().map(Car::getNumber).sorted(reverseOrder()).collect(Collectors.toList());
// getName으로 차량 이름을 역정렬 후 존재하는 객체를 모아다가 리스트로 만들기
List<Car> collect1 = car.stream().sorted(comparing(Car::getName).reversed()).collect(Collectors.toList());
// getName으로 차량 이름을, 그리고 추가정렬로 차량번호를 이용하여 정렬 후 객체를 모아다가 리스트로 만들기
List<Car> collect2 = car.stream().sorted(comparing(Car::getName).thenComparing(Car::getNumber)).collect(Collectors.toList());
System.out.println(collect1.toString());
System.out.println(collect2.toString());
System.out.println(r1);
System.out.println(ar);
System.out.println(collect);
}
}
- 스트림 내부에 있는 데이터가 조건에 만족하는지 확인
- anyMatch()
- 해당 스트림의 모든 요소들을 보면서 최소 하나의 값이 조건에 만족하는지 검사
- allMatch()
- 모든 요소들이 조건에 만족하는지에 대한 검사
- nonMatch()
- 모든 요소들이 조건에 만족하지 않는지에 대해 조사
- anyMatch()
List<Lecture> Classes = new ArrayList<>();
Classes.add(new Lecture(6, "The Java, Test", true));
Classes.add(new Lecture(7, "The Java, Code manipulation", true));
Classes.add(new Lecture(8, "The Java, 8 to 11", false));
// 해당 Classes 스트림의 값 중에 Test 글자가 포함되어있는지 확인
boolean test1 = Classes.stream().anyMatch(oc -> oc.getTitle().contains("Test"));
boolean test2 = Classes.stream().allMatch(oc -> oc.getTitle().contains("Test"));
boolean test3 = Classes.stream().noneMatch(oc -> oc.getTitle().contains("Test"));
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
- 스트림을 데이터 하나로 뭉치기
- collect()
- 만들어진 stream을 원하는 자료형으로 변경해준다
- ex : List / Set / Map... etc
- collect()
22.11.17 : 스트림 메서드 추가
22.12.01 : 스트림 메서드 sorted 및 기본 정의 추가, 예제 추가
'Programming > Java' 카테고리의 다른 글
[Java] Java와 JVM (0) | 2022.11.30 |
---|---|
[Java] 객체지향설계 - SOLID (0) | 2022.11.21 |
[Java] 객체지향 (0) | 2022.11.14 |
[Java] static 개념 및 정리 (0) | 2022.06.26 |
[Java] 자바의 추상 클래스 & 인터페이스 정리 / 상속 (0) | 2022.03.29 |