ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 람다와 스트림 - 컬렉션과 함수형 인터페이스
    백엔드/자바 2021. 9. 1. 19:38

    JDK 1.8부터는 함수형 인터페이스가 추가됐고, 그와 더불어 컬렉션 프레임웍을 편하게 사용할 수 있도록

     

    컬렉션의 인터페이스에 디폴트 메서드가 추가됐다.

     

    그 중 일부는 함수형 인터페이스를 사용하는데 이번 파트에서 그러한 메서드들에 대해 설명한다.

     

    인터페이스 메서드 설명
    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, BiFunction<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(BiConsume<K,V> action) 모든 요소에 작업 action 수행
    void replaceAll(BiConsume<K,V,V> f) 모든 요소에 치환 작업 f를 수행

     

    Collection - removeIf()

     

    removeIf()는 조건에 맞는 요소를 컬렉션 내에서 지우는 역할을 한다.

     

    removeIf()는 Predicate의 참조 변수를 받아서 참조변수가 저장한 조건에 맞게 요소를 거른다.

     

    기존에 if()문을 쓰는 것보다 훨씬 코드도 짧아지고 편리하다.

     

    물론 위의 코드도 람다식을 쓰면 아래와 같이 더 간단히 표현이 가능하다.

     

     

     

    removeIf() 안에는 Predicate의 참조변수가 들어가야된다.

     

    그래서 첫 코드와 달리 아예 매개변수로 람다식을 전달해주면 형식만 맞는다면

     

    알아서 Predicate 인터페이스와 매칭되어 코드가 정상적으로 잘 작동한다.

     

    List - replaceAll()

     

    replaceAll()은 단항 연산을 하는 UnaryOperator의 참조변수를 매개변수로 전달하면

     

    내용에 맞춰 리스트의 모든 요소에 해당 참조변수의 연산 작업을 수행한다.

     

    이해가 안된다면 위의 코드를 보도록 하자.

     

    UnaryOperator의 참조변수는 전달받은 매개변수에 10을 더하는 연산을 시행한다.

     

    이 참조변수를 replaceAll()에 매개변수로 전달하면 리스트의 모든 요소에 10을 더한다.

     

     

     

    그리고 removeIf()에서 했던 것과 같이 간략하게 람다식을 전달해도 잘 작동한다.

     

    Iterable - forEach()

     

    forEach()는 Consumer 참조변수를 전달 받아 각 요소에 작업을 수행하는 역할을 한다.

     

    이것만 보면 replaceAll()과 비슷해 보인다.

     

    하지만 참조변수가 Consumer이므로 반환값이 없다는 것을 알고 사용해야 한다.

     

     

     

    물론  forEach()도 다른 메서드들과 마찬가지로 람다식을 매개변수로 전달할 수 있다.

     

     

     

    그리고 forEach()는 리스트 뿐 아니라 Map을 사용할 때도 유용하다.

     

    위와 같이 forEach()를 사용하면 key와 value를 쉽게 출력할 수 있기 때문이다.

     

    Map - compute()

     

    Compute()는 Map에서 value의 값을 바꿀 때 사용하는 메서드다.

     

    매개변수로 전달된 key가 Map에 존재하면 그 key에 저장된 value를 수정할 수 있게 도와준다.

     

    위의 코드처럼 key가 map에 존재한다면 거기에 저장된 value를 원하는대로 바꿀 수 있다.

     

     

     

    물론 위와 같이 compute() 메서드안에 바로 람다식을 전달하는 방법도 있다.

     

     

     

    만약 여러 개의 key의 value를 변경하고 싶다면 위와 같이 만들어줄 수도 있다.

     

    forEach()를 통해 map의 각 요소들을 순서대로 꺼낸다고 가정했을 때,

     

    이때 제공되는 key를 compute()에 key로 전달하는 것이다.

     

    그렇게하면 일일이 key를 입력해줄 필요없이 편리하게 value를 변경할 수 있다.

     

     

     

    그리고 마지막으로 value의 값을 매번 다른 값으로 바꾸고 싶다면 위와 같이 해도 된다.

     

    forEach()가 key를 순서대로 제공할 때 key를 받는다.

     

    그리고 그 key를 인덱스 넘버로 사용해 list에서 값을 꺼내와서 value를 변경할 수도 있다.

     

    Map - computeIfAbsent()

    computeIfAbsent()는 만약 제공된 key가 Map안에 없을 때,

     

    새로운 key를 만들고 그 안에 Function 참조변수의 반환값을 저장하는 역할을 한다.

     

    위 코드의 경우 4가 없기 때문에 4를 key로 하여 value에는 Function의 반환값을 저장했다.

     

    그 다음 해당 key:value를 map에 추가했다.

     

    만약 computeIfAbsent()에 제공된 key가 map 안에 있을 경우에는 어떤 작업도 수행하지 않는다.

     

    Map - computeIfPresent()

     

    computeIfPresent()는 만약 제공된 key가 map에 있다면,

     

    key에 해당하는 value에 BiFunction의 작업을 수행하는 것이다.

     

    이것만 보면 compute()와는 별 차이가 없어 보인다.

     

    왜냐하면 compute()도 제공된 key가 map안에 있다면

     

    key에 해당하는 value에 BiFuntion의 작업을 수행하기 때문이다.

     

    그런데 둘은 아주 큰 차이가 하나 있다 바로 key가 map에 존재하지 않을 때다.

     

     

     

    우선 compute()를 다시 한번 실행해보자.

     

    위 상황에서 compute()에 4를 key로 전달했다. 그런데 4는 Map안에 존재하지 않는다.

     

    그럴때 compute()는 새로운 key인 4를 만들어서 BiFunction의 반환값을 value로 저장한다.

     

    오히려 이런 점을 보면 compute()는 computeIfAbsent()과 비슷해 보인다.

     

    하지만 compute()는 key가 map안에 존재하든 안하든 BiFunction을 실행하여 값을 Map에 저장한다.

     

    반면 computeIfAbsent()는 key가 map안에 존재할 경우 Function을 수행하지 않는다.

     

     

     

    이제 다시 computeIfPresent()로 돌아와서 보면 compute()와 차이를 알 수 있다.

     

    compute()는 제공된 key가 Map 안에 없을 때 새로운 key과 value를 만든다.

     

    하지만 computeIfPresent()는 제공된 key가 Map 안에 없으면 동작하지 않음을 알 수 있다.

     

    Map - merge()

     

    merge()는 기존에 key에 저장되있던 value를 변경하는 기능을 수행한다.

     

    merge()에 key와 특정 값을 매개변수로 제공하면,

     

    key의 value와 특정 값이 BiFuntion의 매개변수로 전달된다.

     

    그리고 BiFunction을 거친 반환값은 처음에 merge()에 제공한 key의 value에 저장된다.

     

    내용 이해가 힘들다면 아래의 흐름도를 보도록 하자.

     

     

     

    이전 코드와 같은 코드인데 이번엔 코드의 흐름을 그려봤다.

     

    이전에 설명한 것처럼 merge()에 주어진 key의 value와 제공한 특정 값을 BiFunction에 전달된다.

     

    그리고 BiFunction은 두 숫자를 매개변수로 받아서 합쳐서 반환한다.

     

    그리고 merge()는 BiFunction이 반환한 숫자를 처음 제공한 key가 가진 value에 새로 저장한다.

     

    이제 대충 설명을 보면 어떤 식으로 흐름이 전개되는 지는 알 수 있을 것이다.

     

    하지만 뭔가 의문이 들 것이다. 쓸데없이 메서드의 구조가 복잡하다는 것이다.

     

    그리고 이렇게 할거라면 다른 메서드를 사용하는 것이 더 편해보일 수 있다.

     

    왜 그럴까?

     

     

     

    merge()는 흔히 두 개의 Map의 값을 합쳐서 계산하는데 쓰이기 때문이다.

     

    일반적으로 위와 같은 용도로 쓰인다.

     

    설명하자면, map1의 key를 순서대로 받아서 merge()에 전달한다.

     

    그리고 나서 map1의 key의 value와 map2의 key의 value를 BiFunction으로 전달한다.

     

    그리고나서 BiFunction에서 두 값을 합치고 merge()는 이 반환값을 map1의 key의 value에 저장한다.

     

    흐름이 이해가 안간다면 아래 흐름도를 보도록 하자.

     

     

     

    map1의 key가 순서대로 merge()에 전달된다.

     

    해당 key의 value와 map2에서 해당 key와 동일한 key의 value가 BiFunction으로 전달된다.

     

    BiFunction은 두 value를 받아서 더하고 반환한다.

     

    merge()는 반환 받은 값을 map1으로부터 받은 key의 value에 저장한다.

     

    그렇게 더하다보면 '100+50 -> 150' , '200+100 -> 300', '300+150- > 450' 순서로 계산된다.

     

    그리고 최종적으로 map1의 값은 150, 300, 450이 된다.

     

    즉 이런 식으로 map1과 map2의 value가 합쳐진 것이다.

     

     

     

    그리고 당연히 merge() 안에 들어가는 BiFunction을 람다로 변경이 가능하다.

     

    위 코드와 같이 한 줄로 간략하게 표현이 가능하다.

     

    Map - replaceAll()

     

    replaceAll()은 컬렉션의 모든 요소를 순회하며 미리 BiFunction 매개변수에 지정한 동작을 실행한다.

     

    위의 예시의 경우 map의 key값을 순회하며 value값을 변경해주는 코드다.

     

    미리 BiFunction에 map의 key, value를 매개변수로 전달받아 어떻게 동작할지 지정한다.

     

    그리고 이 BiFunction의 참조변수를 replaceAll()의 매개변수로 전달하면 위와 같이 작동한다.

     

     

     

    그리고 당연히 BiFunction 참조변수의 값을 람다식으로 replaceAll()에 전달해도 작동한다.

Designed by Tistory.