JAVA Study

[JAVA] 컬렉션 프레임워크

영하10도 아아 2024. 4. 16. 01:36

컬렉션 프레임워크


컬렉션 프레임워크란?

 

자바는 널리 알려져 있는 자료구조(Data Source)를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공한다. 이들을 총칭해서 컬렉션 프레임워크(Collection Framework)라고 부른다.

 

간단설명으로

컬렉션 : 객체의 저장을 뜻함.

 

 

프레임 워크 : 사용 방법을 정해놓은 라이브러리를 말함.

 

 

컬렉션 프레임워크 장점

 

1.인터페이스와 다형성을 이용한 객체지향적 설계를 통해 표준화되어 있기 때문에, 사용법을 익히기도 쉽고 재사용성이 높다.

 

2. 데이터 구조 및 알고리즘 고성능 구현을 제공하여 프로그램의 성능과 품질을 향상시킨다.

 

3.관련 없는 API 간의 상호 운용성을 제공한다.

 

4.이미 구현되어 있는 API를 사용하면 되기에, 새로운 API를 익히고 설계하는 시간이 줄어든다.

 

5.소프트웨어 재사용을 촉진한다. 만약 자바에서 지원하지 않는 새로운 자료구조가 필요하다면, 컬렉션들을 재활용해 종합하여 새로운 알고리즘 생성이 가능하다.

 

 

-컬렉션에 저장할 수 있는 데이터는 오로지 객체이다. 즉 int형이나 double형 같은 자바의 primitive 타입은 적재를 못한다는 뜻이다.

따라서 primitive 타입을 wrapper 타입으로 변환하여 Integer 객체나 Double 객체로 박싱(boxing)하여 저장해야 한다.

또한 객체를 담는다는 것은 주소값을 담는 것과 같기에 null도 저장이 가능하다.

 

 


박싱(boxing)과 언박싱(unboxing)

 

자바의 모든 형식은 참조형(Byte,Integer,Object,List) 아니면 기본형(int,double,byte,char)에 해당된다.

자바에서는 기본형을 참조형으로 변환하는 기능을 제공한다. 이 기능을 박싱(boxing)이라고 한다. 참조형을 기본형으로 변환하는 반대 동작을 언박싱(unboxing)이라고 한다. 또한, 프로그래머가 편리하게 코드를 구현할 수 있도록 박싱과 언박싱이 자동으로 이루어지는 오토박싱(autoboxing)이라는 기능도 제공한다.

 

 

그래서 박싱을 하는 이유가 뭔데?

 

 

자바에서 박싱을 하는 이유는 기본 데이터 타입과 객체 간의 호환성을 제공하고, 객체 지향 프로그래밍의 특징을 활용하기 위해서다.

 

 

단점

 

객체 생성 : 기본타입의 값을 래퍼 클래스 객체로 변환하게 되는데, 이때 객체가 새로 생성되며 객체 생성과 가비지 컬렉션은 추가적인 메모리와 CPU 시간을 소모할 수 있다.

 

메모리 사용 증가 : 래퍼 클래스 객체는 기본 타입보다 더 많은 메모리를 사용한다. 예를 들어, Integer 객체는 int 기본 타입보다 메모리 공간을 더 차지한다.

 

 

 


컬렉션 프레임워크의 종류

 

 

 

 

 

컬렉션 프레임워크의 주요 인터페이스로는 List, Set, Map이 있다.

 

 

List 컬렉션

 

 

List는 배열과 비슷하게 객체를 인덱스로 관리한다. 배열과의 차이점은 저장 용량이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여된다는 것이다. 그리고 추가, 삭제, 검색을 위한 다양한 메소드들이 제공된다.

 

List 컬렉션은 객체 자체를 저장하는 것이 아닌, 다음 그림과 같이 객체의 번지를 참조하는 것이다. 그렇기 때문에 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다. 그리고 null 값도 저장이 가능하며, 이 경우 해당 인덱스를 객체를 참조하지 않는다.

 

 

 

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

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

List 컬렉션 공통 메서드

 

List 컬렉션에 객체를 추가할 때는 add() 메서드를 사용하고, 객체를 찾아올 때에는 get() 메서드를 사용한다. 그리고 객체 삭제는 remove()를 사용한다.

List<String> list = ...;
list.add("홍길동");             //맨 끝에 객체 추가    
list.add(1,"신용권");           //지정된 인덱스에 객체 삽입
String str = list.get(1);     //인덱스로 객체 검색
list.remove(0);               //인덱스로 객체 삭제
list.remove("신용권");         //객체 삭제

 

 

 


 

 

ArrayList

 

 

ArrayList는 인터페이스의 대표적인 구현 클래스이다.

//ArrayList 생성하는 방법

List<E> list =- new ArrayList<E>();
//타입 파라미터               타입 파라미터

 

 

ArrayList를 생성하기 위해서는 저장할 객체 타입을 E 타입 파라미터 자리에 표기하고 기본 생성자를 호출하면 된다.

