본문 바로가기
Spring/Java

[ java ] Stream(스트림) 정리 & 예제소스

by snow_hong 2022. 3. 17.
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]

[ 참고 사이트 ]

https://wakestand.tistory.com/419

728x90

댓글