목차
Java Stream 사용법 정리😃
Java 1.8부터 지원되기 시작한 stream사용법에 대해 기록한다.
그동안 stream에 대해 깊게 공부해본 적이 없어서 이 기회에 제대로 공부해 보도록 하겠다.
앞으로 jdk 1.8을 사용할 경우에는 배열, 컬렉션 다룰 때 for문 도배하기 보단 효율적으로 stream을 사용할 수 있도록 공부할 예정이다.🤗
예를 들어 int타입 배열의 값을 중복 제거 후 내림차순 정렬해서 list로 반환하려고 한다.
그러면 스트림을 사용하지 않는 경우는 아래처럼 긴 코드를 입력해야 한다.
이를 스트림을 이용하면 아래 한줄이면 된다.
이를 좀 더 풀이하면 아래와 같다.
System.out.println(Arrays.stream(arr).boxed() // stream 생성
.distinct() // 중복 제거
.sorted(Comparator.reverseOrder()) // 내림차순 정렬
.collect(Collectors.toList()) // list로 반환
);
stream에 다양한 메소드들이 많기 때문에 메소드들을 잘 붙이면 효율적으로 이용할 수 있다.
stream은 선언(생성), 가공, 반환 세 부분으로 이루어짐
코드로 보면 아래와 같다.
선언(생성)🥰
배열, 컬렉션(list, set, map..etc) 등을 stream 형태로 만든다.
String[] arr = {"a", "b", "c", "a", "b", "c", "f"};
List<String> list = Arrays.asList(arr);
// 배열 stream 생성
Stream<String> stream = Arrays.stream(arr);
// 컬렉션 stream 생성
Stream<String> cStream = list.stream();
// 병렬처리 stream 생성
Stream<String> paStream = list.parallelStream();
스트림을 선언하고 값을 넣는 위 방법 말고도 아래처럼 직접 바로 사용할 수도 있다.
Arrays.stream(arr).메소드()...
list.stream(arr).메소드()...
가공😏
스트림을 필요한 형태로 가공한다.
.boxed()
int, Long, Double 등 숫자 타입의 배열을 stream으로 만들 경우 stream 각종 메소드를 사용하기 위해 사용
더 찾아보니 IntStream을 Stream으로 변환할 때 boxed()를 사용한다고 한다.
int 자체로는 Collection에 못 담기에 Integer 클래스로 변환하여 List<Integer> 로 담는 용도 등에 사용
ex) IntStream -> Stream<Integer>
컬렉션 스트림에서는 이 메소드를 사용하지 않는다.
// 배열의 값을 중복 제거 후 내림차순 정렬한 뒤 list로 반환받는 코드(편의 상 한줄로 씀)
System.out.println(Arrays.stream(arr).boxed().distinct().sorted(Comparator.reverseOrder()).collect(Collectors.toList()));
.count()
배열, 컬렉션 크기 확인
// 배열의 크기 확인
System.out.println(Arrays.stream(arr).count());
// 컬렉션(list)의 크기 확인
System.out.println(list.stream().count());
.sorted()
정렬할 때 사용
아래 결과를 보면 순서가 정렬되지 않은 int형 배열인 arr과 list의 값이 정렬된 걸 확인 할 수 있다.
// 배열 정렬
System.out.println("배열 내림차순 정렬 : " + Arrays.stream(arr).boxed().sorted(Comparator.reverseOrder()).collect(Collectors.toList()));
// 컬렉션(list)의 크기 확인
System.out.println("컬렉션 정렬 : " + list.stream().sorted().collect(Collectors.toList()));
.sorted(Comparator.reverseOrder())
역정렬(내림차순)할 때 사용
// 배열 역정렬
System.out.println(Arrays.stream(arr).boxed().sorted(Comparator.reverseOrder()).collect(Collectors.toList()));
// 컬렉션 역정렬
System.out.println(list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()));
.findFirst()
스트림의 첫번 째 값을 가져온다.
// 배열 스트림 첫번 째 값 가져온다.
System.out.println(Arrays.stream(arr).findFirst().getAsInt());
// 컬렉션 스트림 첫번 째 값 가져온다.
System.out.println(list.stream().findFirst().get());
.skip(index)
값의 인덱스까지 생략하고 그 다음 번지부터 가져온다.
// 배열 스트림의 첫번 째 인덱스를 제외한 값만 가져온다.
System.out.println(Arrays.stream(arr).boxed().skip(1).collect(Collectors.toList()));
// 컬렉션 스트림의 첫번 째 인덱스를 제외한 값만 가져온다.
System.out.println(list.stream().skip(1).collect(Collectors.toList()));
// 컬렉션 스트림의 첫번 째 인덱스를 제외한 값만 가져온다.(skip에 0을 넣어보니 변화가 없는 걸 보니 1번지부터 시작인 듯 싶다.)
System.out.println(list.stream().skip(0).collect(Collectors.toList()));
.skip(배열크기 -1).findFirst()
스트림의 마지막 인덱스 값 가져오기
// 배열 스트림의 마지막 값 찾기
System.out.println(Arrays.stream(arr).skip(arr.length -1).findFirst().getAsInt());
// 배열 스트림의 마지막에서 두번 째 값 찾기
System.out.println(Arrays.stream(arr).skip(arr.length -2).findFirst().getAsInt());
// 컬렉션 스트림의 마지막 값 찾기
System.out.println(list.stream().skip(list.size() - 1).findFirst().get());
// 컬렉션 스트림의 마지막에서 두번 째 값 찾기
System.out.println(list.stream().skip(list.size() - 2).findFirst().get());
.limit(index)
인덱스까지만 값을 가져온다.
// 배열 스트림 5번 째 인덱스까지만 값 가져오기
System.out.println(Arrays.stream(arr).boxed().limit(5).collect(Collectors.toList()));
// 컬렉션 스트림 5번 째 인덱스까지만 값 가져오기
System.out.println(list.stream().limit(3).collect(Collectors.toList()));
.distinct()
중복 생략
// 배열 스트림의 중복 생략한 값
System.out.println(Arrays.stream(arr).boxed().distinct().collect(Collectors.toList()));
// 컬렉션 스트림의 중복 생략한 값
System.out.println(list.stream().distinct().collect(Collectors.toList()));
.max(데이터타입::compare)
최대값
// 배열 스트림의 최대값
System.out.println(Arrays.stream(arr).boxed().max(Integer::compare).get());
// 컬렉션 스트림의 최대값
System.out.println(list.stream().max(Integer::compare).get());
.min(데이터타입::compare)
최소값
// 배열 스트림의 최소값
System.out.println(Arrays.stream(arr).boxed().min(Integer::compare).get());
// 컬렉션 스트림의 최소값
System.out.println(list.stream().min(Integer::compare).get());
.average()
평균
배열은 바로 사용 가능, 컬렉션의 경우 mapToDouble(Integer::doubleValue)을 한번 거친 다음 사용 가능
// 배열 스트림의 평균
System.out.println(Arrays.stream(arr).average().getAsDouble());
// 컬렉션 스트림의 평균(컬렉션의 경우 아래처럼 .mapToDouble(Integer:doubleValue)을 한번 씌운 다음 .average() 사용 가능
System.out.println(list.stream().mapToDouble(Integer::doubleValue).average().getAsDouble());
.sum()
합계
.average()와 마찬가지로 컬렉션의 경우 mapToInt(Integer::intValue)로 한번 변경 필요
// 배열 스트림의 합계
System.out.println(Arrays.stream(arr).sum());
// 컬렉션 스트림의 합계
System.out.println(list.stream().mapToInt(Integer::intValue).sum());
람다(Lambda)를 활용한 stream 메소드😎
람다에 대한 설명은 아래 글 참조
람다(Lambda)
람다의 핵심은 (paramter) -> { 수행 코드 }를 통해 메소드를 정의하지 않고도 메소드처럼 사용할 수 있다는 것인데 메소드와 같은 기능이지만 정의하지 않고도 사용하는 것이라고 한다.
Lambda는 아무 곳에서나 사용이 불가능하기에 대부분은 스트림이나 forEach 등을 사용할 때 일회용으로 사용된다.
Lambda를 이용한 list, map 출력
.map((function) -> 수행 코드)
함수를 paramter값으로 넘기고 코드를 수행한다. 주로 값을 바꿔주거나 더해줄 때 사용한다.
map은 코드에서 메소드 사용이 불가능한데 이건 .forEach를 사용하면 됨
//userVo list를 userDto list로 변환
public List<UserDto> selectUserList() throws Exception{
List<UserVo> userVoList = userMapper.selectUserList();
List<UserDto> userDtoList = userVoList.stream().
map(u -> new UserDto(u.getUserNo(), u.getName(), u.getEmail(), u.getPassword())).collect(Collectors.toList());
return userDtoList;
}
// 배열 스트림에서 map이 1이면 true 아니면 false
System.out.println(Arrays.stream(arr).boxed().map(val -> val == 1).collect(Collectors.toList()));
// 컬렉션 스트림에서 map이 1이면 true 아니면 false
System.out.println(list.stream().map(val -> val ==1).collect(Collectors.toList()));
// map 값마다 10 더하기
System.out.println(Arrays.stream(arr).boxed().map(val -> val = val + 10).collect(Collectors.toList()));
System.out.println(list.stream().map(val -> val = val + 10).collect(Collectors.toList()));
// map 값 반올림
System.out.println(Arrays.stream(arr).boxed().map(val -> Math.round(val*10)/10.0).collect(Collectors.toList()));
System.out.println(list.stream().map(val -> Math.round(val*10)/10.0).collect(Collectors.toList()));
.forEach((parameter) -> 코드)
각 인덱스의 값을 parameter값으로 넘기고 코드 수행
값마다 다른 메소드를 수행한다거나 할 때 사용
// forEach(모든 값마다 입력한 내용 수행)
Arrays.stream(arr).boxed().forEach(val -> System.out.println("forEach print : " + val));
System.out.println();
list.stream().forEach(val -> System.out.println("ForEach print : " + val));
.anyMatch((paramter) -> {코드})
스트림 중 하나의 값이라도 조건에 맞으면 true
// 스트림 값 중 하나라도 맞으면 true
System.out.println(Arrays.stream(arr).anyMatch(val -> val == 100));
System.out.println(list.stream().anyMatch(val -> val == 40));
.noneMatch((parameter) -> {코드})
스트림 중 하나의 값도 조건에 맞지 않으면 true
// 스트림 값 중 하나도 안맞으면 true
System.out.println(Arrays.stream(arr).noneMatch(val -> val == 10));
System.out.println(list.stream().noneMatch(val -> val == 99));
.allMatch((parameter) -> {코드})
스트림의 값이 모두 조건에 맞아야 true
// 스트림 값 중 모두 조건에 일치하면 true
System.out.println(Arrays.stream(arr).allMatch(val -> val == 30));
System.out.println(list.stream().allMatch(val -> val == 1));
.filter((parameter) -> {코드})
코드에 맞는 값만 가져온다. 자주 쓰이는 함수이다.
// 조건에 해당되는 값만 가져온다.
System.out.println(Arrays.stream(arr).boxed().filter(val -> val == 10).collect(Collectors.toList()));
System.out.println(list.stream().filter(val -> val == 100).collect(Collectors.toList()));
.reduce(값, 데이터타입::sum)
스트림의 값을 모두 하나로 합칠 때 사용함
데이터타입과 sum으로 하나로 합친 뒤 마지막에 값을 더해서 반환함
(String의 경우에는 값, String::concat을 사용)
// 스트림 값을 모두 하나로 합치고 마지막에 5를 더한다.
System.out.println(Arrays.stream(arr).reduce(100000, Integer::sum));
System.out.println(list.stream().reduce(500000, Integer::sum));
// 스트림을 하나로 합치고 "avenger : "를 붙인다.
System.out.println(list2.stream().reduce("avengers : ", String::concat));
반환😃
위에서 가공한 값을 원하는 형태로 가져온다.
값이 하나만 있는 경우라면 get(), getAsInt() 등으로 가져옴
배열로 가져올려면 끝에 .toArray();
나머지 컬렉션 형태는 .collect(Collectors.toList()); 이런식으로 가져오면 되고
list가 아닌 다른 걸로 가져오고 싶으면 Collectors.toList()를 toSet(), toMap() 이런식으로 가져오면 됨
그 외의 반환 방법은 아래처럼 반환 갯수로 반환할 수도 있고 나머지는 아래 반환 예제에 적었다.
Long cnt = Arrays.stream(arr).boxed().sorted().collect(Collectors.counting());
아래처럼 특정 문자열을 구분자로 붙여서 반환할 수도 있음
System.out.println(Arrays.stream(strArr).collect(Collectors.joining("|")));
반환 예제
package java_test.dev.java;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
// stream 반환 예제
public class Test1 {
public static void main(String[] args) {
int[] arr = {10, 122, 10, 30, 2, 30, 31, 32, 33, 100, 1};
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
list.add(1);
list.add(3);
list.add(4);
list.add(40);
list.add(100);
System.out.println(Arrays.stream(arr).boxed().distinct()); // 반환하기 전, 즉 가공만 한 상태
System.out.println(list.stream().max(Integer::compare)); // 반환하기 전, 즉 가공만 한 상태
int[] arr2 = Arrays.stream(arr).distinct().toArray(); // 배열로 반환
for (int i : arr2) {System.out.println(i);}
System.out.println("=============================================================");
List<Integer> list2 = Arrays.stream(arr).boxed().distinct().collect(Collectors.toList());
list2.forEach(val -> System.out.println(val));
System.out.println("=============================================================");
int val2 = list.stream().max(Integer::compare).get(); // 최대값 하나 반환
System.out.println("val2 : " + val2);
System.out.println("=============================================================");
long cnt = list.stream().collect(Collectors.counting()); // 해당하는 갯수 반환
System.out.println("cnt : " + cnt);
// 문자열 배열 선언
String[] strArr = {"10", "20", "30"};
System.out.println("=============================================================");
// 컬렉션 내 모든 값을 |를 붙여서 반환 | 없이 붙여줄려면 ""로 변경
System.out.println(Arrays.stream(strArr).collect(Collectors.joining("|")));
System.out.println("=============================================================");
Double val4 = Arrays.stream(strArr) // Int 형태로 평균값 반환 (배열이 String일 경우)
.collect(Collectors.averagingInt(val -> Integer.parseInt(val)));
Double val5 = Arrays.stream(strArr) // Double 형태로 평균값 반환(배열이 String일 경우)
.collect(Collectors.averagingDouble(val -> Double.parseDouble(val)));
Double val6 = Arrays.stream(strArr) // Long 형태로 평균값 반환(배열이 String일 경우)
.collect(Collectors.averagingLong(val -> Long.parseLong(val)));
System.out.println("val4 : " + val4);
System.out.println("val5 : " + val5);
System.out.println("val6 : " + val6); // 값 확인
System.out.println("=============================================================");
String[] getGroupParti = {"hulk", "ironMan", "loki", "thor", "hulk", "hulk", "hulk", "hulk"};
// 이름, 갯수로 grouping함(오라클에서 group by해서 가져오는 것과 같은 개념)
Map<String, Long> map = Arrays.stream(getGroupParti)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println("hulk : " + map.get("hulk")); // 결과 : hulk : 5(hulk가 5개니까)
System.out.println("=============================================================");
// 조건에 맞으면 true, 아니면 false의 list 형태로 담아줌
Map<Boolean, List<String>> map2 = Arrays.stream(getGroupParti)
.collect(Collectors.partitioningBy(val -> val == "hulk"));
System.out.println(map2.get(true)); // 결과 : [hulk,hulk,hulk,hulk,hulk]
}
}
결과
java.util.stream.DistinctOps$1@e9e54c2
Optional[100]
10
122
30
2
31
32
33
100
1
=============================================================
10
122
30
2
31
32
33
100
1
=============================================================
val2 : 100
=============================================================
cnt : 7
=============================================================
10|20|30
=============================================================
val4 : 20.0
val5 : 20.0
val6 : 20.0
=============================================================
hulk : 5
=============================================================
[hulk, hulk, hulk, hulk, hulk]
마무리🥱
stream의 장점🤗
1. 사용하기 편함
2. 코드가 간결해짐
3. 가독성이 높아짐
stream의 단점😑
1. 디버깅이 힘듬
2. 재활용 불가능
3. 속도가 느림
장단이 있으니 적재적소에 효율적으로 쓰는게 중요할 듯 싶다.
절대 외울 필요는 없고 이해를 하고 그를 바탕으로 나중에 참조해서 사용하면 된다.
역시 기억하는 것보다 기록하는게 더 공부가 되는 듯 싶고 실제로 코딩해보고 블로그에 글을 한번 정리하는 것만으로 기존보단 많이 이해가 되었다.
코딩하고 결과 보고 이해하고 글 적는 시간 토탈 대략 1시간 넘게 걸린 것 같다.😥
참조 : Wakestand Island, 2020.12.6, https://wakestand.tistory.com/419, https://wakestand.tistory.com/418
'IT > development' 카테고리의 다른 글
[Java] Java String convert to byte[], byte[] convert to String (2) | 2022.11.23 |
---|---|
[php] php <-> cubrid driver 연동 (0) | 2022.11.23 |
[Java] Java Lambda 정리(jdk 1.8부터 사용 가능) (0) | 2022.11.23 |
[IDE] eclipse tomcat "Serve modules without publishing"... (0) | 2022.11.23 |
[php] Windows 10 Laravel 설치... (0) | 2022.11.23 |