[Java] 스트림 중간 처리 - 필터링, 매핑, 정렬

Bam·2024년 3월 19일
0

Java

목록 보기
82/98
post-thumbnail

지난 포스트에서 스트림의 기본 개념과 리소스로부터의 스트림 취득 방법을 알아보았으니 이번에는 스트림 파이프라인에서 중간 처리를 하는 기능들을 알아보겠습니다.


필터링

필터링(filtering)은 조건에 맞는 요소를 걸러내는 중간 처리 기능입니다. 필터링 기능을 하는 메소드는 다음 두 가지 메소드가 있습니다.

메소드설명
distinct()중복 요소 제거
filter(Predicate)조건에 따라 필터링
Predicate는 스트림 요소 타입에 따르는 함수형 인터페이스(람다식으로 작성)

distinct()는 중복을 제거하는데, 요소가 객체일 경우 equals() 메소드의 결과가 true일 경우에만 제거를 수행합니다.

filter()Predicate라는 함수형 인터페이스를 인자로 받아서 조건부 필터링을 수행합니다. Predicatetest()라는 추상 메소드를 가진 함수형 인터페이스입니다.

test()는 매개변수 값을 조건에 따라 검사하고 boolean 타입을 반환하는 메소드입니다. 즉, test()를 다음과 같은 형태의 람다식으로 사용합니다.

elem -> {
	//elem 검사 코드
    return true/false;
}

다음 코드는 중복 제거와 필터링을 수행하는 코드입니다.

public class Main {
    public static void main(String[] args) {

        Integer[] arr = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7};
        List<Integer> list = Arrays.asList(arr);

		//스트림 중복 제거
        list.stream().distinct().forEach(elem -> System.out.print(elem + " "));

        System.out.println();

		//스트림 필터링: 홀수만 출력
        list.stream().filter(elem -> elem % 2 == 1).forEach(elem -> System.out.print(elem + " "));
    }
}

distinct(), filter()는 중간 처리 기능이기 때문에 최종 처리를 하는 forEach()와 같은 메소드를 반드시 사용해 주어야한다는 것을 꼭 기억해주세요.


매핑

매핑(mapping)은 스트림의 요소를 다른 요소로 변환하는 중간 처리 기능입니다. 매핑 기능을 하는 메소드는 다음과 같습니다.

메소드설명(변환)
Stream<After> map(Function<Before, After>)Before을 After로 변환
IntStream mapToInt(Function<T>)T를 int로 변환
LongStream mapToLong(Function<T>)T를 long로 변환
DoubleStream mapToDouble(Function<T>)T를 double로 변환
Stream<T> mapToObj(Function<U>)int, long, double을 U로 변환
DoubleStream mapToDouble(Function)int, long을 double로 변환
IntStream mapToInt(Funtion)
LongStream mapToLong(Function)
doule을 int, long으로 변환

Function은 마찬가지로 함수형 인터페이스입니다. 이 함수형 인터페이스는 applyXxx()라는 추상 메소드를 가지고 있습니다.

applyXxx()는 매개변수로 전달한 값을 return 값으로 변환하는 역할을 수행하는 메소드입니다. Xxx는 변환 타입에 따라 AsInt 등과 같은 이름이 옵니다. 람다식으로 표현하면 다음과 같습니다.

T -> {
	//매핑 처리 코드
	return R
}

다음 코드는 문자열로 된 숫자 배열 요소를 int형으로 매핑하고 출력하는 예제입니다.

public class Main {
    public static void main(String[] args) {
        String[] arr = { "1", "2", "3" };
        List<String> list = Arrays.asList(arr);

        list.stream()
        	.mapToInt(elem -> Integer.parseInt(elem))
            .forEach(elem -> System.out.print(elem + " "));
    }
}


만약 기본형끼리의 매핑이나 래퍼 객체로 변환하는 경우에는 다음과 같은 메소드를 이용해서 더 편리하게 코드를 작성할 수 있습니다.

메소드설명(변환)
LongStream asLongStream()int를 long으로 변환
DoubleStream asDoubleStream()int, long을 double로 변환
Stream<Integer or Long or Double> boxed()int는 Integer, long은 Long, double은 Double로 변환

또한 하나의 요소를 여러 개의 요소로 매핑할 수도 있습니다. flatMap() 메소드는 하나의 요소를 여러 요소로 변환한 새 스트림을 반환합니다.

