본문 바로가기

스터디/JAVA

[JAVA] 컬렉션

[컬렉션 프레임워크]

자바는 널리 사용되는 자료구조를 바탕으로 객체들을 효율적으로 관리할 수 있게 해주는 인터페이스나 클래스들을

총칭해서 컬렉션 프레임워크라고 한다. 컬렉션의 주요 인터페이스로는 List, Set 등 2가지 종류가 있다.

LIFO 자료구조를 제공하는 스택 클래스와 FIFO 자료구조를 제공하는 큐 인터페이스를 제공한다.

스택을 응용한 대표적인 예가 JVM 스택 메모리이다.

LIFO : 스택 메모리에 저장된 변수는 나중에 저장된 것부터 제거된다. 

FIFO  : 작업 큐는 먼저 들어온 작업부터 처리한다.

 

[스택]

스택은 LIFO자료구조로 구현한 클래스이다.

예시로 지갑에서 먼저 넣은 동전이 밑으로 쌓이고 늦게 넣은 동전일수록 먼저 사용하게 되므로 LIFO자료구조이다.

 


[List  컬렉션]

 순서를 유지하고 저장, 중복 저장이 가능하다.

 객체를 리스트에 저장해서 인덱스를 통해서 관리하기 때문에 객체를 저장하면 해당 객체에는 인덱스가 부여되고

 인덱스로 객체를 검색 및 삭제 기능이 가능해진다.

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

-> Array, Vector, Linked List

 

[1. ArrayList]

ArryaList는 배열로 구성되어 있다.

다만 배열보다 나은 점은 데이터가 배열을 꽉 채우게 되면 알아서 내부의 공간을 늘려준다.

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

위와 같은 코드처럼 ArrayList를 선언하는 방법은 3가지 종류가 있다.

<> 사이에 어떠한 자료형을 넣어도 저장할 수 있고, 객체도 저장이 가능하다. 

같은 타입의 자료형을 담은 배열이기 때문에 속도가 빠르다.

하지만 중간에 값이 변경되거나 삭제되면 앞이나 뒤에 있던 배열들이 한 칸씩 당기기 때문에 시간이 오래 걸려서

메모리를 사용하는데 비효율적이다.

그래서 Linked List를 사용한다.

 

[2. Linked List]

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

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

ArrayList는 내부 배열에 객체를 저장하지만, Linked List는 인접 객체를 체인처럼 엮어서 사용한다.

ArrayList와는 달리 리스트 중간의 값을 변경하거나 삭제할 때 앞, 뒤 배열을 1씩 당기는 게 아닌 앞, 뒤에 있는 인접 객체와의 링크만 변경하면 되므로 보다 좋은 성능을 발휘한다.

 

 

[3. Vector]

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

ArrayList와 동일한 내부 구조를 가지고 있고, 차이점은 Vector는 동기화된 메서드로 구성되어 있기 때문에

멀티 스레드가 동시에 Vector() 메서드를 실행할 수 없다.

즉, 멀티 스레드 환경에서는 안전하게 객체를 추가 및 삭제가 가능하다.

만약 벡터 컬렉션에 스레드 객체 2개를 생성하여 값을 할당하였을 때는 값이 하나만 나오거나 에러가 발생할 수 있다.


 

[Set  컬렉션]

순서유지와 객체를 중복 저장이 불가능하다. 그래서 객체를 하나만 저장할 수 있다.

데이터들을 모은 집합이라고 생각하면 편하다.

 

[1. HashSet]

Set<E> set = new HashSet<E>();	// E에 지정된 타입의 객체만 저장
Set<E> set = new HashSet<>();	// E에 지정된 타입의 객체만 저장
Set set = new HashSet<>();	// 모든 타입의 객체만 저장

객체 그 자체를 저장하고 중복을 허용하지 않고, 내부적으로 데이터를 저장할 때 해시 함수를 이용해서 저장한다.

또한 Set컬렉션에 속해있으므로 순서를 보장하지 않기 때문에 데이터의 고유성을 보장한다.

빠른 검색이 필요할 때 주로 사용한다.

또한 HashSet자료구조는 HashMap을 사용해서 데이터의 중복을 없앤다.

객체가 hashCode() 메서드를 통해 해시 값을 생성하고 equals() 메서드를 통해 기존에 저장되어 있는 데이터들의 해시 값과

새로 삽입되는 객체의 해시값을 비교하여 중복된 객체가 있는지 확인한다.

장점으로 검색의 속도가 굉장히 빠르다.

 

객체의 해시나 자료형이든 뭐든 불러와서 같은지 확인

해당 객체의 내용이 같은지 다른지 비교하기 위해서는

같은 객체로 묶고 싶다면 hashCode()로 한번, equals()로 2번 오버라이딩 해줘야 한다.

이렇게 안 해주면 객체가 묶이지 않아서 다른 주소가 나오게 된다.

 


[Map 컬렉션]

Map 컬렉션 : 키와 값으로 구성된 엔트리 객체를 저장한다.

여기서 키와 값은 객체이며 키는 중복 저장이 불가능하지만 값은 중복 저장이 가능하다.

Key를 이용해서 value를 얻을 수 있기 때문에 검색의 개념이 강하다.

만약 기존에 있던 키와 동일한 키로 값을 작성하면 기존의 값은 삭제되고 새로운 값으로 대체된다.

