본문 바로가기

프로그래밍/Java

Vector와 Hashtable은 왜 안쓰일까?

반응형

학습목표

  • Vector와 대체제는 무엇이 있을까??
  • Vector와 대체제의 구별되는 특징
  • 그래서 Vector를 왜? 안써야 하는가?
  • Hashtable과 대체제는 무엇이 있을까?
  • Hashtable과 대체제의 구별되는 특징
  • 그래서 Hashtable은 왜? 안써야 하는것일까?

Vector란?

Vector는 List인터페이스를 구현한 클래스로 List의 특징을 그대로 가지고 있다.

Vector는 내부적으로 배열을 선언해 List를 구현하고 있다. ArrayList와 같은 방식으로 구현 하고 있다. 즉 Vector를 쓰지않고 ArrayList를 써도 무관하다는 것이다.

Vector VS ArrayList

그렇다면 Vector와 ArrayList의 차이는 무엇일까??

그것은 바로 구현방식의 차이에 있다. 아래의 코드를 확인해보자.

**public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }**

**public synchronized E set(int index, E element) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

public synchronized boolean add(E e) {
        modCount++;
        add(e, elementData, elementCount);
        return true;
    }**

get과 set, add 메소드가 모두 synchronized되어 있다. 뿐만아니라 forEach 메소드또한 synchronized 되어있는데, 이 때문에 Vector는 사용시 오버헤드가 발생한다. 덕분에 멀티스레드 환경에서 안전히 사용 할수 있지만, 그외의 환경에서는 성능저하가 일어난다.

그렇다면 단일스레드 환경에서는 ArrayList, 멀티스레드 환경에서는 Vector를 쓰면될까??

아니다. ArrayList는 synchronized 키워드를 자체적으로 선언하지 않았을뿐, ArrayList도 멀티스레드 환경에서 사용할 수 있는 방법을 제공한다.

ArrayList<T> al = new ArrayList<>(Collections.synchronizedList());

이렇게 ArrayList는 멀티스레드,단일스레드 환경 모두에서 사용이 가능하고, 단일스레드 환경에서는 Vector보다 더 좋은 성능을 보여준다. 실제로 ArrayList는 Vector를 더 개선한 클래스로, Vector클래스가 남아있는 이유는 하위버전과의 호환성을 위해 남겨뒀을 뿐이다. 멀티스레드 환경을 위해 쓴다고 Vector클래스를 쓰지 말도록 하자.

💡

마찬가지로, Vector를 상속받은 Stack도 똑같은 문제를 가지고 있다. Stack을 쓰려한다면 ArrayList와 마찬가지로 더 개선된 Deque를사용하자!

Hashtable이란?

Hashtable 클래스는 자바의 컬렉션 프레임워크가 만들어지기 이전부터 존재했다. 일반적으로 알고있는 HashMap의 기능과 사용법과 동일하다고 보면 된다. 그리고 컬렉션 프레임워크가 만들어지며 HashMap클래스가 만들어졌다. 즉, Hashtable을 개선한 클래스가 HashMap이라 볼수 있다.

Hashtable VS HashMap

  • Thread-safe 여부

Hashtable은 앞서 알아본 Vector와 마찬가지로 메소드에 synchronized 키워드가 선언되어 있다. 코드를 확인해 보자.

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }
...

즉, 멀티스레드 환경에서 안전하지만, 단일스레드에서는 성능저하가 일어날 수 밖에 없다는 소리다.

  • NULL값의 허용여부Hashtable은 key에 null을 허용하지 않지만, HashMap은 key에 null을 허용한다.
  • Enumeration 여부Hashtable은 not fail-fast Enumeration을 제공하지만, HashMap은 제공하지 않는다.
    • not fail-fast Enumertion이란?Enumeration에 대해 먼저 알고 넘어가자.Enumeration 객체는 new 연산자로 생성할 수 없으며, Vector를 이용하여 생성할 수 있다.즉, 컬렉션 프레임워크에서 쓰이는 Iterator의 역할을 하던 객체이다.Enumeration은 객체의 순차적 접근 시 컬렉션 객체에 변경이 일어나도, 이를 무시하고 끝까지 동작한다. 이에반해 Iterator는 순차적 접근이 끝나기 전에 변경이 일어난 경우 접근이 실패하며 예외를 return하는데, 이 방식을 fast-fail 방식이라 부른다. 즉 not fail-fast Enumertaion은 Enumeration의 방식인 것이다.
    • Itrerator의 fast-fail 방식은 안전하지 않을지도 모르는 행위의 위험을 막는다. 이러한 위험은 실행 중 불특정한 시간에 멋대로 결정되지 않은 행위를 할 가능성이 있기 때문이다.
    • Enumeration과 Iterator 이 두가지의 큰 차이점이 바로 fast-fail 방식이다.
    • Vector 클래스의 elements() 라는 메소드는 객체의 모든 요소들을 Enumeration 객체로 반환한다.
    • Enumeration은 객체들의 집합(Vector)에서 각각의 객체들을 한순간에 하나씩 처리할 수 있는 메소드를 제공하는 컬렉션이다.
  • HashMap은 보조해시함수가 있어 Hashtable보다 해시충돌이 덜 일어난다.HashMap과 Hashtable 모두 해시함수를 통해서 데이터를 저장하는데, 서로다른 키임에도 해시코드가 똑같이 나오게 되는 경우가 있다. 이를 해시충돌이라 부르고, 이런 해시충돌이 발생하면 같은 해시값에 여러 객체가 저장되므로, get, contain등의 연산에 악영향을 끼친다. 즉 get과 contain 메소드의 시간복잡도O(1)이 깨지게된다. HashMap은 보조해시함수 라는것을 통해서 이런 해시 충돌이 덜 일어나게 된다. 때문에 자연스럽게 성능도 향상된다 할 수있다.

