상세 컨텐츠

본문 제목

[Java] 5. 컬렉션에 정의된 함수형 인터페이스

프로그래밍 언어/Java(자바)

by Guroo 2023. 8. 17. 10:32

본문

자바에서 많이 사용되는 클래스인 ArrayList, HashMap과 같은 Collection Framework의 Interface에 여러 추상 Method가 추가되었다. 이번에는 List, Map 등에 정의된 Method 중 사용 빈도가 높은 메소드 위주로 살펴보자.

 

5-1. Collection 인터페이스에 정의된 Method

 

Collection 인터페이스에서 자주 사용되는 메소드는 removeIf() 가 있는데, 이 메소드는 List와 Set의 상위 인터페이스이기 때문에 아래의 메소드는 하위 인터페이스에서도 동일하게 존재하는 메소드이다.

 

메소드 설명
Boolean removeIf(Predicate<? super E> filter) 전달인자로 주어진 predicate에 충족되는 컬렉션의 모든 요소를 삭제

 

코드를 통해 사용법을 알아보자.

 

RemoveIfTest.java

import java.util.ArrayList;
import java.util.List;

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

        List<Integer> list = new ArrayList<Integer>();
        for(int i=0; i<20; ++i)
            list.add((int)(Math.random()*50));

        System.out.println("원본: " + list);

        list.removeIf(x -> !(x % 3 == 0 || x % 8 == 0));

        System.out.println("3이나 8의 배수: " + list);
    }    
}

 

위의 코드는 0~50 사이의 정수 난수를 20개 발생시켜 ArrayList에 저장한 후 발생된 난수 중 3이나 8의 배수를 남기고 삭제한다.

removeIf()는 전달인자로 Predicate<? super E> filter를 전달하도록 되어 있기 때문에

앞에서 살펴본 Predicate 함수형 인터페이스에 정의된 test() 메소드는 하나의 데이터를 전달받아 조건을 판단하여 Boolean 타입으로 반환하므로 removeIf() 메소드의 전달인자로 Predicate 인터페이스에 맞게 전달하고자 하면

 

(x) -> !(x%3 == 0 || x%8 == 0)

 

과 같이 하나의 값을 전달하고 전달된 데이터값의 연산 결과가 Boolean 타입이 되도록 작성하면 된다.

 

5-2. List에 정의된 Default 메소드

List 인터페이스에 정의된 주요 default를 살펴보면 표와 같다.

 

메소드 설명
void replaceAll(UnaryOperator<E> operator) List에 정의된 모든 요소를 변환
void sort(Comparator<? super E> c) List에 정의된 모든 요소를 정렬
Spliterator<E> spliterator() 병렬작업이 가능한 요소 탐색기능 제공하는 인터페이스. 기본적으로 Iterator와 유사하다.
Collection으로부터 상속받은 메소드이므로 Collection의 하위 인터페이스에는 모두 존재

 

코드를 통해서 메소드들을 살펴보자

 

먼저, replaceAll()은 메소드 명에서도 알 수 있듯 전달인자로 함수형 인터페이스인 UnaryOperator 의 Lambda 식을 전달받아 해당 데이터 값을 다른 값으로 변환하는 메소드이다.

 

RemoveAllTest.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ReplaceAllTest {
    public static void main(String[] args) {
        String str = "송아지 송아지 얼룩 송아지 엄마소도 얼룩소 엄마 닮았네 ㅎ";
        List<String> strList = new ArrayList<>(Arrays.asList(str.split(" ")));

        System.out.println(strList);

        strList.removeIf((x) -> x.equals("ㅎ"));
        System.out.println(strList);

        strList.replaceAll(x -> x.replace("송아지", "강아지"));
        strList.replaceAll(x -> x.replace("소", "개"));

        System.out.println(strList);
    }
}

 

위의 내용 중 아래의 코드는 

List<String> strList = new ArrayList<>(Arrays.asList_(str.split(" ")));

 

문자열에 저장되어 있는 데이터를 String 클래스에 정의된 split() 메소드를 이용해 배열로 분리한다.

 

생성된 배열을 Arrays.asList() 메소드에 전달하여 List로 변환하는 것이 두 번째 작업이다.

이렇게만 strList에 대입시킬 수 있으나, 한 가지 과정을 더 처리했는데, 그것은 ArrayList객체의 생성자로 전달하는 부분이다.

 

왜 이런 형태로 ArrayList 객체를 생성하였을까?

 

이것은 Arrays.asList() 메소드로 생성된 List 데이터는 add와 같이 데이터의 내용을 삽입하여 크기를 수정하는 작업을 하면 UnsupportedOperationException이 발생하기 때문이다.

 

