[Java] Chap15 컬렉션 프레임워크

Seunghee Lee·2023년 3월 13일
0

Java

목록 보기
7/12

1. 컬렉션 프레임워크 소개

배열은 쉽게 생성하고 사용할 수 있지만, 저장할 수 있는 객체 수가 배열을 생성할 때 결정되기 때문에 불특정 다수의 객체를 저장하기에는 문제가 있다. 물론 배열의 길이를 크게 생성하면 되지만, 이는 좋은 방법이 아니다.

자바는 배열의 이러한 문제점을 해결하고, 자료구조를 바탕으로 객체들을 효율적으로 추가·삭제·검색할 수 있도록 java.util 패키지에 컬렉션과 관련된 인터페이스와 클래스들을 포함시켜 놓았는데, 이를 컬렉션 프레임워크(Collection Framework)라고 한다.

자바에서 컬렉션은 객체를 수집해서 저장하는 역할을 한다.
프레임워크란 사용 방법을 미리 정해 놓은 라이브러리를 말한다.

  • 컬렉션 프레임워크의 주요 인터페이스는 List, Set, Map 이 있다.
    아래 그림은 인터페이스별로 사용 가능한 컬렉션 클래스를 보여준다.

  • List와 Set은 객체를 추가·삭제·검색하는 방법에 많은 공통점이 있기 때문에, 공통된 메소들만 모아 Collection 인터페이스로 정의하고 있다.

  • Map은 키와 값을 하나의 쌍으로 묶어서 관리하는 구조로 되어 있다.


2. List 컬렉션

List 컬렉션은 객체를 일렬로 늘어놓은 구조를 가지고 있다.

  • 객체를 인덱스로 관리한다.
    - 객체를 저장하면 자동 인덱스가 부여되고, 인덱스로 삭제·검색할 수 있는 기능을 제공한다.
  • 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조한다.
    - 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다.
    - null값도 저장 가능하며, 이 경우 해당 인덱스는 객체를 참조하지 않는다.

List<String> list = ...;	
list.add("Gildong");		//맨 끝에 객체 추가
list.add(1, "Seunghee");	//지정된 인덱스(1)에 객체 추가
String str = list.get(1);	//인덱스(1)로 객체 찾기
list.remove(0);				//인덱스(0)로 객체 삭제
list.remove("Gildong")		//객체 삭제

만약 전체 객체를 대상으로 하나씩 반복해서 저장된 객체를 얻고 싶다면 다음과 같이 for문을 사용할 수 있다.

List<String> list = ...;

/* 인덱스 번호가 필요한 경우 */
for(int i=0; i<list.size(); i+++) {
	String str = list.get(i);	//i 인덱스에 저장된 String 객체를 가져옴
}

/* 인덱스 번호가 필요 없는 경우 */
for(String str : list) { ... }	//이 방법이 더 편리할 수 있음

1) ArrayList

ArrayList는 List 인터페이스의 구현 클래스로, 객체를 추가하면 객체가 인덱스로 관리된다.

일반 배열과 ArrayList는 인덱스로 객체를 관리한다는 점에서 유사하지만, 큰 차이점을 가지고 있다.

  • 배열은 생성할 때 크기가 고정되고 사용 중에 크기를 변경할 수 없지만,
  • ArrayList는 저장용량(capacity)을 초과한 객체들이 들어오면 자동적으로 용량이 늘어난다.

✅ 형태

List<타입> list = new ArrayList<타입>(용량(생략가능));

// ex) String 객체 30개를 저장할 수 있는 용량을 갖는 list 생성
// List<String> list = new ArrayList<String>(30);
  • 자바5 이후부터 제네릭을 도입하여 ArrayList 객체를 생성할 때 타입 파라미터로 저장할 객체의 타입을 지정함으로써 불필요한 타입 변환을 하지 않도록 했다.

ArrayList에 객체를 추가하면 인덱스 0부터 차례대로 저장된다.

  • ArrayList에서 특정 인덱스의 객체를 제거하면
    → 바로 뒤 인덱스부터 마지막 인덱스까지 모두 1씩 앞당겨진다.
  • 마찬가지로 특정 인덱스에 객체를 삽입하면
    → 해당 인덱스부터 마지막 인덱스까지 모두 1씩 밀려난다.

