개발하는 자몽

Collections, Collector, stream의 함수 본문

Java

Collections, Collector, stream의 함수

jaamong 2023. 5. 3. 16:59

오랜만에 코딩테스트 문제를 풀어보면서 다른 사람들의 풀이를 보니 stream을 정말 잘 활용하고 있었다. 평소 stream을 잘 활용하지 못하기 때문에 복습 겸 정리한다.

 

Collection Framework, Collectors

Collections는 컬렉션 프레임워크(collection framework)로 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 클래스의 집합이다. 컬렉션 프레임워크는 자바의 인터페이스를 사용하여 구현된다.

 

참고 Collection은 인터페이스, Collections는 클래스이다. 

 

Collector는 요소들을 축적(accumulate)하고 이를 병렬로 또는 순차적으로 처리하게 해주는 감소/축소(reduction) 연산이다. stream의 마지막 단계에서 사용된다. Collectors는 이러한 컬렉터 인터페이스의 구현을 모아둔 클래스의 집합이다.

Collections.swap()

Collections.swap()은 말 그대로 값을 서로 바꿀 때 사용할 수 있다. 이 메서드를 사용하면 for문을 만들고 그 안에서 힘들게 임시 저장할 변수를 따로 선언하면서 이리저리 바꿀 필요가 없다!

Collections.swap(list, i, j);

파라미터

  • List<?> list: 위치를 바꿀 요소들이 속한 리스트
  • int i: j와 위치를 바꿀 인덱스
  • int j: i와 위치를 바꿀 인덱스

Collector.joining()

Collector.joining()String(CharSequence, StringBuilder, String)에 입력받은 파라미터를 순서대로 연결시켜서 Collector로 반환한다. 이 메서드를 사용하면(당연히 stream의 마지막 단계에서) 반복문에서 +나 StringBuilder로 문자열을 합치지 않아도 된다! (메서드 내부를 보면 StringBuilder.append()로 합치고 있다)

Arrays.stream(strArr).collect(Collectors.joining())

위 코드는 어떻게 사용되는지 보여주기 위해 작성했다. stream의 마지막 단계에서 사용되며, String 배열의 값을 순서대로 연결시켜서 Collector로 반환한다.

위 방법보다는 아래 코드처럼 filter()로 조건을 넣거나 String이 아닌 입력값을 형변환한 후에 사용하는 방법이 많이 보인다.

Integer.parseInt(Arrays.stream(arr).filter(i -> i % 2 == 0).mapToObj(String::valueOf).collect(Collectors.joining()))

 

IntStream.mapToObj()

IntStream.mapToObj()stream의 중간 연산으로 사용된다. 흔히 mapTo원시타입()의 형태로 많이 볼 수 있는데, 이 메서드는 원시 타입 stream참조 타입 stream으로 변환한다.

Collector.joining()의 두번째 코드에서 intstream을 새로운 Stringstream으로 반환하는 용도로 쓰인다.

 

IntStream.boxed()

IntStream.boxed()stream의 각 원시 타입 요소를 참조 타입으로 박싱하여 Stream으로 반환한다. 예를 들어 intStream이라면 boxed()를 사용하여 Integer형의 Stream으로 반환된다.

IntStream intStream = Arrays.stream(arr);//int
Stream<Integer> boxed = intStream.boxed();//Integer

 

String.chars()

String.chars()stream이나 Collection이 아닌 String 클래스의 메소드이다. 이 메서드도 문제를 풀다가 stream과 같이 사용되는 코드를 발견해서 정리한다. 이 메서드는 Stringchar값의 IntStream으로 반환한다.

String str = "Hello World";
str.chars().forEach(System.out::println);
72
101
108
108
111
32
87
111
114
108
100

이 메서드를 사용한 문제에는 어떤 수를 구성하는 수가 a 또는 b로만 이루어져야 하는 조건이 있다.

예를 들어, 550가 5와 0으로만 이루어져 있다는 조건을 만족하는지 확인해 보자. 550이 조건을 만족하는지 확인하려면 10으로 나눈 값을 확인할 수 있다. 또는 해당 입력값을 String으로 변환하고 char[]/stream으로 다시 변환, 그리고 각 char 값이 '5' 또는 '0'인지 확인하면 된다.

 

IntStream.allMatch()

IntStream.allMatch()는 모든 요소들이 술어(Predicate)로 주어진 조건을 만족하는지 검사하고, 조건을 모두 만족하거나 stream이 비어 있으면 true를 반환한다. 그렇지 않으면 false를 반환한다. 이 메서드는 Collector.joining()처럼 stream의 마지막 단계에서 사용된다.

 

파라미터

  • IntPredicate predicate: stream의 요소에 적용할 술어(라고 하지만 if문에 넣는 조건이라고 생각하면 된다)

 

IntStream.rangeClosed()

IntStream.rangeClosed()는 순차적으로 정렬된 IntStream을 반환한다.

List<Integer> list = IntStream.rangeClosed(start, end)
        .filter(i -> String.valueOf(i).chars().allMatch(ch -> ch == '0' || ch == '5'))
        .boxed().toList();

위 코드는 이전에 먼저 정리한 chars()와 allMatch()boxed()가 모두 나온다.

스트림의 요소를 start부터 end까지 String으로 변환하고 char값의 IntStream으로 반환한다. 해당 스트림의 모든 요소들이 allMatch() 안의 조건을 만족하면 Stream<Integer>으로 박싱하고 List<Integer>로 축적한다.

 

파라미터

  • int startInclusive: 초기화 값
  • int endInclusive: 마지막 값(Inclusive라는 변수명에서 알 수 있는 것처럼  를 의미한다)

위 코드의 rangeClosed(start, end)를 for문으로 하면 아래와 같다.

for (int i = start ; i <= end; i++) {
    ...
}

 

Integer.intValue()

Integer.intValue()Integer에서 int 값으로 반환한다. Stringint형으로 파싱하는 Integer.parseInt()와 다르다.

list.stream().mapToInt(Integer::intValue).toArray()

위 코드는 Integer형의 Stream의 요소를 int 값으로 반환하여 배열로 축적한다(accumulate).

Comments