이렇게 Hashtable은 synchronized키워드로 인해 단일스레드환경에서는 성능상 이점이 없으며, 보조해시함수가 없으므로, 해시충돌이 더 잘나게될 확률이 크다. 뿐만아니라 not fail-fast Enumeration을 제공함으로써, 객체의 무결성에 대해서 완벽히 보장할 수 없게 된다. 성능과 무결성 모두 떨어지는 Hashtable은 하위버전 호환성을 위해서 존재한다고만 알고있고, 실제로는 HashMap을 쓰도록하자!

알게된 점

  • Vector와 대체제는 무엇이 있을까??
    • ArrayList는 Vector 더 개선시킨 클래스로, Vector의 자리에 ArrayList를 사용하면 된다.
  • Vector와 대체제의 구별되는 특징
    • get,add등의 메소드에 synchronized 키워드 선언, 성능이 떨어진다.
  • 그래서 Vector를 왜? 안써야 하는가?
    • 성능상 Vector는 좋지 않을뿐더러, ArrayList 역시 Collections.synchronizedList()메소드를 통해 멀티스레드에 안전하게 사용할 수 있다.
  • Hashtable과 대체제는 무엇이 있을까?
    • Hashtable은 자바 컬렉션프레임워크 이전부터 존재하던 클래스로, Map을 구현한 클래스중 가장 오래됬다. 두번째로 오래된 클래스가 HashMap으로, 컬렉션프레임워크에 포함되어 있으며, Hashtable의 사용법과 동일하다고 보면된다.
  • Hashtable과 대체제의 구별되는 특징
    • Hashtable은 Vector와 마찬가지로 synchronized 선언이 되어 스레드 세이프 하지만, 그만큼 성능저하가 일어난다.
    • Hashtable은 key로 null을 받을수 없지만, HashMap은 받을 수 있다.
    • Hashtable은 not fail-fast Enumeration을 제공한다. 즉, 객체의 변경이 있어도 순차적 접근을 끝까지 실행한다. 이는 객체의 무결성을 해칠수 있다.
    • HashMap은 보조해시함수의 도움을 받아 Hashtable보다 해시충돌이 적게 일어난다. 즉 성능상 더 이점이 있다
    • Hashtable은 Vector와 마찬가지로 하위버전 호환성때문에 존재한다. 반면 HashMap은 게속해서 개선되고 있다.
  • 그래서 Hashtable은 왜? 안써야 하는것일까?
    • synchronized 키워드와 보조해시함수의 부재로 인한 성능상 단점
    • not fail-fast Enumeration으로 인한 객체의 무결성 보장 불가능
    • 더 개선될 여지가 없는 클래스

Reference

fail-fast 방식 개념

Fail-Fast란?

[Java] HashMap vs Hashtable 차이는 무엇일까?

자바에서 Vector와 Stack 컬렉션이 쓰이지 않는 이유?

Uploaded by Notion2Tistory v1.1.0

반응형

'프로그래밍 > Java' 카테고리의 다른 글

Map  (0) 2021.05.31
Set과 Queue -Reboot(3)  (2) 2021.05.31
Set과 Queue -Reboot(2) 미완  (0) 2021.05.28
ArrayList VS LinkedList -addOn(1)  (0) 2021.05.27
Set과 Queue -Reboot(1)  (0) 2021.05.27