[JAVA] Stream을 이용한 Array와 List간의 변환 / Stream 데이터 필터링, 정렬 등

EUN JY·2022년 2월 17일
2

JAVA

목록 보기
6/7
post-thumbnail

Stream

람다와 함께 사용되어 간결하게 표현 가능한 기능으로, Java 8부터 지원된다.
병렬 처리가 가능하며 Stream 인스턴스 생성, 가공, 결과 생성이 가능하다.

1. 생성

주로 배열과 컬렉션을 이용해 Stream을 만든다.
다음은 배열로 Stream을 만드는 방법이다.

String[] arr = new String[]{"y", "n", "j", "c", "h"};
Stream<String> stream1 = Arrays.stream(arr); // 결과 : [y, n, j, c, h]
Stream<String> stream2 = Arrays.stream(arr, 1, 4); // 결과 : [n, j, c]

1-1. builder()

Stream.builder()를 이용하여 직접 원하는 값을 add 할 수 있다.

Stream<String> stream = Stream.<String>builder()
    .add("ynjch").add("eunjy").add("velog")
    .build(); 
    
// 결과 : [ynjch, eunjy, velog]

1-2. iterate()

Stream.iterate()를 이용해 반복적인 값을 넣어 Stream을 만들 수 있다.
사이즈가 무한으로 생성되기 때문에 limit() 설정을 할 필요가 있다.

Stream<Integer> iteratedStream = 
Stream.iterate(10, n -> n+2).limit(3); 

// 결과 : [10, 12, 14]

1-3. range()

두 번째 인자에 들어가는 종료 위치의 포함 여부에 따라,
range()rangeClosed()를 사용할 수 있다.

// import java.util.stream.IntStream;
IntStream stream1 = IntStream.range(1, 5); 
IntStream stream2 = IntStream.rangeClosed(1, 5); 

// stream1 : [1, 2, 3, 4]
// stream2 : [1, 2, 3, 4, 5]

문자열의 경우 chars()를 이용해 IntStream 형태로 생성 가능하다.

IntStream stream = "YNJCH".chars(); 
  
// 결과 : [89, 78, 74, 67, 72]

1-4. splitAsStream()

splitAsStream()을 이용하여 문자열을 잘라 Stream을 구성할 수 있다.

Stream<String> stringStream = 
  Pattern.compile(", ").splitAsStream("Dog, Cat, Pig");
  
// 결과 : [Dog, Cat, Pig]

1-5. concat()

concat()을 이용하여 두 개의 Stream을 연결할 수 있다.

Stream<String> stream1 = Stream.of("Tiger", "Lion");
Stream<String> stream2 = Stream.of("Rabbit", "Monkey", "Wolf");
Stream<String> concat = Stream.concat(stream1, stream2);

// 결과 [Tiger, Lion, Rabbit, Monkey, Wolf]

2. 가공

String 타입의 배열은 다음과 같이 List로 변환할 수 있다.

String[] arr = {"LEON", "TIGRE", "CEBRA", "CEBRA", "OSO"};
List<String> strList = Arrays.asList(arr);

int 타입의 배열은 다음과 같이 Integer 타입의 List로 변환할 수 있다.
boxed()를 사용하여 박싱(boxing)할 수 있다.
IntStream 같은 원시 타입에 대해 Integer 클래스로 변환하여 List<Integer> 로 담기 위해 사용했다.

int[] arr = {3, 7, -4, 6, 0, 2};
List<Integer> intList = Arrays.stream(arr)
	.boxed()
	.collect(Collectors.toList());

2-1. filter()

인자로 Predicate를 받는데, boolean 값을 리턴하는 함수형 인터페이스로 작성하게 된다.
contains() 메소드를 활용해 "O"를 포함하는 값을 리턴해본다.

Stream<String> stream = strList.stream()
	.filter(str -> str.contains("O"));
  
// 결과 : [LEON, OSO]

startsWith() 메소드로 "C"로 시작하는 값을 리턴해본다.
distinct() 메소드로 중복을 제거한다.

Stream<String> stream = strList.stream()
    .filter(str -> str.startsWith("C"));
  
// 결과 : [CEBRA, CEBRA]