[1. HashMap]

Map<String, Integer> map = new HashMap<String, Integer>();
Map<String, Integer> map = new HashMap<>();

HashSet과 중복된 객체를 확인하는 방법은 같지만 HashMap은 hashCode() 메서드에 Key로 삽입되는 데이터를 넣어

해시 값을 생성한다.

만약 생성된 해시 값을 사용하여 해시 버킷에 접근했을 때 버킷에 데이터가 없으면 넣고 끝나지만,

데이터가 존재한다면 충돌이 발생한다. 

이때 해결방법으로 2가지의 선택지가 존재하는데

1 ) 이미 존재하는 데이터의  Key값들이 서로 같다면  새로운 Value로 교체하거나   

2 ). Key값이 서로 다르다면 이미 존재하는 데이터의 뒤에 새로 넣으려는 데이터를 연결하여 삽입한다.

2개의 방식 모두 Key값을 비교해야 하는데, 이때 equals() 메서드를 사용한다.

HashMap은 내부적으로 중복을 검색하고 제거해 준다.

 

[2. HashTable]

Map<String, Integer> map = new Hashtable<String, Integer>();
Map<String, Integer> map = new Hashtable<>();

HashMap과 동일한 구조를 가지고 있지만 차이점은 동기화된 메서드로 구성되어 있기 때문에 멀티 스레드가 동시에 HashTable의 메서드들을 실행할 수 없다.

  즉, 멀티 스레드 환경에서도 안전하게 객체를 추가, 삭제할 수 있다. -> 벡터와 비슷하다고 볼 수 있다.

 

[3. Properties]

 HashTable의 자식 클래스이므로 HashTable의 특징을 그대로 가지고 있다.

 키와 값을 String 타입으로 제한한 컬렉션이다. 주로 확장자가. properties 파일을 읽을 때 사용한다.

Properties properties = new Properties();
properties.load(~~.class.getResourceAsStream("database.properties"));

위의 코드와 같이 Properties를 사용하면 properties에 선언해 놓은 내용을 코드에서 쉽게 불러와서 사용이 가능하다.

선언한 후 Properties객체를 생성한 후 객체. load()를 통해 해당 파일의 내용을 메모리로 로드하게 된다.


[검색 기능을 강화시킨  컬렉션]

 다른 기능보다는 검색 기능을 강화시킨 TreeSet, TreeMap컬렉션이 존재한다.

 여기서 TreeSet은 set컬렉션이고, TreeMap은 map컬렉션에 속해 있다.

 

[1. TreeSet]

 이진트리를 기반으로 한 Set컬렉션이다.  Set컬렉션은 중복 지원과  순서 유지가 안되고, 객체를 하나만 저장할 수 있다.

 이진트리 구조란 나무가 가지 치듯이 내려보내는 방식을 말한다.

TreeSet<E> treeSet = new TreeSet<E>();
TreeSet<E> treeSet = new TreeSet<>();

 TreeSet에 객체를 저장하면 부모 노드의 객체와 비교했을 때 값이 낮은 것은 왼쪽으로, 높은 것은 오른쪽 자식 노드에 저장된다.

데이터를 넣을 때마다 자동으로 오름차순으로 정렬된다.

머신러닝에 쓰이기 좋은 구조다.

 

[2. TreeMap]

TreeMap<E> treeMap = new TreeMap<E>();
TreeMap<E> treeMap = new TreeMap<>();

 이진 트리를 기반으로 한 Map컬렉션이다. TreeSet과 차이점은 키와 값이 저장된 Entry 객체를 저장한다.

 TreeMap에 엔트리를 저장하면 키를 기준으로 자동 정렬되는데, 부모 노드와 비교해서 낮은 값은 왼쪽, 높은 쪽은 오른쪽 노드로 Entry 객체를 저장한다.

Key를 기준으로 오름차순으로 정렬된다.

 

데이터 자체끼리 비교하면 TreeSet 아니라면 TreeMap이다.

 

[Comparable and Comparator]

 

TreeSet의 객체, Map의 키 객체는 저장과 동시에 오름차 순으로 정렬된다.

모든 객체들이 정렬되는 것이 아니고, 객체가 Comparable 인터페이스를 구현하고 있어야 가능하다.

자료형 타입들은 모두 Comparable를 자동으로 구현하고 있기 때문에 상관이 없지만 

사용자 정의 객체를 저장할 때는 반드시 Comparable를 구현해야 한다.

사용자 정의 객체에서는 compareTo()라는 정의된 메서드를 이용하여 오버라이딩 하여 비교 결과를 정수 값으로 리턴해야 한다.

Comparable 구현 객체는 TreeSet에 저장하거나 TreeMap의 키로 저장하는 것이 원칙이지만, 비교 기능이 없는

Comparable 비구현 객체를 저장하고 싶다면 TreeSet과 TreeMap을 생성할 때 비교자를 만들어주고 메서드를 오버라이딩해서 정수값으로 리턴해주면 된다.


'스터디 > JAVA' 카테고리의 다른 글

[JAVA] 추상 & 메서드  (0) 2024.04.13
[JAVA] Thread  (0) 2024.02.15
[JAVA] 상속  (0) 2024.02.12
[JAVA] 배열  (1) 2024.02.07
[JAVA] 클래스  (1) 2024.02.03