예를 들어 String을 저장하는 ArrayList는 다음과 같이 생성할 수 있다.

//ArrayList 생성
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>();

 

두 번째 코드와 같이 <> 안을 생략하면 왼쪽 List에 지정된 타입을 따라간다. 따라서 위 두 코드는 동일하게 String을 저장하는 ArrayList 객체를 생성한다.

 

ArrayList를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기용량을 가지게 된다. 저장되는 객체 수가 늘어나면 용량도 자동으로 증가한다.

 

다음은 인덱스가 삽입되었을 때 그림이다.

 

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

 

이런 동작 때문에 객체 수가 많고, 특정 인덱스에 객체를 추가하거나 제거하는 일이 빈번하다면 ArrayList보다는 뒤에 나올 LinkedList를 사용하는 것이 좋다. 하지만 인덱스를 이용해 객체를 찾거나 맨 마지막에 객체를 추가하는 경우는 ArrayList가 더 좋은 성능을 발휘한다.

 

 

 

특정 인덱스에 저장되어 있는 값을 변경하고 싶다면 set 메서드를 사용하면 된다. 다음은 예시 코드이다.

ArrayList<String> list1 = new ArrayList<>();

list1.add("list1");
list1.add("list1");
list1.add("list1");
 
// index 1번의 데이터를 문자열 "setData"로 변경한다.
list1.set(1, "setData"); 

System.out.println(list1); // [list1, setData, list1]

 

 

 

 

ArrayList 장단점

 

1. 리스트의 길이가 가변적이다. 이를 동적 할당이라고 한다.

 

2. 배열과 달리 메모리에 연속적으로 나열되어있지 않고 주소로 연결되어 있는 형태이기 때문에 index를 통한 색인(접근) 속도가 배열보다는 느리다.

 

3. 데이터 사이에 빈 공간을 허용하지 않는다.

 

4. 객체로 데이터를 다루기 때문에 적은 양의 데이터만 쓸 경우 배열에 비해 차지하는 메모리가 커진다.

 

 

 

 

 

 


 

Vector

 

 

 

Vector는 ArrayList와 동일한 내부 구조를 가지고 있다. Vector를 생성하기 위해서는 저장할 객체 타입을 타입 파라미터로 표기하고 기본 생성자를 호출하면 된다.

 

//Vector 생성방법
List<E> list = new Vector<E>();
List<E> list = new Vector<>();      //Vector의 타입 파라미터를 생략하면 
                                    //왼쪽 List에 지정된 타입을 따라 감.

 

 

ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Vector의 메소드를 실행 할 수 없다.

하나의 스레드가 메소드를 실행을 완료해야만 다른 스레드가 메소드를 실행할 수 있다는 것이다. 그래서 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다. 이것을 스레드의 안전(thread safe)하다고 표현한다. 뒤에서 동기화와 함께 추가설명 하겠다.

Thread Safe

 

 

Vector는 removeAllElements()는 clear() 메서드와 달리 모든 요소를 지우고 용량(capacity)도 0으로 만든다.

// 모든 값 제거
v.clear(); // 요소만 제거
v.removeAllElements(); // 요소도 제거하고 용량도 0으로 만듬

 

 

또한 capacity() 메서드를 통해 컬렉션의 용량을 반환받을 수 있다. ( ArrayList 에서는 불가능)

 

// Vector 초기 용량 10으로 생성
Vector<Integer> v = new Vector<>(10);

// 값 추가
v.add(1); 
v.add(2);

// Vector 자료 총 개수(size) 
System.out.println(v.size()); // 2

// Vector 용량 
System.out.println(v.capacity()); // 10

 

 

 

 

Vector의 동기화

 

ArrayList와 거의 동일하지만 한 가지 다른 점이 있다. 바로 메서드에 synchronize 유무이다. 실제로 Vector 클래스 정의 소스 파일에 가서 메서드들을 보면 synchronize 키워드가 걸려있음을 볼 수 있다.

 

 

 

synchronize 키워드는 멀티 쓰레드 환경에서 두 개 이상의 쓰레드가 하나의 변수에 동시에 접근을 할 때 Race condition(경쟁상태)이 발생하지 않도록 한다. 한마디로 해당 메서드를 실행하는 동안 다른 쓰레드가 접근하지 못하도록 메서드를 잠금(lock)을 거는 것이다.

 

 

 

 

 

 


 

LinkedList

 

 

LinkedList는 List 구현 클래스이므로 ArrayList와 사용 방법은 똑같은데, 내부 구조는 완전히 다르다. ArrayList는 내부 배열에 객체를 저장해서 관리하지만, LinkedList는 인전 참조를 링크해서 체인처럼 관리한다.

 

ArrayList vs LinkedList 구조

 

LinkedList에서 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다. 특정 인덱스에 객체를 삽입할 때에도 마찬가지이다. 다음은 중간에 객체를 제거할 경우 앞뒤 링크의 수정이 일어나는 모습이다.

 

LinkedList 객체 삽입시 구조

