본문 바로가기

Algorithm & problem solving

Map 인터페이스와 MyLinearMap 구현

반응형

Map 인터페이스

public interface Map<K,V> {
    int size();
    boolean isEmpty();
    boolean containsKey(Object key);
    boolean containsValue(Object value);
    V get(Object key);
    V put(K key, V value);
    V remove(Object key);
    void putAll(Map<? extends K, ? extends V> m);
    void clear();
    Set<K> keySet();
    Collection<V> values();
    Set<Map.Entry<K, V>> entrySet();

    interface Entry<K,V> {
    K getKey();
    V getValue();
    V setValue(V value);
    boolean equals(Object o);
    int hashCode();
    }
}

HashMap 클래스
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }


Map 인터페이스를 가지고 만들고 싶은 클래스에 implements Map을 통해 다양한 Map 클래스를 만들 수 있다.
MyLinearMap이라는 클래스를 만든다.

public class MyLinearMap<K, V> implements Map<K, V> {
    
    private List<Entry> entries = new ArrayList<Entry>();

          public class Entry implements Map.Entry<K, V> {
                   private K key;
                   private V value;
                   public Entry(K key, V value) {
                             this.key = key;
                             this.value = value;
                   }
                   @Override
                   public K getKey() {
                             return key;
                   }
                   @Override
                   public V getValue() {
                             return value;
                   }
                   @Override
                   public V setValue(V newValue) {
                             value = newValue;
                             return value;
                   }
          }

          @Override
          public void clear() {
                   entries.clear();
          }
          @Override
          public boolean containsKey(Object target) {
                   return findEntry(target) != null;
          }

          @Override
          public boolean containsValue(Object target) {
                   for (Map.Entry<K, V> entry: entries) {
                             if (equals(target, entry.getValue())) {
                                      return true;
                             }
                   }
                   return false;
          }
          @Override
          public Set<Map.Entry<K, V>> entrySet() {
                   throw new UnsupportedOperationException();
          }
          @Override
          public V get(Object key) {
                   // TODO: FILL THIS IN!
                   Entry entry = findEntry(key);
                   if(entry == null) {
                             return null;
                   }else {
                             return entry.getValue();
                   }                           
          }
          @Override
          public boolean isEmpty() {
                   return entries.isEmpty();
          }
          @Override
          public Set<K> keySet() {
                   Set<K> set = new HashSet<K>();
                   for (Entry entry: entries) {
                             set.add(entry.getKey());
                   }
                   return set;
          }
          
          @Override
          public V put(K key, V value) {
                   // TODO: FILL THIS IN!
                   Entry entry  = findEntry(key);
                   if(entry == null) {
                             entries.add(new Entry(key, value));
                             return null;
                   }else{
                             V oldValue = entry.getValue();                          
                             entry.setValue(value);
                             return oldValue;
                   }
          }
          @Override
          public void putAll(Map<? extends K, ? extends V> map) {
                   for (Map.Entry<? extends K, ? extends V> entry: map.entrySet()) {
                             put(entry.getKey(), entry.getValue());
                   }
          }
          @Override
          public V remove(Object key) {
                   // TODO: FILL THIS IN!
                   Entry entry = findEntry(key);
                   if(entry == null) {
                             return null;
                   }else {
                             V oldValue = entry.getValue();
                             entries.remove(entry);
                             return oldValue;
                   }
                   
          }
          @Override
          public int size() {
                   return entries.size();
          }
          @Override
          public Collection<V> values() {
                   Set<V> set = new HashSet<V>();
                   for (Entry entry: entries) {
                             set.add(entry.getValue());
                   }
                   return set;
          }

          /**
           * Returns the entry that contains the target key, or null if there is none.
           *
           * @param target
           */
          private Entry findEntry(Object target) {
                   // TODO: FILL THIS IN!
                   for(Entry entry: entries) {
                             if(equals(target, entry.getKey())) {
                                      return entry;
                             }
                   }
                   return null;
          }
          /**
           * Compares two keys or two values, handling null correctly.
           *
           * @param target
           * @param obj
           * @return
           */
          private boolean equals(Object target, Object obj) {
                   if (target == null) {
                             return obj == null;
                   }
                   return target.equals(obj);
          }

          /**
           * Returns a reference to `entries`.
           *
           * This is not part of the Map interface; it is here to provide the functionality
           * of `entrySet` in a way that is substantially simpler than the "right" way.
           *
           * @return
           */
          protected Collection<? extends java.util.Map.Entry<K, V>> getEntries() {
                   return entries;
          }

중요한것은 Map.Entry의 key와 value를 통해 Entry클래스를 사용하여 키,밸류로 저장하고 변경하고 지우고 값을 찾는 자료구조형이다.
메서드중에 채워넣어야할 것을(// TODO: FILL THIS IN!) 구현하여 MyLinearMap를 구현한다.

private 내부함수를 추가하고 내부함수를 호출하여 처리할 수 있다.
그리고 핵심 메서드의 성능을 분석하고 상수시간, 선형 계산 등 고려하여 알맞게 구현한다.


결론: 
여기에서 만든 MyLinearMap 클래스의 핵심 메서드는 모두 실행시간이 선형이다. 
엔트리 개수가 작다면 이 구현은 쓸만하다.
하지만, 핵심 메서드가 상수시간인  Map의 구현도 있다.

개선 방법:
1. 엔트리를 하나의 커다란 List에 저장하는 대신 다수의 작은 리스트로 쪼갠다.
각 키에 대해 해시 코드를 사용하여 어느 리스트를 사용할지 선택
2. 하나의 큰 리스트 대신 다수의 작은 리스트를 사용하는 것이 더 빠르지만, 증가 차수를 개선하지 못한다. 핵심 연산은 여전히 선형이다.
하지만 다른 묘수가 있다. 리스트의 개수를 늘려서 리스트당 엔트리 개수를 제한할 수 있다면 결과는 상수 시간 맵이 된다.

- 출처: Think Data Structures (자바로 배우는 핵심자료구조와 알고리즘) 저자: 앨런B.다우니 지음, 유동환 옮김-

반응형

'Algorithm & problem solving' 카테고리의 다른 글

MyLinearMap와 해싱  (0) 2018.08.17
연결리스트 알고리즘  (0) 2018.07.03
선택정렬 알고리즘  (0) 2018.07.03
컴퓨터 알고리즘  (0) 2018.05.18