코드 중간에 List 인터페이스로부터 상속받은 removeIf() 메소드를 사용하는데 이 메소드는 List 내의 데이터를 삭제하여 리스트의 크기가 수정되도록 하는 코드이기 때문에 해당 코드에서 UnsupportedOperationException 발생을 막기 위해 다시 ArrayList 클래스를 생성한 것이라고 보면 된다.

 

어쨌든 이렇게 생성된 데이터의 가장 마지막에 삽입된 “ㅎ” 문자열을 삭제하기 위해 Collection 인터페이스로부터 상속된 removeIf()를 사용해 보았다.

 

다음으로 replaceAll() 메소드이다.

위에서 언급한 것처럼 replaceAll() 메소드는 함수형 인터페이스인 UnaryOperator 내의 apply() 메소드(Function 인터페이스로부터 상속받은 메소드)가 R apply(T t) 형태로 선언되어 있어 이에 맞도록 Lambda 식을 전달해야 한다.

 

x -> x.replace("송아지", "강아지")

 

위 메소드는 전달된 문자열을 다른 문자열로 변환하는 람다식이다. 이 람다식은 replayAll() 메소드에 전달할 수 있는 형태를 갖췄으므로

 

strList.replaceAll(x -> x.replace("송아지", "강아지"))

 

로 코드가 작성되었고, 결과적으로 “송아지”는 “강아지”로 변경되고.

 

strList.replaceAll(x -> x.replace("소", "개"));

 

위의 코드를 거치게 되면 List 데이터의 “소”는 “개”로 변경되어 마지막 출력 결과는

 

[강아지, 강아지, 얼룩, 강아지, 엄마개도, 얼룩개, 엄마, 닮았네]

 

가 되는 것이다.


이번에 살펴볼 메소드는 sort() 이다.

 

sort() 메소드는 이전 내용의 코드 마지막에 예제가 있는데, 메소드명 그래도 주어진 데이터를 정렬하는 일을 수행한다.

sort() 메소드의 전달인자인 Comparator.인터페이스는 함수형 인터페이스이며 추상메소드로 compare()를 정의하고 있다.

 

@FunctionalInterface
public interface Comparator

 

compare() 메소드는 2개의 데이터를 전달받아 정수형 결과값을 반환하는데, 두 개의 데이터의 차이 값을 반환하도록 구현하면 된다.

 

int compare(T o1, T o2)

 

아래의 코드를 살펴보자.

 

SortTest.java

import java.util.Arrays;
import java.util.List;

public class SortTest {
	public static void main(String[] args) {
		String lunch = "순대국 카레라이스 부대찌게 돈가스 짜장면 김밥 햄버거";
		List<String> lunchList = Arrays.asList(lunch.split(" "));
		
		List<Integer> numList = Arrays.asList(7, 9, 2, 4, 1, 34,12, 98, 22);
		
		System.out.println(lunchList);
		lunchList.sort((a, b) -> b.compareTo(a));
		numList.sort((a, b) -> a - b);
		
		System.out.println(lunchList);
		System.out.println(numList);
	}
}

 

앞서 살펴본 코드와 유사하게 문자열로 정의된 값을 분리(splite() 한 후 Arrays.asList()로 전달하여 List 타입의 데이터로 변경하였다.

이 List 데이터에 정의된 sort() 메소드의 전달인자로 람다식을 사용하였는데, 이것은 sort() 메소드가 설명한 대로 Comparator 함수형 인터페이스 타입으로 전달받기 때문이다.

 

그러므로 sort() 메소드는 두 개의 값을 전달하여 한 개의 정수값을 반환하는 람다식을 작성하여 전달하면 되는데, 그 코드가 바로

 

lunchList.sort((a, b) -> a.compareTo(b));

 

이다.

 

이 때 전달된 변수인 a, b는 문자열이므로(lunchchList가 문자열 List이므로) a – b는 할 수 없고, 두 값을 비교하여 코드값의 차이를 반환하는 String 클래스의 compareTo()를 호출하였다.

 

실행결과는 lunchList의 데이터가 오름차순으로 정렬된 것인데, 만약 내림차순으로 정렬하고 싶다면

 

lunchList.sort((a, b) -> b.compareTo(a));

 

로 a, b의 위치를 변경하면 된다.

 

sort를 호출하는 List 데이터가 숫자형일 경우 값의 차이를 반환하면 된다.

numList.sort((a, b) -> a - b);

 

두 값의 차이를 반환하도록 하여 정렬할 수 있다.

 

글이 길어지므로 이번에는 여기에서 마치고 다음에는 Map에서 사용할 수 있는 람다식을 살펴보도록 하자.

 


 

이전 글: 4. java.util.function에 정의된 함수형 인터페이스

 

다음 글: 6. Map에 정의된 추상 메소드와 default 메소드

관련글 더보기

댓글 영역