Stream (스트림)
java8부터 추가되었다.
스트림은 배열이나 컬렉션(List, Set, Map)으로 원하는 값을 얻을 때 for 문 도배를 방지하기 위해 나온 개념
즉, 컬렉션의 저장 요소를 하나씩 참조해서 처리할 수 있도록 해주는 반복자
장점으로는 코드 간결, 내부 반복자 사용으로 병렬처리가 쉽다.
스트림은 선언, 가공, 반환 세 부분으로 이뤄진다.
방법은 차례대로 알아보자!
선언
배열, 컬렉션(List, Set, Map) 등을 스트림 형태로 만들기
[ 각각 배열과 컬렉션을 사용하는 경우 ]
- Stream<데이터타입> stream명 = Arrays.stream(배열명);
- Stream<데이터타입> stream명 = 리스트명.stream();
- Stream<데이터타입> stream명 = Stream.of('값', '값'....);
[ 직접 값을 넣어 사용하는 경우 ]
- stream을 선언한 후 값을 넣고 사용
또는 꼭 stream을 선언한 후 값을 넣고 사용하는 것이 아니라
- Arrays.stream(배열명).가공메소드...
- 리스트명.stream.가공메소드...
이런 식으로 바로 사용 가능하다.
[ 스트림 예제 소스 ]
import java.util.*;
import java.lang.*;
import java.util.stream.Collectors;
class Rextester
{
public static void main(String args[])
{
int[] arr = {1,1,10,30,2};
// Stream을 사용하는 경우
System.out.println("Stream을 이용한 출력 : " +
Arrays.stream(arr).boxed() // Stream 생성
.distinct() // 중복 제거
.sorted(Comparator.reverseOrder()) // 역정렬
.collect(Collectors.toList()) // List로 반환
);
//결과 : Stream을 이용한 출력 : [30, 10, 2, 1]
}
}
가공
스트림을 필요한 형태로 가공
아래 소스와 같이 스트림을 선언한다고 가정한다.
import java.util.*;
import java.lang.*;
import java.util.stream.Collectors;
public static void main(String[] args) {
int[] arr = {1,1,10,30,2};
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(10);
list.add(30);
list.add(2);
}
[ .boxed() ]
Int, Long, Double 배열로 Stream을 만들었을 경우 각종 메서드를 사용하기 위해 사용한다.
처음에는 붙이지 않고 쓰다가 특정 메서드가 안된다 싶으면 붙이는 식으로 사용하면 된다.
- 컬렉션(List, Set, Map) 스트림에서는 해당 메서드를 사용하지 않는다!!!
// 숫자 배열 Stream 사용 시 여러 메소드를 쓰기 위한 boxed 처리
Arrays.stream(arr).boxed();
[ .count() ]
배열, 컬렉션 크기 확인
// count() 배열, 컬렉션 크기 확인
System.out.println("count() 배열, 컬렉션 크기 확인");
System.out.println(Arrays.stream(arr).count());
System.out.println(list.stream().count());
//결과
//count() 배열, 컬렉션 크기 확인
//5
//5
[ .sorted() ]
정렬 (오름차순 정렬)
// sorted() 정렬
System.out.println("sorted() 정렬");
System.out.println(Arrays.stream(arr).boxed().sorted().collect(Collectors.toList()));
System.out.println(list.stream().sorted().collect(Collectors.toList()));
/* 결과
sorted() 정렬
[1, 1, 2, 10, 30]
[1, 1, 2, 10, 30] */
[ .sorted( Comparator.reverseOrder() ) ]
역정렬 (내림차순 정렬)
// sorted(Comparator.reverseOrder()) 역정렬
System.out.println("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()));
/* 결과
sorted(Comparator.reverseOrder()) 역정렬
[30, 10, 2, 1, 1]
[30, 10, 2, 1, 1] */
[ .findFirst() ]
스트림의 처음 값 가져오기
// findFirst() 처음 값
System.out.println("findFirst() 처음 값");
System.out.println(Arrays.stream(arr).findFirst().getAsInt());
System.out.println(list.stream().findFirst().get());
/*결과
findFirst() 처음 값
1
1 */
[ .skip(배열크기 - 1).findFirst() ]
스트림의 마지막 값 가져오기
// skip(배열크기 - 1).findFirst() 마지막 값
System.out.println("skip(배열크기 - 1).findFirst()");
System.out.println(Arrays.stream(arr).skip(arr.length - 1).findFirst().getAsInt());
System.out.println(list.stream().skip(list.size() - 1).findFirst().get());
/* 결과
skip(배열크기 - 1).findFirst()
2
2
*/
[ .skip(값) ]
값의 인덱스까지 생략하고 나머지를 가져옴 ( N개 생략 )
// skip(값) N개 생략하고
System.out.println("skip(값) N개 생략하고");
System.out.println(Arrays.stream(arr).boxed().skip(2).collect(Collectors.toList()));
System.out.println(list.stream().skip(3).collect(Collectors.toList()));
/* 결과
skip(값) N개 생략하고
[10, 30, 2]
[30, 2]
*/
[ .limit(값) ]
값의 인덱스까지 가져옴
// limit(값) N개 까지
System.out.println("limit(값) N개 까지");
System.out.println(Arrays.stream(arr).boxed().limit(2).collect(Collectors.toList()));
System.out.println(list.stream().limit(3).collect(Collectors.toList()));
/* 결과
limit(값) N개 까지
[1, 1]
[1, 1, 10] */
[ .distinct() ]
중복 생략
// distinct() 중복 생략
System.out.println("distinct() 중복 생략");
System.out.println(Arrays.stream(arr).boxed().distinct().collect(Collectors.toList()));
System.out.println(list.stream().distinct().collect(Collectors.toList()));
/* 결과
distinct() 중복 생략
[1, 10, 30, 2]
[1, 10, 30, 2] */
[ .max(데이터타입::compare) ]
최댓값
// max(데이터타입::compare) 최대값
System.out.println("max(데이터타입::compare) 최대값");
System.out.println(Arrays.stream(arr).boxed().max(Integer::compare).get());
System.out.println(list.stream().max(Integer::compare).get());
/* 결과
max(데이터타입::compare) 최대값
30
30 */
[ .min(데이터타입::compare) ]
최솟값
// min(데이터타입::compare) 최소값
System.out.println("min(데이터타입::compare) 최소값");
System.out.println(Arrays.stream(arr).boxed().min(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());
/* 결과
min(데이터타입::compare) 최소값
1
1 */
[ .average() ]
평균값
- 배열일 경우에는 바로 사용 가능하지만
list, set, map의 경우에는 mapToDouble()을 이용해 한번 바꿔준 후 사용해야 한다.
// average() 평균
System.out.println("average() 평균");
System.out.println(Arrays.stream(arr).average().getAsDouble());
System.out.println(list.stream().mapToDouble(Integer::doubleValue).average().getAsDouble());
/*결과
average() 평균
8.8
8.8 */
[ .sum() ]
합계
- 배열일 경우에는 바로 사용 가능하지만
list, set, map의 경우에는 mapToDouble()을 이용해 한번 바꿔준 후 사용해야 한다.
// sum() 합계
System.out.println("sum() 합계");
System.out.println(Arrays.stream(arr).sum());
System.out.println(list.stream().mapToInt(Integer::intValue).sum());
/* 결과
sum() 합계
44
44 */
[ .reduce(값, 데이터타입::sum) ]
스트림의 값을 모두 하나로 합칠 때 사용하는데 데이터 타입과 sum으로 하나로 합친 뒤 마지막에 값을 더해서 가져오게 된다 (String의 경우에는 값, String::concat을 사용)
// reduce (스트림 값을 모두 하나로 합치기)
System.out.println("reduce (스트림 값을 모두 하나로 합치기) 다 합치고 5 더하기 예제");
System.out.println(Arrays.stream(arr).reduce(5, Integer::sum));
System.out.println(list.stream().reduce(5, Integer::sum));
/* 결과
reduce (스트림 값을 모두 하나로 합치기) 다 합치고 5 더하기 예제
49
49 */
◎ 여기서부터는 람다(Lambda)를 활용한 Stream 가공 메서드를 알아볼 것이다.
람다는 (파라미터) -> {코드}의 간단한 구조이다.(실행코드가 한줄이면 {}생략가능)
[ .map((파라미터) -> 코드) ]
각 인덱스의 값을 파라미터로 넘기고 코드를 수행한다.
주로 값을 바꿔주거나 더해주거나 할 때 사용한다.
map은 코드 부분에는 메서드 사용이 불가능한데 forEach를 사용해 주면 된다.
// 람다(Lambda)를 이용한 가공
// map(값을 원하는대로 가공)
System.out.println("map 1이면 true 아니면 false 예제");
System.out.println(Arrays.stream(arr).boxed().map(val -> val == 1).collect(Collectors.toList()));
System.out.println(list.stream().map(val -> val == 2).collect(Collectors.toList()));
/*결과
map 1이면 true 아니면 false 예제
[true, true, false, false, false]
[false, false, false, false, true]
*/
[ .forEach((파라미터) -> {코드}) ]
각 인덱스의 값을 파라미터로 넘기고 코드를 수행
값마다 다른 메서드를 수행한다거나 할 때 사용한다.
map과 forEach는 흡사하지만 map은 값만 바꿔주는 정도고 forEach는 if else나 메서드 등을 사용하는 것이 중점이 된다.
// map 값마다 10 더하기 예제
System.out.println("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 값마다 10 더하기 예제
[11, 11, 20, 40, 12]
[11, 11, 20, 40, 12]
*/
// map 값 반올림 예제
System.out.println("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()));
/* 결과
map 값 반올림 예제
[1.0, 1.0, 10.0, 30.0, 2.0]
[1.0, 1.0, 10.0, 30.0, 2.0]
*/
// forEach(모든 값마다 입력한 내용 수행)
System.out.println("forEach(모든 값마다 입력한 내용 수행)");
Arrays.stream(arr).boxed().forEach(val -> System.out.println("ForEach 출력! : " + val));
list.stream().forEach(val -> System.out.println("-ForEach 출력! : " + val));
/* 결과
forEach(모든 값마다 입력한 내용 수행)
ForEach 출력! : 1
ForEach 출력! : 1
ForEach 출력! : 10
ForEach 출력! : 30
ForEach 출력! : 2
-ForEach 출력! : 1
-ForEach 출력! : 1
-ForEach 출력! : 10
-ForEach 출력! : 30
-ForEach 출력! : 2
*/
[ .anyMatch((파라미터) -> {코드}) ]
anyMatch는 스트림 중 하나의 값이라도 조건에 맞으면 true
// anyMatch(스트림에서 조건이 하나라도 맞으면)
System.out.println("anyMatch(스트림에서 조건이 하나라도 맞으면) TRUE");
System.out.println(Arrays.stream(arr).anyMatch(val -> val == 1));
System.out.println(list.stream().anyMatch(val -> val == 1));
/*결과
anyMatch(스트림에서 조건이 하나라도 맞으면) TRUE
true
true */
[ .noneMatch((파라미터) -> {코드}) ]
noneMatch는 스트림 중 하나의 값도 조건에 맞지 않으면 true
// noneMatch(스트림에서 조건이 하나도 안맞으면)
System.out.println("noneMatch(스트림에서 조건이 하나도 안맞으면) TRUE");
System.out.println(Arrays.stream(arr).noneMatch(val -> val == 99));
System.out.println(list.stream().noneMatch(val -> val == 30));
/*결과
noneMatch(스트림에서 조건이 하나도 안맞으면) TRUE
true
false */
[ .allMatch((파라미터) -> {코드}) ]
allMatch는 스트림의 값이 모두 조건에 맞아야 true
// allMatch(스트림의 값이 모두 조건과 맞아야)
System.out.println("allMatch(스트림의 값이 모두 조건과 맞아야) TRUE");
System.out.println(Arrays.stream(arr).allMatch(val -> val == 1));
System.out.println(list.stream().allMatch(val -> val == 1));
/* 결과
allMatch(스트림의 값이 모두 조건과 맞아야) TRUE
false
false */
[ .filter(파라미터) -> {코드}) ]
코드에 맞는 값만 가져온다.
// filter (특정 값만 허용)
System.out.println("filter (특정 값만 허용)");
System.out.println(Arrays.stream(arr).boxed().filter(val -> val == 10).collect(Collectors.toList()));
System.out.println(list.stream().filter(val -> val == 1).collect(Collectors.toList()));
/* 결과
filter (특정 값만 허용)
[10]
[1, 1]
*/
반환
가공한 값을 원하는 형태로 가져오기
이제 가공까지 했으면 실제 사용할 수 있도록 값을 가져와야 하는데 System.out.println 으로 값을 찍어봤을 때 java.util.stream.IntPipeline$1@3f99bd52 이런 식으로 나온다면 정상적인 값이 나올 수 있도록 반환 작업을 해야한다.
[ 값이 하나만 있는 경우 ]
.get(), .getAsInt() 등으로 가져올 수 있다.
int val2 = list.stream().max(Integer::compare).get(); // 값 하나 반환
System.out.println(val2); //30
[ 배열 형태로 가져오는 경우 ]
배열의 경우에는 끝에 .toArray();
int[] arr2 = Arrays.stream(arr).distinct().toArray(); // 배열로 반환
System.out.println(arr2); // [I@15aeb7ab
[ 컬렉션(List, Set, Map) 형태로 가져오는 경우 ]
.collect(Collectors.toList()); 에서 List만 Set, Map으로 바꿔주면 된다
List<Integer> list2 = Arrays.stream(arr).boxed().distinct().collect(Collectors.toList()); // List로 반환
System.out.println(list2); //[1, 10, 30, 2]
다른 반환 방법으로는 다음과 같다.
[ .collect(Collectors.counting()); ]
해당하는 갯수 반환
long val3 = list.stream().collect(Collectors.counting()); // 해당하는 갯수 반환
System.out.println(val3); //5
[ .collect(Collectors.joining("값")); ]
모든 값을 합치면서 '값'를 붙여준다.
"" 붙이면 그냥 값만 다 붙이게 됨
String[] strArr = {"10", "20", "30"};
// 컬렉션 내 모든 값을 |를 붙여서 반환
// | 없이 붙여줄려면 ""로 변경
System.out.println(Arrays.stream(strArr).collect(Collectors.joining("|")));
//10|20|30
[ .collect(Collectors.averagingInt(val -> Integer.parseInt(val)) ] [ .collect(Collectors.averagingDouble(val -> Double.parseDouble(val)) ] [ .collect(Collectors.averagingLong(val -> Long.parseLong(val)) ]
값을 int, double, long 형태로 변환한 뒤 double 형태의 평균을 구해 반환
String[] strArr = {"10", "20", "30"};
Double val4 = Arrays.stream(strArr) // Int 형태로 평균값 반환 (배열이 String일 경우)
.collect(Collectors.averagingInt(val -> Integer.parseInt(val)));
Double val5 = Arrays.stream(strArr) // Long 형태로 평균값 반환(배열이 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); //val4 : 20.0
System.out.println("val5 : " + val5); //val5 : 20.0
System.out.println("val6 : " + val6); //val6 : 20.0
[ .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()) ]
이름, 갯수의 형태로 Map<String, Long>로 반환을 해 준다
1,1,1,2,2 이렇게 들었으면 "1" , 3 이런식으로 들어간다는 말이다.
오라클에서 그룹핑 해서 가져오는 것과 동일한 개념이라고 보면 된다.
import java.util.function.Function; //위에 선언하기
String[] getGroupParti = {"zeebra", "cobra", "cobra", "dog"};
// 이름, 갯수로 Group으로 묶어 담아줌
Map<String, Long> map = Arrays.stream(getGroupParti)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println("cobra_map : " + map); //cobra_map : {cobra=2, zeebra=1, dog=1
System.out.println("cobra : " + map.get("cobra")); //cobra : 2
[ .collect(Collectors.partitioningBy((파라미터) -> {코드}) ]
조건에 맞으면 true, 아니면 false로 list를 만들고 Map<Boolean, List<String>> 형태로 반환한다.
// 조건에 맞으면 true, 아니면 false의 list 형태로 담아줌
Map<Boolean, List<String>> map2 = Arrays.stream(getGroupParti)
.collect(Collectors.partitioningBy(val -> val == "cobra"));
System.out.println(map2); //{false=[zeebra, dog], true=[cobra, cobra]}
System.out.println(map2.get(true)); //[cobra, cobra]
[ 참고 사이트 ]
'Spring > Java' 카테고리의 다른 글
[ Java ] String.format() - 문자열 형식 설정 (0) | 2022.04.08 |
---|---|
[ Java ] 쉘 스크립트 실행하기 (0) | 2022.03.18 |
[ java 14 ] 개선된 switch 문(Enhanced Switch Expressions) / 화살표(->) case문 (0) | 2022.03.17 |
[ Java ] HashMap, LinkedHashMap 차이점 및 사용법 (0) | 2022.03.16 |
[ Spirng ] SimpleJdbcCall을 사용하여 프로시저 호출하기 (0) | 2022.03.15 |
댓글