본문 바로가기

자바

[JAVA] 컬렉션 자료구조 (List, Set)

컬렉션 프레임워크

 

자바는 자료구조를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 관련된 인터페이스와 클래스들을 java.util. 패키지에 포함시켜두었다. 이들을 총칭해서 컬렉션 프레임워크라고 부른다.

 

컬렉션 프레임워크는 몇가지 인터페이스를 통해서 다양한 컬렉션 클래스를 이용할 수 있도록 설계됐다.

주요 인터페이스로는 List, Map, Set 이 있는데 이들로 사용하는 컬렉션 객체의 종류는 아래와 같다.

 

 

List, Set 은 객체를 추가, 삭제, 검색하는 방법에 있어서 공통점이 있기에 공통된 메서드만 따로 모아 Collection 인터페이스로 정의해두고 이것을 상속한다. Map 은 키와 값을 하나의 쌍으로 묶어서 관리하는 구조로 되어 있어 List, Set 과는 사용 방법이 다르다.

 

 

인터페이스 분류 특징 구현 클래스
List - 순서를 유지하고 저장
- 중복 저장 가능
ArrayList, Vector, LinkedList
Set - 순서를 유지하지 않고 저장
- 중복 저장 안됨
HashSet, TreeSet
Map - 키와 값으로 구성된 엔트리 저장
- 키는 중복 저장 안됨
HashMap, Hashtable,
TreeMap, Properties

  

 

 


 

 

List 컬렉션

 

List 컬렉션은 객체를 인덱스로 관리하기 때문에 객체를 저장하면 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있는 기능을 제공한다.

종류에는 ArrayList, Vector, LinkedList 등이있는데 List 컬렉션에서 공통적으로 사용가능한 List 인터페이스 메서드는 다음과 같다.

인덱스로 객체들이 관리되기 때문에 인덱스를 매개값으로 갖는 메서드가 많다.

 

기능 메서드 설명
객체
추가
boolean add(E e)  주어진 객체를 맨 끝에 추가
void add(int index, E element) 주어진 인덱스에 객체를 추가
set (int index, E element)  주어진 인덱스의 객체를 새로운 객체로 바꿈
객체
검색
boolean contains(Object o) 주어진 객체가 저장되어 있는지 여부
E get(int index) 주어진 인덱스에 저장된 객체를 리턴
isEmpty()  컬렉션이 비어있는지 조사
int size() 저장되어 있는 전체 객체수를 리턴
객체
삭제
void clear() 저장된 모든 객체를 삭제
E remove(int index) 주어진 인덱스에 저장된 객체를 삭제
boolean remove(Object o) 주어진 객체를 삭제

 

 

 

ArrayList

 

ArrayList는 List 컬렉션에서 가장 많이 사용하는 컬렉션이다. 객체를 추가하면 내부 배열에 객체가 저장된다.

일반 배열과의 차이점은 ArrayList는 제한 없이 객체를 추가한다는 것이다.

 

List 컬렉션은 객체 자체를 저장하는 것이 아니라 객체의 번지를 저장한다.

또한 동일한 객체를 중복 저장할 수 있는데, 이 경우에는 동일한 번지가 저장된다.

 

List<E> list = new ArrayList<>(); // E에 지정된 타입의 객체만 저장
List list = new ArrayList(); // 모든 타입의 객체를 저장

 

타입 파라미터 E 에는 저장하고싶은 객체 타입을 지정한다. 객체 타입을 모두 생략하면 모든 종류의 객체를 지정할 수 있다.

ArrayList 컬렉션에 객체를 추가하면 인덱스 0번부터 차례대로 저장되며, 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스로부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다. 마찬가지로 객체를 삽입하면 해당 인덱스부터 마지막 인덱스까지 모두 1씩 밀려난다.

 

so, 빈번한 객체 삽입과 삭제가 일어나는 곳에는 ArrayList 를 사용하는 것이 바람직하지 않다.

 

 

 

 

ArrayList 사용 예시코드

 

package ArrayList;

public class Board {
    private String subject;
    private String content;
    private String writer;

