성실한 사람이 되자

성실하게 글쓰자

This is spear

JAVA_SPRING/JAVA

HashSet, ArrayList, LinkedList, Vector 각 클래스 간의 특징과 공통점

Imaspear 2022. 2. 7. 09:31
728x90

HashSet, ArrayList, LinkedList, Vector 각 클래스 간의 특징과 공통점

HashSet, ArrayList, LinkedList, Vector의 공통점은 부모 클래스가 Collection 인터페이스를 상속받고 있다는 점이다.

 

인터페이스 특징 구현 클래스
List 순서가 있는 데이터의 집합, 데이터의 중복을 허용한다. ArrayList, LinkedList, Stack, Vector
Set 순서를 유지하지 않는 데이터의 집합, 데이터의 중복을 허용하지 않는다. HashSet, TreeSet

 

Vector

Vector는 컬렉션이 만들어지기 전부터 존재하기 때문에 컬렉션 프레임워크의 명명법을 따르지 않는다. 해당 클래스는 호환을 위해 설계를 변경해서 남겨 뒀지만 사용하지 않는 것이 좋다.

Vector 구조

 

JDK1.2 이전 버전 상속 계층도

Vector -> Object

ArrayList

ArrayList는 Vector를 개선한 것으로 Vetor와 구현원리와 기능적인 측면에서 동일하다. ArrayList는 데이터를 순차적으로 저장하고, Object타입의 데이터를 저장할 수 있다. 즉, 모든 종류의 객체를 저장할 수 있다.

ArrayList 구조

ArrayList는 AbstractList 추상 클래스를 상속받고 있고, List를 구현한 구현체이다.

 

List<Item> items = new ArrayList<Item>();
ArrayList를 생성할 때, 저장할 요소의 개수를 고려해서 생성해야 한다. 기존 크기보다 늘어날 경우 저장은 되지만 처리시간이 많이 소요된다.

 

LinkedList

배열은 구조가 간단하며 데이터를 읽는 시간이 가장 빠르다는 장점이 있지만 크기를 변경할 수 없고, 비순차적인 데이터 변경에 많은 시간이 걸린다. 이러한 배열의 단점을 보완하기 위해 LinkedList 자료구조가 탄생했다. LinkedList는 데이터를 서로 연결한 형태로 구성되어 있다. 해당 자료구조는 데이터의 추가와 삭제가 빠르다.

LinkedList 구조

 

ArrayList vs LinkedList

순차적으로 추가/삭제는 ArrayList가 빠르다. 중간 데이터를 추가/삭제는 LinkedList가 빠르다.

 

HashSet

HashSet은 위 클래스들과 다르게 Set 인터페이스를 구현한다. HashSet의 가장 큰 특징은 중복된 요소를 저장하지 않는다. List 인터페이스를 구현한 ArrayList 구현체와 달리 HashSet은 저장순서를 유지하지 않는다.

HashSet 구조

 

저장순서를 유지하고자 한다면 LinkedHashSet을 사용하자

 

테스트

ChallengeMainTest

class ChallengeMainTest {
    static final Long MAX_INT = 10000L;

    private static void printItem(Item item) {
        System.out.println(item.selfIntroduce());
    }

    private void printStart() {
        System.out.println("idx     item");
    }

    @Test
    @DisplayName("ArrayList Test")
    void ArrayListTest() {
        List<Item> list = new ArrayList<>();
        for (long i = 1L; i < MAX_INT; i++) {
            Item item = new Item(i,"Item"+i);
            list.add(item);
        }
        printStart();
        list.iterator().forEachRemaining(ChallengeMainTest::printItem);
    }

    @Test
    @DisplayName("LinkedList Test")
    void LinkedListTest() {
        List<Item> list = new LinkedList<>();
        for (long i = 1L; i < MAX_INT; i++) {
            Item item = new Item(i,"Item"+i);
            list.add(item);
        }
        printStart();
        list.iterator().forEachRemaining(ChallengeMainTest::printItem);

    }

    @Test
    @DisplayName("Vector Test")
    void VectorTest() {
        List<Item> list = new Vector<>();
        for (long i = 1L; i < MAX_INT; i++) {
            Item item = new Item(i,"Item"+i);
            list.add(item);
        }
        printStart();
        list.iterator().forEachRemaining(ChallengeMainTest::printItem);
    }

    @Test
    @DisplayName("HashSet Test")
    void HashSetTest() {
        Set<Item> list = new HashSet<>();
        for (long i = 1L; i < MAX_INT; i++) {
            Item item = new Item(i,"Item"+i);
            list.add(item);
        }
        printStart();
        list.iterator().forEachRemaining(ChallengeMainTest::printItem);
    }
}

 

테스트 결과

LinkedList 자료구조가 가장 늦었고, 그 다음 ArrayList, 마지막으로 Vector와 HashSet이 가장 빨랐다.

 

equals() and hashCode()

equals() 메서드와 hashCode() 메서드를 추가했는데 해당 idx가 같으면 같은 데이터라고 설정했다. 이렇게 되면 HashSet은 add() 메서드를 호출할 때, eqauls()hashcode() 메서드를 이용해 같은 데이터인지 판별한다.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Item item = (Item) o;
    return Objects.equals(idx, item.idx);
}

@Override
public int hashCode() {
    return Objects.hash(idx);
}

 

HashSet일 때, 값을 순차적으로 들고 올 수 있는 이유가 무엇일까

Set을 Iterator를 이용해서 저장된 요소들을 출력오면 저장된 순서와 다르게 출력이 된다. 그런데 해당 eqauls()hashcode() 메서드를 이용하면 순차적으로 출력이 된다.

우선 Iterator는 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화한 것이라고 한다. HashSet에서 Iterator로 변경할 때, eqauls()hashcode() 메서드를 이용한 변경점이 있지 않을까 생각을 한다.

Iterator<Item> iterator = list.iterator();
while (iterator.hasNext()) {
    Item next = iterator.next();
    System.out.println(next.selfIntroduce());
}

 

수정 전
우선 Iterator는 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화한 것이라고 한다. HashSet에서 Iterator로 변경할 때, eqauls()와 hashcode() 메서드를 이용한 변경점이 있지 않을까 생각을 했다.

 

실제는,
HashSet은 내부적으로 HashMap을 이용해서 만들어졌으며 해싱을 이용해서 구현했다. 그래서 hashcode() 메서드를 재정의해서 순차적인 것처럼 보이게 만들었을 뿐이지 순차적으로 저장되고 출력이 되는 것이 아니다.

 

위 메서드를 추가했을 경우

1 2 3 4 5 ··· 7