JAVA의 가장 기본이 되는 내용

Stream API

cwchoiit 2024. 4. 7. 19:51
728x90
반응형
SMALL

요즘 Stream API를 사용하면서 얻게 되는 코드 가시성 향상과 코드 라인 간소화가 너무 재밌어진다. 그래서 이것 또한 JAVA에서 기본이 되는 내용 같아서 정리를 하고자한다.

Stream API가 필요한 이유

만약, 다음과 같은 배열이 주어졌다면 그리고 그 배열에 어떤 정렬이 필요하다고 할 때 Stream을 사용하지 않고 처리해보자.

String[] names = {"choi", "kim", "park", "sung", "jang"};

이런 배열이 있을 때, 오름차순으로 정렬을 하고 싶다. 그러면 다음과 같은 메서드를 사용해볼 수 있다.

Arrays.sort(names);

 

그리고 이것을 사용해서 실제로 배열을 출력해보기 위해 다음과 같은 코드를 작성한다. 

String[] names = {"kim", "choi", "park", "sung", "jang"};
        
Arrays.sort(names);

for (String name : names) {
    System.out.println(name);
}

 

실행결과:

choi
jang
kim
park
sung

 

원하는대로 잘 정렬이 됐다. 이게 이 요구사항을 처리할 때 나쁜 코드라고 말할 순 없을 것이다. 

근데 이것은 2가지 단점이 있다.

  • 원본 데이터가 변경된다.
  • 더 복잡한 요구사항이 있다면 로직을 작성했을 때 코드의 간결성이 떨어질 수 있다.

실제로 기존에 names 배열은 이제 없고 정렬된 배열로 변경된다. 위 코드를 보면 반환값을 찍은게 아닌 그대로 "names"를 찍었다.

 

그래서 이를 더 간결화하기 위해 Stream API를 사용한 코드를 보자.

Stream API 사용

Arrays
    .stream(names)
    .sorted()
    .forEach(System.out::println);

 

실행결과:

choi
jang
kim
park
sung

 

저 한 줄로 원하는 결과를 출력할 수 있게 됐다. 그리고 가장 큰 장점은 기존의 배열에는 영향이 없다는 사실이다.

실제로 그런지 아래 코드를 실행해보자.

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "jang"};

    Arrays.stream(names).sorted().forEach(System.out::println);

    System.out.println("names = " + Arrays.toString(names));
}

 

실행결과:

choi
jang
kim
park
sung
names = [kim, choi, park, sung, jang]

 

Stream의 특징

  • 원본의 데이터를 변경하지 않는다.
  • 일회성이므로 한 번 사용하면 다시 사용하지 못한다.

 

어? 원본의 데이터를 변경하지 않는다는 것은 알았는데, 일회성이란 말은 무슨말일까? 다음 코드를 보자.

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "jang"};

    Stream<String> streamNames = Arrays.stream(names);

    streamNames.sorted().forEach(System.out::println);

    System.out.println("firstValue = " + streamNames.findFirst());
}

 

streamNames 라는 Stream을 만들고, 그 녀석으로 오름차순 정렬을 수행한 뒤 루프를 돌려 하나씩 출력하는 코드를 진행 후에 다시 그 스트림을 가지고 첫번째 값을 찍어내는 시도를 했다. 실행 결과는 다음과 같다.

 

보는것과 같이 에러가 발생했다. 에러 내용은 스트림이 이미 한 번 작동됐고 더 사용할 수 없게 닫혀진 상태다라는 내용이다. 이렇듯 스트림은 한번 사용한 후 다시 재사용할 수 없다. 이것을 반드시 알아두어야 한다.

 

 

Stream의 여러 기능 중 절대적으로 중요한 것들

많은 기능이 있는데 개인적으로 filter(), map(), sorted(), distinct() 정도는 알아야 할 것 같다.

 

filter()

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "jang"};

    List<String> filteredList = 
            Arrays.stream(names)
                    .filter(s -> s.length() >= 4)
                    .toList();
    System.out.println("filteredList = " + filteredList);
}

 

말 그대로다. 어떤 솔팅과정을 해주는 메서드이고, 다음과 같이 길이가 4이상인 문자열만 뽑아내고 싶다는 요구사항에 맞게 코드를 작성해주면 다음과 같은 결과를 얻을 수 있다.

filteredList = [choi, park, sung, jang]

 

map()

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "jang"};

    List<String> upperCases = Arrays.stream(names).map(String::toUpperCase).toList();
    System.out.println("upperCases = " + upperCases);
}

 

map()은 기존의 Stream 요소들을 변환하여 새로운 Stream을 만들어내는 메서드이다. 위 코드는 요소 하나하나를 모두 대문자로 변경하고 리스트로 변환하는 경우이다. 출력하면 다음과 같은 결과를 얻을 수 있다.

upperCases = [KIM, CHOI, PARK, SUNG, JANG]

 

 

sorted()

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "jang"};

    List<String> sorted = Arrays.stream(names).sorted().toList();
    System.out.println("sorted = " + sorted);
}

정렬을 할 때 사용되는 sorted(). 실행 결과는 다음과 같다.

sorted = [choi, jang, kim, park, sung]

 

 

distinct()

public static void main(String[] args) {
    String[] names = {"kim", "choi", "park", "sung", "choi", "choi"};

    List<String> sorted = Arrays.stream(names).distinct().toList();
    System.out.println("sorted = " + sorted);
}

중복 제거를 위해 사용되는 distinct(). 실행 결과는 다음과 같다.

sorted = [kim, choi, park, sung]

 

728x90
반응형
LIST

'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글

지역 클래스  (0) 2024.04.21
중첩 클래스(정적 중첩 클래스, 내부 클래스)  (0) 2024.04.14
날짜와 시간  (0) 2024.04.04
Enum  (0) 2024.04.03
Class 클래스  (0) 2024.04.03