🚩 따라서 빈번한 객체 삽입과 삭제가 일어나는 곳에서는 ArrayList를 사용하는 것은 바람직하지 않다 !

  • 이런 경우라면 LinkedList를 사용하는 것이 좋다.

🚩 ArrayList는 인덱스 검색이나 맨 마지막에 객체를 추가하는 경우에 사용하는 것이 좋다 !

  • [ ArrayListExample.java ] - String 객체를 저장하는 ArrayList
public class ArrayListExample {
    public static void main(String... args) {
        List<String> list = new ArrayList<String>();

		/* 객체 추가 */
        list.add("java");
        list.add("spring");
        list.add("jdbc");
        list.add(2, "data");
        list.add("js");

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

		/* 2번 인덱스의 객체 얻기 */
        String skill = list.get(2);
        System.out.println("2: " + skill);
        System.out.println();

		/* 저장된 총 객체 출력 */
        for(int i=0; i<list.size(); i++) {
            String str = list.get(i);
            System.out.println(i + ":" + str);
        }
        System.out.println();

		/* 객체 삭제*/
        list.remove(2);
        list.remove(2);
        list.remove("data");

		/* 저장된 총 객체 출력 */
        for(int i=0; i<list.size(); i++) {
            String str = list.get(i);
            System.out.println(i + ":" + str);
        }
        System.out.println();
    }
}

🚩 ArrayList를 생성하고 런타임 시 필요에 의해 객체들을 추가하는 것이 일반적이지만, 고정된 객체들로 구성된 List를 생성할 때도 있다.

  • 이런 경우에는 Arrays.asList(T... a) 메소드를 사용하는 것이 간편하다.

✅ 형태

List<타입> list = Arrays.asList(매개값1, 매개값2, ...);
  • [ ArraysAsListExample.java] - Arrays.asList() 메소드
public class ArraysAsListExample {
    public static void main(String... args) {
        List<String> list1 = Arrays.asList("a", "b", "c");
        for(String alpha : list1) {
            System.out.println(alpha);
        }

        List<Integer> list2 = Arrays.asList(1, 2, 3);
        for(int number : list2) {
            System.out.println(number);
        }
    }
}

2) Vector

Vector는 ArrayList와 동일한 내부 구조를 가지고 있으나, Vector는 동기화된 메소드로 구성되어 있다.

  • 따라서 멀티 스레드가 동시에 메소드들을 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다.

멀티 스레드 환경에서 안전하게 객체를 추가·삭제할 수 있다.

  • 이것을 스레드가 안전(Thread Safe)하다라고 말한다.

  • [ VectorExample.java] - Board 객체를 저장하는 Vector

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

        list.add(new Board("title1", "content1", "writer1"));
        list.add(new Board("title2", "content2", "writer2"));
        list.add(new Board("title3", "content3", "writer3"));
        list.add(new Board("title4", "content4", "writer4"));
        list.add(new Board("title5", "content5", "writer5"));

        list.remove(2);
        list.remove(3);

        for(int i=0; i<list.size(); i++){
            Board board = list.get(i);
            System.out.println(board.subject + "\t" + board.content + "\t" + board.writer);
        }
    }
}
  • [ Board.java] - 게시물 정보 객체
public class Board {
    String subject;
    String content;
    String writer;

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

3) LinkedList

LinkedList는 List 구현 클래스이므로 ArrayList와 사용 방법은 똑같지만, 내부 구조는 완전히 다르다.

  • ArrayList는 내부 배열에 객체를 저장해서 인덱스로 관리하지만,
  • LinkedList는 인접 참조를 링크해서 체인처럼 관리한다.

✅ 형태

LinkedList<타입> list = new LinkedList<타입>();

  • LinkedList에서 특정 인덱스의 객체를 제거하거나 삽입하면
    앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다.
    👍 따라서 객체의 삭제와 삽입이 빈번하게 일어나는 곳에 적합하다.

실제로 객체를 삽입하는 데 걸린 시간을 측정하면 ArrayList보다 LinkedList가 빠른 것을 알 수 있다.

  • [ LinkedListExmaple.java ] - ArrayList와 LinkedList의 실행 성능 비교
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");
    }
}

🚩 끝에서부터 순차적으로 추가/삭제하는 경우ArrayList 가 빠르고
🚩 중간에 추가/삭제하는 경우LinkedList가 더 빠르다.

profile
자라나라 개발개발 ~..₩

0개의 댓글