Stream<String> stream = strList.stream()
    .distinct()
    .filter(str -> str.startsWith("C"));
  
// 결과 : [CEBRA, CEBRA]

다음과 같이 작성하여 Integer 타입의 리스트에서 0보다 큰 수를 리턴한다.

Stream<Integer> stream = intList.stream()
    .filter(int -> (int>0));
    
// 결과 : [3, 7, 6, 2]

2-2. sorted()

기본 오름차순 정렬을 위해 sorted()를 사용한다.
내림차순 정렬 시 .sorted(Comparator.reverseOrder())를 사용한다.

Stream<String> stream = strList.stream()
	.distinct()
    .sorted();
           
// 결과 : [CEBRA, LEON, OSO, TIGRE] 

문자열의 길이에 따라 정렬하기 위해 Comparator를 사용한다.

Stream<String> stream = strList.stream()
  	.sorted(Comparator.comparingInt(String::length));
    
// 결과 : [OSO, LEON, TIGRE, CEBRA, CEBRA] 

2-3. map()

Stream 내의 값들이 모두 특정 값으로 변환될 수 있도록 한다.
모든 문자열을 소문자로 바꾸는 toLowerCase, 대문자로 바꾸는 toUpperCase를 예로 들 수 있다.

Stream<String> stream = strList.stream()
	.filter(str -> str.contains("O"))
    .map(String::toLowerCase);

// 결과 : [leon, oso]

다음처럼 요소 내에 있는 Item 개체의 수량을 꺼내올 수도 있다.
예시는 Item을 Item의 수량으로 맵핑한 것이다.

Stream<Integer> stream = itemList.stream()
	.map(Item::getAmount);
    
// 결과 : [120, 300, 256]

3. 결과

생성된 Stream을 forEach() 메소드를 이용해 출력해본다.

Stream<String> stream = strList.stream()
	.filter(str -> str.contains("O"))
    
stream.forEach(System.out::println);

/* 
LEON
OSO
*/

3-1. 통계

count()를 이용해 개수를 구하거나, sum()을 이용해 총합을 구할 수 있다.

long count1 = IntStream.of(1, 3, 5, 7, 9).count();
long sum1 = LongStream.of(1, 3, 5, 7, 9).sum();
long count2 = IntStream.range(1, 4).count();
long sum2 = IntStream.range(1, 4).sum();

// count1 : 5, sum1 : 25, count2 : 4, sum2 : 10

arr의 길이를 구하기 위해 Stream<Integer>를 사용하여 count() 연산을 하므로 boxed()가 필요하다.
평균, 최솟값, 최댓값을 구하기 위해서는 IntStream을 활용하므로 boxed()를 쓰지 않는다.

만약 Stream이 비어있다면, count(), sum()은 0을 리턴한다.
average(), min(), max()는 그렇지 않기 때문에 OptionalDouble 또는 OptionalInt를 사용한다.
값 출력 시 orElse(0) 을 활용해 0을 리턴할 수 있도록 한다.

// int[] arr = {3, 7, -4, 6, 0, 2}

long count = Arrays.stream(arr).boxed().count();
OptionalDouble avg = Arrays.stream(arr).average();
OptionalInt min = Arrays.stream(arr).min();
OptionalInt max  =Arrays.stream(arr).max();

System.out.println(avg.orElse(0));    
System.out.println(min.orElse(0));
System.out.println(max.orElse(0));

// count : 6

/*
2.3333333333333335
-4
7
*/

3-2. toList()

Stream에서 작업한 내용을 List로 반환할 때 사용한다.

int[] arr = {3, 7, -4, 6, 0, 2};

List<Integer> intList = Arrays.stream(arr)
	.boxed()
	.collect(Collectors.toList());

3-3. joining()

결과를 하나의 String으로 이어 붙여 반환할 때 사용한다.

String[] arr = {"LEON", "TIGRE", "CEBRA", "CEBRA", "OSO"};
Stream<String> stream = Arrays.stream(arr);
String answer = stream.collect(Collectors.joining());

// 결과 : LEONTIGRECEBRACEBRAOSO

https://futurecreator.github.io/2018/08/26/java-8-streams/ 를 참고하면 더 다양한 사용법을 확인할 수 있다.

profile
개린이

0개의 댓글