관리 메뉴

JumpUp

프로그래머스 [나누어 떨어지는 숫자 배열]_Stream 본문

알고리즘

프로그래머스 [나누어 떨어지는 숫자 배열]_Stream

yeunnnn 2021. 3. 22. 23:35

프로그래머스 [나누어 떨어지는 숫자 배열] 문제를 풀어보면서, 간단한 문제에 비해 코드 길이가 비효율적이라고 생각이 들었다.

아래 풀이 법은, 조건에 맞는 숫자를 count하고 count 수에 따라 결과 배열 크기를 할당한 후, 조건에 맞는 수를 결과 배열에 할당하는 방식이다.

import java.util.Arrays;

class Solution {
    public int[] solution(int[] arr, int divisor) {
        int count = 0;
        for(int i=0;i<arr.length;i++){
            if(arr[i] % divisor == 0){
                count++;
            }
        }

        int[] answer;
        if(count == 0) {
            answer = new int[]{-1};
        }
        else {
            answer = new int[count];
            int index = 0;
            for(int i=0;i<arr.length;i++){
                if(arr[i] % divisor == 0){
                    answer[index++] = arr[i];
                }
            }
            Arrays.sort(answer);
        }

        return answer;
    }
}

 

Arrays.stream을 이용해 코드를 수정해보았다.

import java.util.Arrays;

class Solution {
    public int[] solution(int[] arr, int divisor) {
        int[] answer = Arrays.stream(arr).filter(e -> e % divisor == 0).sorted().toArray();
        if(answer.length == 0) return answer = new int[]{-1};
        else return answer;
    }
}

 

코드 길이는 훨씬 짧아졌지만, 수행 속도는 수정 전 코드에 비해 현저히 늦었다.

코드 길이가 짧아진다고 해서 무조건적으로 스트림을 사용할 필요는 없다.

스트림의 단점 두 가지를 들어보자면,

1. 스트림은 오직 한 번만 소비되기 때문에 재사용이 안된다.

2. 성능적으로 빠른 것은 아니다.

   단순 for loop의 경우 오버헤드가 없는 단순한 인덱스 기반 메모리 접근이기 때문에 Stream을 사용했을 때보다 더 빠르다. 이는 이번 문제를 통해 확인했다.

또, 컴파일러 관점에서 오랫동안 유지해온 for-loop의 경우 최적화를 할 수 있으나 반대로 비교적 최근에 도입된 스트림의 경우 for-loop와 같은 정교한 최적화를 수행하지 않는다고 한다.

 

코딩 테스트 때는, 복잡한 로직처리를 위해서가 아니라면 스트림보단 for문으로 해결하는 것이 나을 수도 있다.

 


 

자주 사용될 것 같은 stream메서드들을 정리하고자 한다.

Stream에 대한 내용은 크게 세 가지로 나눌 수 있다.

1. 생성하기: 스트림 인스턴스 생성
2. 가공하기: filtering 및 mapping등 원하는 결과를 만들어가는 중간 작업
3. 결과 만들기

전체 -> 맵핑 -> 필터링 -> 결과 만들기

 

1. 생성하기

  • 배열 스트림

         Arrays.stream(arr)

         Arrays.stream(arr, a, b)  a~b-1범위 값만 스트림으로 생성

 

  • Stream.<Type>builder().add("값").build() - 직접적으로 원하는 값을 넣을 수 있다.

Stream<String> builderStream = 
  Stream.<String>builder()
    .add("Eric").add("Elena").add("Java")
    .build(); // [Eric, Elena, Java]

 

 

  • Stream.of("값") - 직접적으로 원하는 값을 넣을 수 있다

Stream<String> stream1 = Stream.of("Java", "Scala", "Groovy");

 

  • Stream.iterate(초기값, 증감값) - 초기값과 해당 값을 다루는 람다를 이용해 스트림에 들어갈 요소를 만든다. 이 방법은 스트림의 사이즈가 무한하기 때문에 특정 사이즈로 제한해야 한다.

Stream<Integer> iteratedStream = 
Stream.iterate(30, n -> n + 2).limit(5); // [30, 32, 34, 36, 38]

 

  • Stream.concat(stream1, stream2) - 스트림 연결하기

 

 

2. 가공하기

List<String> names = Arrays.asList("Eric", "Elena", "Java");
/*java.util.Arrays.ArrayList로 반환된다. 이 ArrayList는 set,get메서드는 포함하지만 새로운 원소를 추가하는
메서드는 없어 크기를 바꿀 수 없다.*/

아래 가공 메소드들은 위의 리스트를 기반으로 예를 들었다.

 

  • filter( 요소 -> 조건 ) - 스트림 내 요소들을 하나씩 평가해 걸러내는 작업

Stream<String> stream = names.stream().filter( n -> n.contrains("a"));  // [Elena, Java]

 

  • map(개체::메서드) - 스트림 내 요소들을 하나씩 특정 값으로 변환해주는 작업. 

Stream<String> stream = names.stream().map(String::toUpperCase); // [ERIC, ELENA, JAVA]

 

  • Sorting

        sorted()

        sorted(Comparator<? super T> comparator);

 

3. 결과 만들기

  • Calculating

    count, sum 등 기본 형 타입으로 결과를 만들 수 있다. 만약, 스트림이 비어있는 경우 count와 sum은 0을 출력하게 된다. 하지만 평균, 최소, 최대의 경우에는 표현할 수 없기 때문에 Optional을 이용해 리턴한다.

OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();
OptionalInt max = IntStream.of(1, 3, 5, 7, 9).max();

 

  • Collecting
  • Collectors.toList() - 스트림에서 작업한 결과를 담은 리스트로 반환한다.

List<String> collectorCollection =productList.stream().map(Product::getName).collect(Collectors.toList());
// [potatoes, orange, lemon, bread, sugar]

 

  • Collectors.joining() - 스트림에서 작업한 결과를 하나의 스트링으로 이어 붙일 수 있다.

String listToString = productList.stream().map(Product::getName).collect(Collectors.joining());
// potatoesorangelemonbreadsugar

String listToString =  productList.stream().map(Product::getName).collect(Collectors.joining(", ", "<", ">"));
// <potatoes, orange, lemon, bread, sugar>
delimiter : 각 요소 중간에 들어가 요소를 구분시켜주는 구분자
prefix : 결과 맨 앞에 붙는 문자
suffix : 결과 맨 뒤에 붙는 문자

 

 

 

references


 

Java 스트림 Stream (1) 총정리

이번 포스트에서는 Java 8의 스트림(Stream)을 살펴봅니다. 총 두 개의 포스트로, 기본적인 내용을 총정리하는 이번 포스트와 좀 더 고급 내용을 다루는 다음 포스트로 나뉘어져 있습니다. Java 스트

futurecreator.github.io

 

자바 스트림 정리: 5. 스트림을 사용할 때 주의할 점

자바 스트림 API를 사용할 때 실수할 수 있는 부분과 고민해볼 점은 무엇이 있을까?

madplay.github.io

 

728x90