*빈번한 객체 삭제와 삽입이 일어나는 곳에서는 LinkedList가 좋은 성능을 발휘한다.

 

 

 

LinkedList가 처음 생성될 때에는 어떠한 링크도 만들어지지 않기 때문에 내부는 비어있다고 보면 된다.

//LinkedList 객체 생성
List<E> list = new LinkedList<E>();
List<E> list = new LinkedList<>();

 

 

 

구분 순차적으로 추가/삭제 중간에 추가/삭제 검색
ArrayList 빠르다 느리다 빠르다
LinkedList 느리다 빠르다 느리다

 

 

 

 

 

 

 


 

Set 컬렉션

 

 

 

List 컬렉션은 객체의 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다. 또한 객체를 중복해서 저장할 수 없고, 하나의 null 만 저장할 수 있다. 다시말해 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다.

 

수학의 집합과 비슷한 성질인데, 집합은 순서와 상관없고 중복이 허용되지 않기 때문이다.

 

 

 

Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있는데, 인덱스로 관리하지 않기 때문에 인덱스를 매개값으로 갖는 메소드가 없다.

 

다음은 Set 컬렉션에 String 객체를 저장하고 삭제하는 방법을 보여준다.

//Set 컬렉션 String 객체 저장 및 삭제
Set<String> set = ...;
set.add("홍길동");     //객체 추가
set.add("신용권");
set.remove("홍길동");  //객체 삭제

 

 

Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없다. 대신, 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)를 제공한다. 반복자 Iterator 인터페이스를 구현한 객체를 말하는데, iterator() 메소드를 호출하면 얻을 수 있다.

Set<String> set = ...;
Iterator<String> iteraor = set.iterator();

 

 

 

다음은 Iterator 인터페이스에 선언된 메소드들이다.

 

 

다음은 Set 컬렉션에서 String 객체들을 반복해서 하나씩 가져오는 코드이다.

Set<String> set = ...;
Iterator<String> tierator = set.iterator();
while(iterator.hasNext()){
    //String 객체 하나를 가져옴
        String str = iterator.next();   //저장된 객체 수만큼 루핑
        }

 

 

 

 

 


 

HashSet

 

 

HashSet은 Set 인터페이스의 구현 클래스이다.  때문에 Set의 성질을 그대로 상속받는다는 것이 특징이다.

 

HashSet의 특징

 

1.HashSet은 중복된 값을 허용하지 않는다.

 

2.List 등과는 다르게 저장한 순서가 보장되지 않는다.

 

3.null을 값으로 허용한다.

 

 

 

중복을 걸러내는 과정

 

HashSet은 객체를 저장하기 전에 먼저 객체의 hashCode() 메소드를 호출해서 해시 코드를 얻어낸 다음 저장되어 있는 객체들의 해시 코드와 비교한 뒤 같은 해시 코드가 있다면 다시 equals() 메소드로 두 객체를 비교해서 true가 나오면 동일한 객체로 판단하고 중복 저장을 하지 않는다.

 

해시코드 작동 방식

1단계로 해시코드로 먼저 분류를 한다.

해시코드가 다르다면 다른객체로 인식.

혹시나 해시코드가 같아도 다른객체일 수 있기 때문에 equals로 물리적인 주소값을 비교한다.

(equals는 부담되는 방식이기에 해시코드로 먼저 대부분 분류를 한다.)

-정확한 값(위치)을 갖고 있기 때문에 10억개 이던지 100억개 이던지 금방 찾을 수 있다.

 

 

 

 

 

 


 

Map 컬렉션

 

 

 

Map 컬렉션은 키(key)와 값(value)로 구성된 Map.Entry 객체를 저장하는 구조를 가지고 있다. Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다. 여기서 키와 값은 모두 객체다.

 

키는 중복 저장될 수 없지만 값은 중복 저장될 수 있다. 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값을 없엊고 새로운 값으로 대체된다.

 

 

 

Map 컬렉션에는 HashMap, Hashtable, LinkedHashMap, Properties, TreeMap 등이 있다.

 

//Map 객체 생성 및 추가/삭제
Map<String,Integer> map = ...;
map.put("홍길동",30);          //객체 추가
int score = map.get("홍길동"); //객체 찾기
map.remove("홍길동");          //객체 삭제

 

 

 

 

 

 

 

 

 


 

 

HashMap

 

 

HashMap의 키로 사용할 객체를 hashCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다. 객체가 달라도 동등 객체라면 같은 키로 간주하고 중복 저장되지 않도록 하기 위함이다. 동등 객체의 조건은 hashCode()의 리턴값이 같아야 하고, equals() 메소드가 true 를 리턴해야한다. 

 

HashMap은 이름 그대로 해싱(Hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 보인다.

 

 

 

 

 

 

 


 

'JAVA Study' 카테고리의 다른 글

[JAVA]예외 처리  (0) 2024.04.11
[JAVA]중첩 클래스  (0) 2024.04.11
[JAVA] 상속(Inheritance)  (0) 2024.04.04
스터디 1회차 - 클래스  (0) 2024.04.02