메소드설명(변환)
flatMap(Function)T, int, long, double을 각각 타입의 Stream으로 변환
IntStream flatMapToInt(Function<T, IntStream>)T를 IntStream으로 변환
LongStream flatMapToLong(Function<T, LongStream>)T를 IntStream으로 변환
DoubleStream flatMapToDouble(Function<T, DoubleStream>)T를 IntStream으로 변환

정렬

요소를 오름차순, 내림차순의 순서로 정렬하는 것도 중간 처리 기능입니다. 스트림은 요소 정렬을 위해 sorted() 메소드를 제공하고 있습니다.

sorted() 메소드는 Comparable을 구현하고 있는 객체, 기본형에 대해서만 정렬이 가능합니다. 물론 람다식을 통해 구현하지 않은 객체에 대해서도 정렬할 수 있습니다.

Comparable, Comporator 인터페이스에 대한 이야기가 나옵니다. 잘 모르신다면 이 포스트를 먼저 참조해주세요. 두 인터페이스에 대해 아셔야 조금 더 수월하게 이해하실 수 있습니다.

Comparable 구현 객체 정렬

Comparable 구현 객체를 정렬하는 방법은 sorted()를 이용하기만 하면 됩니다. 기본적으로 오름차순으로 정렬되며, 내림차순으로 정렬하고 싶다면 sorted()의 인자로 Comparator.reverseOrder()를 전달하면됩니다.

다음은 Comparable을 구현한 구현 객체 Animal입니다.

public class Animal implements Comparable<Animal> {
    private String species;
    private int numberOfLeg;

    public Animal(String species, int numberOfLeg) {
        this.species = species;
        this.numberOfLeg = numberOfLeg;
    }

    public String getSpecies() {
        return this.species;
    }

    public int getNumberOfLeg() {
        return this.numberOfLeg;
    }

    @Override
    public int compareTo(Animal animal) {
        return Integer.compare(numberOfLeg, animal.numberOfLeg);
    }
}

이 객체를 다리의 숫자를 기준으로 오름차순, 내림차순 정렬해보겠습니다.

public class Main {
    public static void main(String[] args) {
        List<Animal> list = new ArrayList<Animal>();

        list.add(new Animal("뱀", 0));
        list.add(new Animal("문어", 8));
        list.add(new Animal("고양이", 4));
        list.add(new Animal("닭", 2));

        Stream<Animal> stream1 = list.stream();
        //오름차순 정렬
        stream1.sorted().forEach(elem -> {
            System.out.println(elem.getSpecies() + " " + elem.getNumberOfLeg());
        });

        System.out.println();

        Stream<Animal> stream2 = list.stream();
        //내림차순 정렬
        stream2.sorted(Comparator.reverseOrder()).forEach(elem -> {
            System.out.println(elem.getSpecies() + " " + elem.getNumberOfLeg());
        });
    }
}

Comparable 미구현 객체 정렬

Comparable을 구현하지 않아도 sorted()로 정렬할 수 있습니다. 람다식으로 Comparator 인터페이스를 구현해서 sorted()에 전달하면 됩니다.

예시를 위해 이전 객체에서 Comparable 구현 부분을 제거했습니다.

public class Animal  {
    private String species;
    private int numberOfLeg;

    public Animal(String species, int numberOfLeg) {
        this.species = species;
        this.numberOfLeg = numberOfLeg;
    }

    public String getSpecies() {
        return this.species;
    }

    public int getNumberOfLeg() {
        return this.numberOfLeg;
    }
}

마찬가지로 오름차순, 내림차순 정렬을 수행한 예제 코드입니다. Comparator를 구현해서 전달하는 sorted() 부분을 주목해주세요.

public class Main {
    public static void main(String[] args) {
        List<Animal> list = new ArrayList<Animal>();

        list.add(new Animal("뱀", 0));
        list.add(new Animal("문어", 8));
        list.add(new Animal("고양이", 4));
        list.add(new Animal("닭", 2));

        Stream<Animal> stream1 = list.stream();
        //오름차순 정렬
        stream1.sorted((a1, a2) -> Integer.compare(a1.getNumberOfLeg(), a2.getNumberOfLeg()))
                .forEach(elem -> {
                    System.out.println(elem.getSpecies() + " " + elem.getNumberOfLeg());
                });

        System.out.println();

        Stream<Animal> stream2 = list.stream();
        //내림차순 정렬
        stream2.sorted((a1, a2) -> Integer.compare(a2.getNumberOfLeg(), a1.getNumberOfLeg()))
                .forEach(elem -> {
                    System.out.println(elem.getSpecies() + " " + elem.getNumberOfLeg());
                });
    }
}

0개의 댓글