자바
Predicate, 컬렉션 프레임웍에서 함수형 인터페이스
망재이
2023. 3. 11. 22:26
- Predicate와 논리 연산자들을 연결해서 하나의 식을 구성할 수 있는데 && -> and(), || -> or(), ! -> negate()로 연결할 수 있음
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2 == 0;
Predicate<Integer> notP = p.negate(); // i>=100;
Predicate<Integer> all = notP.and(q.or(r));
// i>=100 && i<200 || i%2==0 ;
System.out.println(all.test(150));
// 150>=100 && 150<200 || 150%2==0 ;
- isEqual()도 역시 사용할 수 있는데, 이는 str1.equals(str2)과 같음
Predicate <String> p = Predicate.isEqual(str1);
boolean result = p.test(str2);
// 같은 뜻!
boolean result = Predicate.isEqual(str1).test(str2)
[ 활용 ]
package java14;
import java.util.function.Function;
public class Predicate {
public static void main(String[] args) {
// andThen 메소드 -> 두 개의 함수를 더해주는 메소드
// A함수의 출력 메소드와 B함수의 입력 메소드가 같아야 함
// compose 메소드 -> andThen과 같은 기능이지만 위치만 다른 것!(둘의 위치만 다른 것이므로 그냥 andThen만 기억해도 무방)
Function<String, Integer> f = (s) -> Integer.parseInt(s, 16); //문자열을 16진수로 변환
Function<Integer, String> g = (i) -> Integer.toBinaryString(i); //정수를 2진수 문자열로 변환
Function<String, String> h = f.andThen(g);
Function<Integer, Integer> h2 = f.compose(g);
System.out.println(h.apply("FF")); //"FF" -> 255 -> "11111111" f를 거쳐서 g로 반환
System.out.println(h2.apply(2)); //2 -> "10" -> 16
Function<String, String> f2 = x -> x; //항등함수(identify function) 매개변수 반환값 같음
System.out.println(f2.apply("ABC")); //"ABC"출력
java.util.function.Predicate<Integer> p = i -> i < 100;
java.util.function.Predicate<Integer> q = i -> i < 140;
java.util.function.Predicate<Integer> r = i -> i%2 == 1;
java.util.function.Predicate<Integer> notP = p.negate();
java.util.function.Predicate<Integer> all = notP.or(q.and(r));
//i>=100 || (i<140 && i%2==1)
System.out.println(all.test(90));
//90>=100 || (90<140 && 90%2==1)
//F || (T && F) -> F
String str = new String("abc");
String str2 = "abc";
// -> true 왜냐하면 Predicate.isEqual메소드는 equals와 같기 때문에 주소값을 비교하는게 아니라 값을 비교
boolean result = java.util.function.Predicate.isEqual(str).test(str2);
System.out.println(result);
}
}
- 함수형 인터페이스가 빛을 발하는 순간은 컬렉션 프레임워크와 함께 사용할 때 같다. 그전까진 람다식을 어디서 어떻게 왜 써야하는건지 의문이였는데 이 파트 공부를 하고나서 그런 생각이 완벽하게 깨졌다. 람다식은 꼭 필요한 녀석이다,,,!
인터페이스 | 메서드 | 설명 |
Collection | boolean removeIf(Predicate<E> filter) | 조건에 맞는 요소를 삭제 |
List | void replaceAll(UnaryOperator<E> operator) | 모든 요소를 변환하여 대체 |
Iterable | void forEach(Consumer<T> action) | 모든 요소에 작업 action을 수행 |
Map | V compute(K key, BiFunction<K, V, V> f) | 지정된 키의 값에 작업 f를 수행 |
V computeIfAbsent(K key, Function<K, V> f) | 키가 없으면, 작업 f 수행 후 추가 | |
V computeIfPresent(K key, Bifunction<K, V, V> f) | 지정된 키가 있을 때, 작업 f 수행 | |
V merge(K key, V value, BiFunction<V, V, V> f) | 모든 요소에 병합작업 f를 수행 | |
void forEach(BiConsumer<K, V> action) | 모든 요소데 작업 action을 수행 | |
void replaceAll(BiFunction<K, V, V> f) | 모든 요소에 지환작업 f를 수행 |
[ 활용 ]
package java14;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class CollectionRamda {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(i);
}
//list의 모든 요소 출력
list.forEach(i -> System.out.println(i+" "));
//iterator를 이용해서 list출력 -> 람다식을 이용해서 한 줄로 줄일 수 있음!
Iterator it=list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//2 또는 3의 배수를 삭제
list.removeIf(x -> x%2==0 || x%3==0);
System.out.println(list);
//list의 모든 요소에 10 곱함
list.replaceAll(i -> i*10);
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("1","a");
map.put("2","b");
map.put("3","c");
map.put("4","d");
map.forEach((k,v) -> System.out.println("["+k+", "+v+"]"));
//iterator이용 -> 람다식을 이용해서 한 줄로 표현
Iterator iterator = map.entrySet().iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
- 좀 더 구체적으로 Map의 메소드들을 살펴보자
- computeIfAbsent : 키가 없으면 작업 수행 후 추가
map.put("사과",1);
map.put("딸기",1);
map.computeIfAbsent("사과",(key) -> key.length());
map.computeIfAbsent("포도",(key) -> key.length());
System.out.println(map);
//{포도=2, 사과=1, 딸기=1}
- computeIfPresent : 키가 존재하면 작업 수행, 존재하지 않으면 수행하지 않음
map.computeIfPresent("사과", (key, value) -> value + 1);
map.computeIfPresent("멜론", (key, value) -> value + 1);
System.out.println(map);
//{포도=2, 사과=2, 딸기=1}
- compute : 키가 존재하든, 존재하지 않든 작업 수행.
map.compute("사과", (key, value) -> value+1);
map.compute("멜론", (key, value) -> value+1);
System.out.println(map);
//nullpointexception 에러 -> 멜론의 value값이 null인 상태로 값이 들어왔는데 +1 연산을 수행해서 예외발생
- merge : 위에서 구현한 내용들을 한 줄로 구현할 수 있게 해줌.
- 첫 번째 인자는 key를 받는다.
- 두 번째 인자는 key가 존재하지 않을 경우 삽입될 value를 받는다.
- 세 번째 인자는 key가 존재할 경우 remapping할 BiFunction을 받는다
첫 번째 인자인 key가 맵에서 존재하지 않으면 두 번째 인자와 함께 map에 삽입되고
key가 맵에서 존재하면 세 번째 인자인 BiFunction의 연산 결과를 value에 remapping한다.
Key가 맵에서 존재할 때 BiFunction의 연산 결과가 null이면 map에서 삭제된다.
map.merge("사과",3,(value, putValue) -> value + 1);
map.merge("멜론",1,(value, putValue) -> value + 1);
map.merge("딸기",3,(value, putValue) -> value + 2);
System.out.println(map);
//{포도=2, 멜론=1, 사과=3, 딸기=3}
728x90