    public Board(String subject, String content, String writer) {
        this.subject = subject;
        this.content = content;
        this.writer = writer;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }
}

 

package ArrayList;
import java.util.*;


public class ArrayListExample {
    public static void main(String[] args) {
        List<Board> list = new ArrayList<>();

        list.add(new Board("제목1", "제목1", "글쓴이1 "));
        list.add(new Board("제목2", "제목2", "글쓴이2 "));
        list.add(new Board("제목3", "제목3", "글쓴이3 "));

        System.out.println("list.size() : " + list.size());

        Board board = list.get(2);
        System.out.println(board.getSubject() + " " + board.getContent() + " " + board.getWriter());
        System.out.println("----------------------------------");

        for(Board b : list){
            System.out.println(b.getSubject() + " " + b.getContent() + " " + b.getWriter());
        }

        System.out.println("----------------------------------");
        list.remove(1);
        list.remove(1);

        for(Board b : list){
            System.out.println(b.getSubject() + " " + b.getContent() + " " + b.getWriter());
        }
        System.out.println("----------------------------------");



    }
}

 

 

 

 


 

 

Vector

 

Vector 는 ArrayList와 동일한 내부구조를 가진다. 차이점은 Vector는 동기화된 메서드로 구성되어있기 떄문에 멀티 스레드가 동시에 Vector() 메서드를 실행할 수 없다는 것이다. so, 멀티 스레드 환경에서 안전하게 객체를 추가 또는 삭제할 수 있다.

 

package List;

import java.util.*;

public class VectorExample {
    public static void main(String[] args) {
        List<Board> list = new Vector<>();

        //작업 스레드 객체 생성
        Thread threadA = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    list.add(new Board("제목" + i, "내용" + i, "글쓴이" + i));
                }
            }
        };

        //작업 스레드 객체 생성
        Thread threadB = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 2000; i++) {
                    list.add(new Board("제목" + i, "내용" + i, "글쓴이" + i));
                }
            }
        };

        threadA.start();
        threadB.start();

        //작업 스레드들이 모두 종료될 때 까지 메인 스레드를 기다리게 함
        try {
            threadA.join();
            threadB.join();
        } catch (Exception e) {
        }

        //저장된 총 객체 수 얻기
        System.out.println("총 객체 수 : " + list.size());
    }
}

 

 

 

 

 


 

LinkedList

 

LinkedList는 ArrayList와 사용 방법은 동일하지만 내부 구조는 다르다.

ArrayList는 내부 배열에 객체를 저장하지만, LinkedList는 인접 객체를 체인처럼 연결해서 관리한다.

 

 

LinkedList 는 특정 위치에서 객체를 삽입하거나 삭제하면 바로 앞뒤 링크만 변경하면 되므로 빈번한 객체 삭제와 삽입이 일어나는 곳에는 더 좋은 성능을 발휘한다.

다음 사진은 중간 객체를 제거할 경우 링크의 수정이 일어나는 모습이다.

 

ArrayList, LinkedList 시간 차이 예시 코드

 

package List;

import java.util.*;

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<>();

        List<String> list2 = new LinkedList<>();

        long startTime;
        long endTime;

        //arrayList 걸린 시간
        startTime = System.nanoTime();
        for (int i = 0; i < 10000; i++) {
            list1.add(0, String.valueOf(i));
        }
        endTime = System.nanoTime();
        System.out.println("ArrayList 걸린 시간: " + (endTime - startTime) + " ns");

        //linkedlist 걸린 시간
        startTime = System.nanoTime();
        for (int i = 0; i < 10000; i++) {
            list2.add(0, String.valueOf(i));
        }
        endTime = System.nanoTime();
        System.out.println("LinkedList 걸린 시간: " + (endTime - startTime) + " ns");
    }
}

 

결과를 보면 알듯 LinkedList가 훨씬 빠른 성능을 가진다.

이유는 0번 인덱스에 계속 새로 추가 되면서 ArrayList는 기존 객체의 인덱스를 한칸씩 뒤로 미는 작업을 하기 때문이다.

 

 

 

 


 

 

