스트림에 알아보기 전에 자바 8 이전부터 알아보겠습니다.
자바 8 이전
for, foreach문을 돌면서 요소 하나씩 꺼내서 다루는 방법
→ 로직이 복잡해질수록 코드의 양이 많아짐,
여러 개의 스레드가 하나의 변수에 영향을 줄 수가 있음
이를 예방하기 위해서는 수동으로 변수에 락을 걸어주거나 , synchronized를 사용하는 것인데 이것은 비용이 비쌉니다.
이러한 이유때문과 이전 글에 썼던 이유로 인해서 함수형 프로그래밍이 나왔습니다.
스트림이란?
- 한번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임
- 배열, 컬렉션에 함수 여러 개를 조합해서 원하는 결과를 필터링하고 가공된 결과를 얻는 것
- 배열과 컬렉션을 함수형(선언형)으로 처리
- 병렬 처리 가능(동시에 실행하더라도 안전하게 실행해야함, 가변 데이터에 접근하지 않아야 한다.(순수 함수))
예시
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> resultList = new ArrayList<>();
for (Integer number : numbers) {
if (number %2 == 0) {
resultList.add(number);
}
}
컬렉션에 데이터들을 담고 그 컬렉션을 순회하고 꺼내면서 직접적으로 연산 (외부 반복)
List<Integer> resultList2 = numbers.stream()
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());
연속된 자료들을 다루고 연산 -> 명시적이고 선언형으로 짤 수 있다.(내부 반복)
코드량과 가독성 좋음
스트림의 특징
- 데이터 소스 - 컬렉션, 배열
- 데이터 처리 연산 - filter, map, reduce, find, match, sort 등으로 데이터 조작
- 파이프라이닝 - 스트림 연산끼리 연결해서 파이프라인을 구성할 수 있도록 스트림 자신을 반환
- 내부 반복
List<String> names = Arrays.asList("홍길동","홍가가","홍나나","홍길순","유재석","박명수",
"정형돈","남궁양석","홍길동","홍길순");
//이름이 3글자 이상이고, "홍"으로 시작하는 성씨를 중복을 없애서 하나씩 출력한다.
List<String> nameList = names.stream() -- 데이터 소스
.filter(name -> name.length() >= 3) -- 데이터 처리 연산, 파이프라인 연산 만들기
.filter(name -> name.startsWith("홍"))
.distinct()
.collect(Collectors.toList()); -- 스트림을 다른 형식으로 저장
- 한 번만 탐색 가능 - 한번 탐색한 요소를 다시 탐색하려면 새로운 스트림을 만들어야 한다.
그럼 간단하게 추가적인 처리 연산을 보겠습니다.
매핑
- map - 각 요소에 접근
Apple apple1 = new Apple("red", 100); Apple apple2 = new Apple("red", 100); final List<Apple> appleList = Arrays.asList(apple1, apple2); appleList.stream().map(apple -> apple.getWeight());
- flatMap - 모든 원소를 단일 원소 스트림으로 반환
- 스트림의 콘텐츠로 매핑
검색과 매칭
allMatch, anyMatch, nonematch, findFirst, findAn
boolean b = apples.stream()
.allMatch(apple -> apple.getWeight() > 30);
Optional<Boolean> any = apples.stream()
.map(apple -> apple.getWeight() > 30)
.findAny();
리듀싱 - reduce
모든 스트림 요소를 처리해서 값으로 도출
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
초기값
list.stream().reduce(0, (a, b) -> a + b);
연산과정에서 스트림이 하나의 값으로 줄어들때까지 반복해서 조합
list.stream().reduce(0, Integer::sum);
추가적으로 많은 실수가 있는 코드를 볼게요
public static void main(String[] args) {
final List<Person> personList = List.of(
new Person("홍길동", 15),
new Person("홍길동", 15),
new Person("박씨", 30),
new Person("분씨", 22),
new Person("윤씨", 22),
new Person("유씨", 22),
new Person("홍길동", 15));
personList.stream().filter(o -> o.getAge() < 28)
.distinct()
.forEach(System.out::println);
}
personList가 있습니다. 근데 28살보다 작으면서 이름과 나이가 같은 사람을 distinct 하고 싶다고 가정했을 때
몇 명이 나올까요??
-> 정답은 그냥 28살 밑으로 6명 그대로 나옵니다.
-> 그 이유는 내부적으로 distinct가 equals로 구분하기 때문입니다. 각 객체의 주소 값(?)이 다다릅니다. 그냥 서로 다른 객체여서 그렇습니다. 그냥 우연히 홍길동이면서 15살인 사람이 3명이 있는 거라고 생각하면 될 것 같아요.
-> 해결 방법은
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
위처럼 equals를 오버라이드 해주는 방법입니다!
'언어 > 자바' 카테고리의 다른 글
자바 컬렉션(List, Map, Set) (0) | 2021.09.10 |
---|---|
동시성(Concurrency)과 병렬성(Parallelism) (0) | 2021.09.08 |
함수형 프로그래밍 & 람다 & 메소드 참조 (0) | 2021.09.05 |
JAVA - 추상클래스와 인터페이스 (0) | 2021.09.03 |
자바의 JVM메모리 구조 (0) | 2021.09.01 |