일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Mac
- endpoint
- terraform main commands
- java
- lsof
- terraform 기본개념
- PrivateSubnet
- 이진탐색
- terraform
- 프로그래머스
- Python
- terraform 설치
- session manager
- Jenkins
- Jenkinspipeline
- heapq
- JWT
- Process monitoring
- s3
- ec2
- Timezon설정
- terraform variable
- 인텔리제이
- terraform backend
- SELinux비활성화
- algorithm
- Docker
- haproxy
- linuxr계정설정
- binarysearch
- Today
- Total
MONG 기술블로그
Java Stream 본문
일반적으로 컬렉션 및 배열에 저장된 요소를 반복 처리하기 위해서는 for / while문을 사용해야했다.
Java 8 부터는 컬렉션 및 배열 요소에 대해 반복 처리하기위해 스트림(Stream)을 통해 접근 할 수 있다.
List<String> list = {1,2,3,4,5};
// for문
for(String str : list){...}
// stream
Stream<String> stream = list.stream();
stream.forEach(item -> ... );
Stream은 기존에 사용하던 Iterator와 비교하여 다음과같은 차이점을 가지고 있다.
1. 내부 반복자이므로 처리속도가 빠르고 병렬 처리에 효율적이다
2. 람다식으로 다양한 요소 처리를 정의할 수 있다.
3. 중간 처리와 최종 처리를 수행하도록 파이프라인을 형성할 수 있다.
위 내용에 대해 키워드별로 하나씩 알아보자
내부 반복자
for / iterator 는 컬렉션의 요소를 컬렉션 바깥쪽으로 가져와 반복적으로 처리한다.
이에 비해 stream은 파라미터로 lambda 함수를 넘겨줌으로써 내부에서 반복하여 처리한다.
이를 코드로 비교해보면 다음과 같다.
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add(4);
Iterator<Integer> iterator = integerList.iterator();
while (iterator.hasNext()) {
// (외부 반복자) 반복을 위해 외부에 변수로 할당
Integer element = iterator.next();
}
// (내부 반복자) lambda로 정의된 함수를 전달하여 내부에서 반복
integerList.stream().forEach(element -> System.out.println(element));
따라서 위와같이 내부 반복자를 사용할 경우 멀티코어 CPU를 최대한 활용하기위해 요소들을 분배시켜 병렬 작업을 할 수 있다.
코드 예제를 통해 스트림을 이용한 병렬 수행에 대해 한번 살펴보자.
List<Integer> integerList = new ArrayList<>();
for(int i=0;i<100;i++){
integerList.add(i);
}
integerList.parallelStream().forEach(element ->{
System.out.println(element + ": " + Thread.currentThread().getName());
});
위의 실행 결과를 살펴보면 여러개의 Thread를 사용하여 병렬적으로 처리됨을 확인할 수 있다.
스트림 파이프라인
스트림은 하나 이상 연결될 수 있다. 즉 스트림의 목적에 따라 최초 스트림에 필터링 / 매핑 / 집계처리 기능을 하는 스트림을 추가로 연결하여 일종의 파이프라인 구성이 가능하다.
다음은 0부터 99까지의 숫자 중 짝수만 따로 필터한 뒤 합계를 구하는 스트림 예제이다.
List<Integer> integerList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
integerList.add(i);
}
int evenSum = integerList.parallelStream()
.filter(element -> element % 2 == 0)
.mapToInt(element -> element)
.sum();
System.out.println(evenSum);
요소 필터링
필터링은 조건에 맞는 요소를 걸러내는 중간 처리 기능이다.
필터링 메소드는 크게 중복을 제거하는 distinct() 와 조건을 Predicate로 정의하여 요소를 걸러내는 filter()가 존재한다.
따라서 filter를 사용하기 전 filter의 매개변수로 사용되는 Predicate 클래스에 대해 간단히 살펴보자.
위를 보면 Predicate는 @FunctionalInterface로써 test라는 한개의 추상 메소드가 정의되어있음을 알 수 있다.
따라서 스트림의 filter 메소드는 다음의 형태로 사용할 수 있는 것이다.
..stream().filter(item -> {
if(item > 10){
return true;
}else{
return false;
}
})
요소 변환(매핑)
매핑은 스트림의 요소를 다른 요소로 변환하는 중간 처리 기능이다.
이를 예시를 통해 알아보자.
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
Function<Integer, String> function = (element) -> {
return element.toString();
};
List<String> stringList = integerList.stream().map(function).toList();
stringList.stream().forEach(elt -> System.out.println(elt));
위 예시를 보면 매핑 기능을 담당하는 map 함수에 Function 클래스의 오브젝트를 전달하여 Integer 배열이 String 배열로 변경됨을 알 수 있다.
이때 전달되는 Function 클래스도 @FunctionalInterface 로써 람다식을 통해 바로 전달이 가능하다.
스트림 정렬
Stream을 통해 2개 이상의 정렬 기준을 가진 객체를 손쉽게 정렬해보자.
package chap17;
import java.util.ArrayList;
import java.util.List;
class Person {
private int age;
private int wait;
public Person(int age, int wait) {
this.age = age;
this.wait = wait;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWait() {
return wait;
}
public void setWait(int wait) {
this.wait = wait;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", wait=" + wait +
'}';
}
}
public class 정렬 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person(1, 10));
personList.add(new Person(2, 10));
personList.add(new Person(1, 5));
List<Person> sortedPersonList = personList.stream().sorted((o1, o2) -> {
if (o1.getAge() == o2.getAge()) {
return -(o1.getWait() - o2.getWait());
}
return o1.getAge() - o2.getAge();
}).toList();
sortedPersonList.stream().forEach(elt -> System.out.println(elt));
}
}
매칭
이번엔 AllMatch 와 AnyMatch에 대해서 알아보자. 해당 기능 또한 filter와 마찬가지로 Predicate를 사용한다.
예시를 통해 알아보자.
int[] arr = {2, 4, 6, 8, 10};
// arr에 존재하는 모든 수가 짝수인가 ? -> true
System.out.println(Arrays.stream(arr).allMatch(elt -> elt % 2 == 0));
// 2가 포함되어있나 ? -> true
System.out.println(Arrays.stream(arr).anyMatch(elt -> elt == 2));
집계
집계는 최종 처리 기능으로 요소들을 처리해서 카운팅, 합계, 평균값, 최대값, 최소값 등과같이 하나의 값으로 산출하는것을 뜻한다.
즉 대량의 데이터를 가공해서 하나의 값으로 축소하는 리덕션의 개념이다.
간단하게 sum 에시를 보고 넘어가자.
int[] arr = {2, 4, 6, 8, 10};
System.out.println(Arrays.stream(arr).sum());
수집
마지막으로 collect에 대해 예시로 알아보자.
List<Element> eltList = new ArrayList<>();
eltList.add(new Element(1, "sam"));
eltList.add(new Element(2, "smith"));
eltList.add(new Element(3, "john"));
eltList.add(new Element(4, "kitty"));
eltList.add(new Element(5, "wheel"));
eltList.add(new Element(6, "paul"));
// toMap(Funtion<T,K> keyMapper, Funtion<T,U> valueMapper)
Map<Long, String> eltEvenMap = eltList.stream()
.filter(elt -> elt.getId() % 2 == 0)
.collect(Collectors.toMap(
elt -> elt.getId(),
elt -> elt.getName()
));
eltEvenMap.keySet().stream().forEach(key ->
System.out.println("key : " + key + " value : " + eltEvenMap.get(key))
);
'Programming > Java' 카테고리의 다른 글
람다식 ( Lambda Function ) (0) | 2023.03.24 |
---|