Set 컬렉션

 

List 컬렉션은 저장 순서를 유지하지만 Set 컬렉션은 저장 순서가 유지되지 않는다. 

또한 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다.

수학의 집합과 비유하여 집합은 순서와 상관없고 중복이 허용되지 않는다는 개념을 생각해라

구슬 주머니와도 같다. 동일한 구슬을 넣을 수 없으며 저장할 때와 찾을 때 순서가 다를 수 있다.

 

 

Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있는데 Set 컬렉션에서 공통적으로 사용 가능한 Set 인터페이스의 메서드는 다음과 같다. 인덱스로 관리하지 않기 때문에 인덱스를 매개값으로 갖는 메서드가 없다.

 

기능 메서드 설명
객체
추가
boolean add(E e) 주어진 객체를 성공적으로 저장하면 true를 리턴하고 중복 객체면 false를 리턴
객체
검색
boolean contains(Object o) 주어진 객체가 저장되었는지 여부
isEmpty() 컬렉션이 비어있는지 조사
Iterator<E> iterator() 저장된 객체를 한번씩 가져오는 반복자 리턴
int size() 저장되어 있는 전체 객체 수 리턴
객체
삭제
void clear() 저장된 모든 객체를 삭제
boolean remove(Object o) 주어진 객체를 삭제

 

 

 

 

HashSet

 

Set 컬렉션 중에서 가장 많이 사용되는 것이 HashSet 이다.

 

Set<E> set = new HashSet<>();

타입 파라미터 E 에는 HashSet에 저장하고싶은 객체의 타입을 지정하면 된다. List와 같이 객체 타입을 생략하면 모든 종류의 객체 저장 가능하다.

HashSet은 동일한 객체는 중복 저장하지 않는다. 여기서 동일한 객체란 동등 객체를 말한다.

HashSet은 다른 객체라도 hashCode() 메서드의 리턴값이 같고, equals() 메서드가 true 를 리턴하면 동일한 객체라 판단 후 저장하지 않는다.

 

 

 

hashSet 작성 예시 코드

 

package List;

import java.security.PublicKey;
import java.util.Objects;

public class Member {
    public String name;
    public int age;

    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member){
            Member target = (Member)obj;
            return target.name.equals(name) &&(target.age == age);
        }
        else return false;
    }
}
package List;

import java.util.*;

public class HashSetExample {
    public static void main(String[] args) {

        Set<Member> set = new HashSet<>();

        set.add(new Member("윤채원", 20));
        set.add(new Member("윤채원", 20));

        System.out.println("총 객체 수: " + set.size());
    }
}

 

 

Set 컬렉션은 인덱스로 객체를 검색할 수 없기에 반복해서 가져올때는 for문을 쓰거나 Set 컬렉션의 iterator() 메서드로 반복자를 얻어 객체를 하나씩 얻어올 수 있다.

Set<E> set = new HashSet<>)();
Iterator<E> iterator = set.iterator();
 
 while(iterator.hasNext()) {
 	E e = iterator.next();
 }

 

 

 

iterator 사용 예시

package List;

import java.util.*;

public class hashSetIteratorExample {
    public static void main(String[] args) {

        Set<String> set = new HashSet<>();

        set.add("Java");
        set.add("JDBC");
        set.add("JSP");
        set.add("Spring");

        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            String element = iterator.next();
            System.out.println(element);
            if(element.equals("JSP")) iterator.remove();

        }
        System.out.println();

        set.remove("JDBC");

        for(String element : set){
            System.out.println(element);
        }
    }
}

 

 

 

 

 

 

 

'자바' 카테고리의 다른 글

[JAVA] 검색 기능 강화 시킨 컬렉션 TreeSet, TreeMap  (0) 2023.09.21
[JAVA] 컬렉션 자료구조 (Map, Hashtable, properties)  (0) 2023.09.21
[JAVA] 제네릭  (0) 2023.09.20
[JAVA] 다형성  (0) 2023.09.18
[JAVA] 예외 처리  (0) 2023.09.18