개발공부/JAVA

Java(자바) - Collection&람다식2 : 함수형 인터페이스의 활용

HyunJng 2022. 8. 30. 18:56

Java.util.function 패키지


람다식을 사용할 때 함수형 인터페이스를 어차피 익명으로 사용하기 때문에 매개변수와 return값만 중요하므로 일반적으로 자주 쓰이는 형식의 메서드를 제네릭 타입의 함수형 인터페이스로 미리 구현해두었다.

매개변수와 반환값에 주목하며 살펴보자.

 

매개변수가 하나인 함수형 인터페이스

함수형 인터페이스  메서드 설명
java.lang.Runnable void run() 매개변수 X, 반환값 X
Supplier<T> T get()  매개변수 X, 반환값 O
Consumer<T> void accept(T t) 매개변수 O, 반환값 X
Function<T,R> R apply(T t) 매개변수 O, 반환값 O
Predicate<T> boolean test(T t) 매개변수 O, 반환타입 boolean

 

매개변수가 두개인 함수형 인터페이스

접두사 Bi가 붙는다.

함수형 인터페이스  메서드 설명
BiConsumer<T, U> void accept(T t, U u) 매개변수 O, 반환값 X
BiFunction<T, U, R> R apply(T t, U u) 매개변수 O, 반환값 O
BiPredicate<T, U> boolean test(T t, U u) 매개변수 O, 반환타입 boolean

 

Function의 변형

매개변수 타입과 반환 타입이 동일하다는 점을 제외하고 Function과 동일하다.

함수형 인터페이스 메서드 설명
UnaryOperator<T> T apply(T t) Function의 자손
Function에서 매개변수와 반환 타입이 동일할 때 사용한다.
BinaryOperator<T> T apply(T t, T t) BiFunction의 자손
BiFunction에서 매개변수와 반환 타입이 동일할 때 사용한다.

 

매개변수가 세 개 이상은 직접 만들어서 써야한다.

 

함수형인터페이스 사용 예제

import java.util.function.*;
import java.util.*;

public class LamdaEx5 {
	public static void main(String[] args) {
		Supplier<Integer> s = () -> (int)(Math.random() * 100) + 1;
		Predicate<Integer> p = i -> i%2 == 0;
		Consumer<Integer> c = i -> System.out.print(i + ", ");
		Function<Integer, Integer> f = i -> i/10 * 10;
		
		List<Integer> list = new ArrayList<Integer>();
		// 랜덤한 값을 10개 넣어줌
		makeRandomList(list, s);
		System.out.println(list);
		
		//짝수 조건식만 출력
		printEvenNum(p, c, list);
		
		// 기본 list에서 일의 자리 수는 버린 새로운 list 생성
		List<Integer> newList = doSomething(list, f);
		System.out.println(newList);
		
	}

	// 리스트를 받고, 특정 행위를 한 뒤 새 리스트를 반납하는 함수
	static <T> List<T> doSomething(List<T> list, Function<T, T> f) {
		List<T> newList = new ArrayList<T>(list.size());
		
		for(T i: list) {
			newList.add(f.apply(i));
		}
		return newList;
	}

	// 리스트를 받고 리스트 내용 중 특정 조건만 출력하는 메서드
	static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
		System.out.print("[");
		for(T i: list) {
			if(p.test(i))
				c.accept(i);
		}
		System.out.println("]");
	}

	// 받은 리스트 안에 특정한 조건의 값을 10개 넣어주는 메서드
	static <T> void makeRandomList(List<T> list, Supplier<T> s) { // 매개변수 X, return O
		for (int i = 0; i < 10; i++)
			list.add(s.get());
	}

}

랜덤이라 돌릴때마다 숫자는 바뀐다.

 

컬렉션 프레임워크와 함수형 인터페이스


컬렉션 프레임 웍의 인터페이스에 다수의 디폴트 메서드가 추가되었는데, 그 중의 일부는 함수형 인터페이스를 매개변수로 받고 있기 때문에 람다식을 안에 넣을 수 있다.

인터페이스 메서드 설명
Collections boolean removeIf(Predicate<E> filter) filter식이 true인 요소를 삭제
List void replaceAll(UnaryOperator<E> operator) 모든 요소에 operation 작업 후 다시 넣어줌
Iterable void forEach(Consumer<T> action) 모든 요소에 작업 action을 수행
Map V compute(K key, BiFunction<K, V, V> f) 지정된 키의 값(value)에 작업 f를 수행
V computeIfAbsent(K key, Function<K, V> f) 키가 없으면, 작업 f를 수행 후 추가
V computeIfPresent(K key, BiFunctio<K, V, V> f) 키가 있으면, 작업 f를 수행
V merge(K key, V value, BiFunction<V, V, V> fv) 모든 요소에 병합작업 f를 수행
void forEach(BiConsumer<K, V> action) 모든 요소에 작업 action을 수행
void replaceAll(BiFunctio<K, V, V> f) 모든 요소에 치환작업 f를 수행

* 맵의 compute는 value를 변환하는 일, merge는 병합하는 일.

* list / set의 경우 람다식의 매개변수에는 원소값이 들어가고
  map의 경우는 람다식에 key나 value가 들어간다.

 

Collection에서의 람다식 사용 예제

import java.util.*;

public class LamdaEx4 {
	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		for(int i = 10; i > 0; i--)
			list.add(i);
            
		// 모든 요소를 출력 ( i에는 list에 저장된 값을 하나씩 대입)
		System.out.print("list.forEach의 결과: ");
		list.forEach(i -> System.out.print(i + ","));//consumer : 매개변수 O, return X
		
		// list의 2또는 3의 배수를 제거
		list.removeIf(x-> x%2 == 0 || x%3 == 0);
		System.out.print("\nremoveIf의 결과: "+list);
		
		// list의 각 요소에 10을 곱함
		list.replaceAll(i -> i * 10);
//		list.forEach(i -> i * 10);	// 안되는 이유: forEach에는 다시 넣어주는 기능은 없기 떄문.
		System.out.print("\nreplaceAll의 결과: "+list);
		
		
		Map<String, String> map = new HashMap<String, String>();
		map.put("1", "1");
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", "4");
		
		//map의 모든 요소를 {k, v} 형식으로 출력
		System.out.print("\nmap.forEach의 결과: ");
		map.forEach((i, j) -> System.out.print("{" + i + ", " + j + "}, "));
	}
}

 

+) 기본형을 사용하는 함수형 인터페이스


위의 함수형 인터페이스는 모두 반환값과 매개변수 타입이 제너릭 타입이었는데, 기본형 타입의 값을 처리할 때도 Wrapper클래스를 사용해왔다. 그러나 기본형은 기본형으로 처리할 때 더 효율적이므로 기본형을 사용하는 함수형 인터페이스도 제공한다.

함수형 인터페이스 메서드 설명
DoubleToIntFunction int applyAsInt(double d) AToBFunction은
입력이 A타입, 출력이 B타입
ToIntFunction<T> int applyAsInt(T value) ToAFunction은
입력은 제네릭타입, 출력은 A타입
IntFunction<R> R apply(T t, U u) AFunction은
입력은 A타입, 출력은 제네릭타입
ObjIntConsumer<T> void accept(T t, U u) ObjAFunction은
입력이 T, A타입이고 출력은 없다.

그냥.. 이런것도 있다 정도만 알아두자.