모던 자바 인 액션 - Chap 08. 컬렉션 API 개선

doxxx·2022년 6월 11일
0

모던자바인액션

목록 보기
5/14
post-thumbnail

8.1 컬렉션 팩토리

Unmodifiable Collections

abstract static class AbstractImmutableCollection<E> extends AbstractCollection<E> {

    // all mutating methods throw UnsupportedOperationException
    @Override
    public boolean add(E e) {
        throw uoe();
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        throw uoe();
    }

    @Override
    public void clear() {
        throw uoe();
    }

    @Override
    public boolean remove(Object o) {
        throw uoe();
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        throw uoe();
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        throw uoe();
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        throw uoe();
    }
}

An unmodifiable collection is a collection, all of whose mutator methods (as defined above) are
specified to throw UnsupportedOperationException. Such a collection thus cannot be modified by
calling any methods on it. For a collection to be properly unmodifiable, any view collections
derived from it must also be unmodifiable.

ImmutableCollections 라고도 불리는 불변 컬렉션은 정의대로 모든 변경자 메소드를 예외를 던지는 상태로 정의되어 있다.

그러므로 이러한 컬렉션은 변경자 메소드를 호출하여 변경할 수 없다. 이러한 컬렉션이 제대로 불변이면 불변 컬렉션으로 부터 부터 상속된 모든 뷰 컬렉션도 불변이다.

List.subLsit도 불변임 ㅋㅋ

8.1.1 리스트(List) 팩토리 메서드

List.of 팩토리 메소드를 이용하여 리스트를 만들 수 있다.

public interface List<E> extends Collection<E> {

    // 10개 까지는 이렇게 생김
    static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
        return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
            e6, e7, e8, e9, e10);
    }

    // 10개 이상의 요소를 가진 리스트를 만들 경우
    static <E> List<E> of(E... elements) {
        switch (elements.length) { // implicit null check of elements
            case 0:
                @SuppressWarnings("unchecked")
                var list = (List<E>) ImmutableCollections.EMPTY_LIST;
                return list;
            case 1:
                return new ImmutableCollections.List12<>(elements[0]);
            case 2:
                return new ImmutableCollections.List12<>(elements[0], elements[1]);
            default:
                return ImmutableCollections.listFromArray(elements);
        }
    }

    // null 요소를 금지함
    List12(E e0) {
        this.e0 = Objects.requireNonNull(e0);
        // Use EMPTY as a sentinel for an unused element: not using null
        // enables constant folding optimizations over single-element lists
        this.e1 = EMPTY;
    }

    List12(E e0, E e1) {
        this.e0 = Objects.requireNonNull(e0);
        this.e1 = Objects.requireNonNull(e1);
    }

}

list.of()메서드를 통해서는 an unmodifiable list(ImmutableCollections)가 만들어진다.

8.1.2 집합(Set) 팩토리 메서드

8.1.3 맵(Map) 팩토리 메서드

Map.of() 메서드는 Entry(KeyValueHolder)를 이용하여 맵을 만들 수 있다.

public interface Map<K, V> {


    static <K, V> Entry<K, V> entry(K k, V v) {
        // KeyValueHolder checks for nulls
        return new KeyValueHolder<>(k, v);
    }
}

Quiz 8-1

public class Quiz8_1 {

    public static void main(String[] args) {
        List<String> actors = List.of("Keanu", "Jessica");
        actors.set(0, "Brad");
        System.out.println(actors);
    }
}
/* 출력 결과
 * Exception in thread "main" java.lang.UnsupportedOperationException
 * at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
 * at java.base/java.util.ImmutableCollections$AbstractImmutableList.set(ImmutableCollections.java:260)
 * */

8.2 리스트와 집합 처리

  • removeIf(): Collection 인터페이스의 default Method이다.
    • Collection 인터페이스의 메서드이기때문에, List, Set을 구현하거나 그 구현을 상속 받은 모든 클래스에서 이용할 수 있다.
    • 책의 코드 패턴과 동일하다.
public interface Collection<E> extends Iterable<E> {

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
  • replaceAll(): List 인터페이스의 default Method이다.
    • 알아서 Iterator 객체로 원하는 작업을 한다.
public interface List<E> extends Collection<E> {

    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }
}

8.3 맵 처리

public interface Map<K, V> {

    // forEach
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

    // comparingByKey
    public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
        Objects.requireNonNull(cmp);
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
    }

    // comparingByValue
    public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
        Objects.requireNonNull(cmp);
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
    }

    // getOrDefault
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

}

8.3.1 forEach 메서드

  • BiConsumer 키와 값을 인수로 받는다.

8.3.2 정렬 메서드

코테에서 간간이 쓸만함

8.3.3 getOrDefault 메서드

  • 키가 존재하더라도 값이 null이면 getOrDefault가 null 을 반환 할 수있다.

8.3.4 계산패턴

  • computeIfPresent
public interface Map<K, V> {

    default V computeIfAbsent(K key,
        Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }

    default V computeIfPresent(K key,
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                // 값을 만드는 함수가 Null을 반환하면 현재 매핑을 맵에서 삭제함
                remove(key);
                return null;
            }
        } else {
            return null;
        }
    }
}

8.3.5 삭제 패턴

  • 매핑을 제거할 때 오버라이드하여 사용한다.
    • remove(key)만이 아닌 remove(key,value)를 통하여 contains(key) && equals(get(key),value)를 대체할 수 있다.
public interface Map<K, V> {

    default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }
}

8.3.6 교체 패턴

  • replaceAll(): 앞서 다루었던 List 인터페이스의 replaceAll() 메서드와 비슷하다.
  • replace(): 키가 존재하면 값을 바꾼다.

8.3.7 합침

중복된 키가 없으면 putAll 메서드를 사용하면 된다.

하지만 중복된 키가 있으면 merge() 메서드를 사용한다.

Quiz 8-2

public class Quiz8_2 {

    public static void main(String[] args) {
        Map<String, Integer> movies = new HashMap<>();
        movies.put("JamesBond", 20);
        movies.put("Matrix", 15);
        movies.put("Harry Potter", 5);
        Iterator<Map.Entry<String, Integer>> iterator = movies.entrySet().iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            if (entry.getValue() < 10) {
                iterator.remove();
            }
        }
        System.out.println(movies);

        movies.clear();

        movies.put("JamesBond", 20);
        movies.put("Matrix", 15);
        movies.put("Harry Potter", 5);

        movies.entrySet().removeIf(entry -> entry.getValue() < 10);
        System.out.println(movies);
        // {Matrix=15, JamesBond=20}
    }
}

8.4 개선된 ConcurrentHashMap

잘 모르겠다..

0개의 댓글

관련 채용 정보