# JAVA Ch11. 컬렉션 프레임웍[1]

uuuu.jini·2022년 3월 16일
0

JAVA -자바의 정석

목록 보기
13/18
post-thumbnail

목차

  1. 컬렉션 프레임웍

1. Collections Framework

컬렉션 프레임웍이란, '데이터 군을 저장하는 클래스들을 표준화한 설계'를 뜻한다. 컬렉션은 다수의 데이터, 즉 데이터 그룹을, 프레임웍은 표준화된 프로그래밍 방식을 의미한다.

컬렉션 프레임웍은 다수의 데이터를 다루는데 필요한 다양하고 풍부한 클래스들을 제공하고 인터페이스와 다형성을 이요한 객체지향적 설계를 통해 표준화되어 있기 때문에 사용법을 익히기에도 편리하고 재사용성이 높은 코드를 작성할 수 있다.

1.1 ] 컬렉션 프레임웍의 핵심 인터페이스

컬렉션 데이터 그룹을 크게 3가지 타입이 존재한다고 인식하고 각 컬렉션을 다루는데 필요한 기능을 가진 3개의 인터페이스를 정의하였다. 그리고 인터페이스 List와 Set의 공통된 부분을 다시 뽑아서 새로운 인터페이스인 Collection을 추가로 정의하였다.

인터페이스특징
List순서가 있는 데이터의 집합, 데이터의 중복을 허용한다.
구현클래스 : ArrayList,LinkedList,Stack,Vector등
Set순서를 유지하지 않는 데이터의 집합, 데이터의 중복을 허용하지 않는다.
구현클래스 : HashSet,TreeSet 등
Map키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합, 순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값은 중복을 허용한다.
구현클래스 : HashMap,TreeMap,Hashtable,Properties 등

컬렉션 프레임웍의 모든 컬렉션 클래스들은 List,Set,Map중의 하나를 구현하고 있으며, 구현한 인터페이스의 이름이 클래스의 이름에 포함되어있어서 이름만으로도 클래스의 특징을 쉽게 알 수 있도록 되어 있다.

그러나 Vector,Stack,Hashtable,Properties와 같은 클래스들은 컬렉션 프레임웍이 만들어지기 이전부터 존재하던 것이기 때문에 컬렉션 프레임웍의 명명법을 따르지 않는다. (Vector와 Hashtable같은 기존의 컬렉션 클래스들은 호환을 위해, 설계를 변경해 남겨두었지만 가능하면 사용하지 않는것이 좋다. )

Collection 인터페이스

List와 Set의 조상인 Collection인터페이스에는 다음과 같은 메서드들이 정의되어 있다.

메서드설명
boolean add(Object o)
boolean addAll(Collection c)
지정된 객체(o) 또는 Collection(c)의 객체들을 Collection에 추가한다.
void clear()Collection의 모든 객체를 삭제한다.
boolean contians(Object o)
boolean containsAll(Collection c)
지정된 객체(o) 또는 Collection의 객체들이 Collection에 포함되어 있는지 확인한다.
boolean equals(Object o)동일한 Collection인지 비교한다.
int hashCode()Collection의 hasCode를 반환한다.
boolean isEmpty()Collection이 비어있는지 확인한다.
iterator iterator()Collection의 Iterator를 얻어서 반환한다.
boolean remove(Object o)지정된 객체를 삭제한다.
boolean removeAll(Collection c)지정된 Collection에 포함된 객체들을 삭제한다.
boolean retainAll(Collection c)지정된 Collection에 포함된 객체만을 남기고 다른 객체들은 Collection에서 삭제한다. 이 작업으로 인해 Collection에 변화가 있으면 true를 그렇지 않으면 false를 반환한다.
int size()Collection에 저장된 객체의 개수를 반환한다.
Object[] toArray()Collection에 저장된 객체를 객체배열(Object[])로 반환한다.
Object[] toArray(Object[] a)지정된 배열에 Collection의 객체를 저장해서 반환한다.

위의 반환타입이 boolean인 메서드들은 작업을 성공하거나 사실이면 true를 그렇지 않으면 false를 반환한다.

List 인터페이스

List인터페이스는 중복을 허용 하면서 저장순서가 유지 되는 컬렉션을 구현하는데 사용된다.

List인터페이스에 정의된 메서드는 다음과 같다. ( Collection인터페이스로부터 상속 제외 )

메서드설명
void add(int index,Object element)
boolean addAll(int index,Collection c)
지정된 위치(index)에 객체(elemente) 또는 컬렉션에 포함된 객체들을 추가한다.
Object get(int index)지정된 위치(index)에 있는 객체를 반환한다.
int indexOf(Object o)지정된 객체의 위치(index)를 반환한다. (첫번째 요소부터 순방향)
int lastIndexOf(Object o)지정된 객체의 위치(index)를 반환한다. ( 마지막 요소부터 역방향 )
ListIterator listIterator()
ListIterator listIterator(int index)
List의 객체에 접근할 수 있는 ListIterator를 반환한다.
Object remove(int index)지정된 위치(index)에 있는 객체를 삭제하고 삭제된 객체를 반환한다.
Object set(int index,Object element)지정된 위치(index)에 객체(element)를 저장한다.
void sort(Comparator c)지정된 비교자(comparator)로 List를 정렬한다.
List subList(int fromIndex,int tolndex)지정된 범위에 있는 객체를 반환한다.

Set 인터페이스

중복을 허용하지 않고 저장순서가 유지되지 않는 컬렉션 클래스를 구현하는데 사용한다. 구현 클래스로는 HashSet,TreeSet등이 있다.

Map 인터페이스

키와 값을 하나의 쌍으로 묶어서 저장하는 컬렉션 클래스를 구현하는데 사용된다. 키는 중복될수 없지만 값은 중복을 허용한다. 기존에 저장된 데이터와 중복된 키와 값을 저장하면 기존의 값은 없어지고 마지막에 저장된 값이 남게 된다. 구현한 클래스로는 Hashtable,HashMap,LinkedHashMap,SortedMap,TreeMap등이 있다.

메서드설명
void clear()Map의 모든 객체를 삭제한다.
boolean containsKey(Object Key)지정된 key객체와 일치하는 Map의 Key객체가 있는지 확인한다.
boolean containsValue(Object value)지정된 value객체와 일치하는 Map의 value객체가 있는지 확인한다.
Set entrySet()Map에 저장되어 있는 key-value쌍을 Map.Entry타입의 객체로 저장한 Set으로 반환한다.
boolean equals(Object o)동일한 Map인지 비교한다.
Object get(Object key)지정한 key객체에 대응하는 객체를 찾아서 반환한다.
int hashCode()해시코드를 반환
boolean isEmpty()Map이 비어있는지 확인한다.
Set keySet()Map에 저장된 모든 key객체를 반환한다.
Object put(Object ket,Object value)Map에 value객체를 key객체에 연결하여 저장한다.
void putAll(Map t)지정된 Map의 모든 key-value쌍을 추가한다.
Object remove(Object key)지정한 key객체와 일치하는 key-value객체를 삭제한다.
int size()Map에 저장된 key-value쌍의 개수를 반환한다.
Collection values()Map에 저장된 모든 value객체를 반환한다.

values()에서는 반환타입이 Collection이고, keySet()에서는 반환타입이 Set이다.

Map.Entry 인터페이스

Map인터페이스의 내부인터페이스로 Map에 저장되는 key-value쌍을 다루기 위해 내부적으로 Entry인터페이스를 정의해놓았다.

메서드설명
boolean equals(Object o)동일한 Entry인지 비교한다.
Object getKey()Entry의 key객체를 반환한다.
Object getValue()Entry의 value객체를 반환한다.
int hashCode()해시코드반환
Object setValue(Object value)Entry의 value객체를 지정된 객체로 바꾼다.

1.2 ] ArrayList

List인터페이스를 구현하기 때문에 데이터의 저장순서가 유지되고 중복을 허용한다. ArrayList는 Object배열을 이용해서 데이터를 순차적으로 저장한다. 배열에 더 이상 저장할 공간이 없으면 보다 큰 새로운 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장된다. elementData라는 이름의 Object배열을 멤버변수로 선언하고 있다. (모든 종류의 객체를 담을 수 있다.)

메서드설명
ArrayList()크기가 10인 ArrayList를 생성
ArrayList(Collection c)주어진 컬렉션이 저장된 ArrayList생성
ArrayList(int initialCapacity)지정된 초기용량을 갖는 ArrayList를 생성
boolean add(Object o)ArrayList의 마지막에 객체를 추가. 성공하면 true
void add(int index,Object element)지정된 위치(index)에 객체를 저장
boolean addAll(Collection c)주어진 컬렉션의 모든 객체를 저장
boolean addAll(int index,Collection c)지정된 위치부터 주어진 컬렉션의 모든 객체를 저장
void clear()ArrayList를 완전히 비운다.
Object clone()ArrayList를 복제
boolean contains(Object o)지정된 객체(o)가 ArrayList에 포함되어 있는지 확인
void ensureCapacity(int minCapacity)ArrayList의 용량이 최소한 minCapacity가 되도록 한다.
Object get(int index)지정된 위치(index)에 저장된 객체를 반환한다.
int indexOf(Object o)지정된 객체가 저장된 위치를 찾아 반환
boolean isEmpty()ArrayList가 비어있는지 확인
iterator iterator()ArrayList의 iterator객체를 반환
int lastIndexOf(Objet o)지정된 객체의 위치를 끝부터 역방향으로 검색
ListIterator listIterator()ArrayList의 ListItearator를 반환
ListIterator listIterator(int index)ArrayList의 지정된 위치부터 식작하는 ListIterator를 반환
Object remove(int index)지정된 위치에 있는 객체를 제거
boolean remove(Object o)지정된 객체 제거
boolean removeAll(Collection c)지정한 컬렉션에 저장된 것과 동일한 객체들을 ArrayList에서 제거
boolean retainAll(Collection c)ArrayList에 저장된 객체중에서 주어진 컬렉션과 공통된것만 남기고 나머지 삭제
Object set(int index,Object element)주어진 객체를 지정된 위치에 저장
int size()ArrayList에 저장된 객체의 수를 반환
void sort(Comparator c)지정된 정렬기준으로 정렬
List subList(int fromIndex,int toIndex)fromIndex부터 toIndex사이에 저장된 객체를 반환
Object[] toArray()ArrayList에 저장된 모든 객체들을 객체배열로 반환
Object[] toArray(Object[] a)ArrayList에 저장된 모든 객체들을 객체배열 a에 담아 반환
void trimToSize()용량을 크기에 맞게 줄인다. (빈공간 없애기)

ArrayList를 생성할 떄, 저장할 요소의 개수를 고려해서 실제 저장할 개수보다 약간 여유있는 크기로 하는 것이 좋다 .자동적으로 크기가 늘어나기는 하지만 처리시간이 많이 소요되기 때문이다.

ArrayList나 Vector같이 배열을 이용한 자료구조는 데이터를 읽어오고 저장하는 데는 효율이 좋지만, 용량을 변경해야 할 때는 새로운 배열을 생성한 후 기존의 배열로부터 새로 생성된 배열로 데이터를 복사해야 하기 때문에 상당히 효율이 떨어진다는 단점을 가지고 있다.

1.3 ] LinkedList

배열은 가장 기본적인 형태의 자료구조로 간단하며 사용하기 쉽고 데이터를 읽어오는데 걸리는 시간이 가장 빠르다는 장점을 가지고 있지만 다음과 같은 단점도 있다.

1. 크기를 변경할 수 없다.

  • 크기를 변경할 수 없으므로 새로운 배열을 생성해서 데이터를 복사해야 한다.
  • 실행 속도를 향상시키기 위해서는 충분히 큰 크기의 배열을 생성해야 하므로 메모리가 낭비된다.

    2. 비순차적인 데이터의 추가 또는 삭제에 시간이 많이 걸린다.
  • 차례대로 데이터를 추가하고 마지막에서부터 데이터를 삭제하는 것은 빠르지만,
  • 배열의 중간에 데이터를 추가하려면, 빈자리를 만들기 위해 다른 데이터들을 복사해서 이동해야 한다.

이러한 배열의 단점을 보완하기 위해 Linked List라는 자료구조가 고안되었다. 배열은 모든 데이터가 연속적으로 존재하지만 링크드 리스트는 불연속적으로 존재하는 데이터를 서로 연결한 형태로 구성되어 있다.

링크드 리스트의 각 요소들은 자신과 연결된 다음 요소에 대한 참조와 데이터로 구성되어 있다.

class Node{
	Node next; // 다음 요소의 주소
    Ovject obj; //  데이터를 저장
}

링크드 리스트의 데이터 삭제는 간단하다. 삭제하고자 하는 요소의 이전요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하기만 하면 된다. (복사하는 과정이 없다.)

새로운 데이터를 추가할 때는 새로운 요소를 생성한 다음 추가하고자 하는 위치의 이전 요소의 참조를 새로운 요소에 대한 참조로 변경해주고, 새로운 요소가 그 다음 요소를 참조하도록 변경하기만 하면 되므로 처리속도가 매우 빠르다.

더블 링크드 리스트는 단순히 링크드 리스트에 참조변수를 하나 더 추가하여 다음 요소에 대한 참조 뿐만 아니라 이전 요소에 대한 참조가 가능하도록 하여 각 요소에 대한 접근과 이동이 쉽다.

class Node{
	Node next;
    Node previous;
    Object obj;
}

더블 써큘러 링크드 리스트는 더블 링크드 리스트의 첫 번째 요소와 마지막 요소를 서로 연결시킨 것이다.

메서드설명
Object elemente()LinkedList의 첫번째 요소를 반환
boolean offer(Object o)지정된 객체(o)를 linkedList의 끝에 추가. 성공하면 true
Object peek()LinkedList의 첫번째 요소를 반환
Object poll()LinkedList의 첫번째 요소를 반환. 제거
Object remove()LinkedList의 첫번째 요소를 제거

결론1 : 순차적으로 추가/삭제 하는 경우에는 ArrayList가 LinkedList보다 빠르다.
결론2 : 중간 데이터를 추가/삭제하는 경우에는 LinkedList가 ArrayList보다 빠르다.

배열은 각 요소들이 연속적으로 메모리상에 존재하기 때문에 간단한 계산만으로 원하는 요소의 주소를 얻어서 저장된 데이터를 곧바로 읽어올 수 있지만, LinkedList는 불연속적으로 위치한 각 요소들이 서로 연결된 것이라 처음부터 n번째 데이터까지 차례대로 따라가야만 원하는 값을 얻을 수 있다.

다루고자 하는 데이터의 개수가 변하지 않는 경우라면 ArrayList가 최상의 선택이 되겠지만, 데이터 개수의 변경이 잦다면 LinkedList를 사용하는 것이 더 나은 선택이 될 것이다.

1.4 ] Stack과 Queue

스택은 마지막에 저장한 데이터를 가장 먼저 꺼내게 되는 LIFO 구조 이고 ,큐는 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO 구조로 되어 있다. 순차적으로 데이터를 추가하고 삭제하는 스택에는 ArrayList와 같은 배열기반의 컬렉션 클래스가 적합하지만, 큐는 데이터를 꺼낼 때 항상 첫 번째 저장된 데이터를 삭제하므로, ArrayList와 같은 배열기반의 컬렉션 클래스를 사용한다면 데이터를 꺼낼 때마다 빈 공간을 채우기 위해서 데이터의 복사가 발생하므로 비효율적이다. 그래서 큐는 데이터의 추가/삭제가 쉬운 LinkedList로 구현하는 것이 더 적합하다.

Stack 의 메서드

메서드설명
boolean empty()Stack이 비어있는지 알려준다.
Object peek()맨위에 저장된 객체를 반환. 객체를 꺼내지는 않음
Object pop()맨위에 저장된 객체를 꺼낸다.
Object push(Object item)객체를 저장
int search(Object o)Stack에 주어진 객체를 찾아서 그 위치를 반환 . 못찾으면 -1을 반환 ,배열과 달리 0이아닌 1부터 시작

Queue의 메서드

메서드설명
boolean add(Object o)지정된 객체를 Queue에 추가한다. 성공시 true반환
Object remove()객체를 꺼내 반환, 비어있으면 에러
Object element()삭제없이 요소를 읽어온다. 비어있으면 에러
boolean offer()객체를 저장 , 성공시 true
Object poll()객체를 꺼내서 반환, 비어있으면 null을 반환
Object peek()삭제없이 요소를 읽어옴, 비어있으면 null을 반환

자바에서는 스택을 Stack클래스로 구현하여 제공하고 있지만 큐는 Queue인터페이스로만 정의해 놓았을 뿐 별도의 클래스를 제공하고 있지 않다. 대신 Queue인터페이스를 구현한 클래스들이 있어서 이 들 중의 하나를 선택해서 사용하면된다.

Queue인터페이스에 정의된 기능을 사용하고 싶다면, All known Implementing classes에 적혀있는 클래스들 중에서 적당한 것을 하나 골라 잡아서 Queue q = new LinkedList()와 같은 식으로 객체를 생성해서 사용하면 된다.

스택과 큐의 활용

스택의 활용 예 - 수식계산,수식괄호검사,워드프로세서의 undo/redo,웹브라우저의 뒤로/앞으로
큐의 활용 예 - 최근사용문서, 인쇄작업 대기목록, 버퍼(buffer)

Priority Queue : 우선순위가 높은 것 부터 꺼내는 Queue인터페이스의 구현체 중의 하나이다. null저장이 안되며, '힙' 이라는 자료구조의 형태로 저장한다.
Deque : 양쪽 끝에 추가/삭제가 가능한 Queue의 변현이다.

1.5 ] Iterator, ListIterator, Enumeration

Iterator,ListItrator,Enumeration은 모두 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다. Enumeration은 Itrator의 구버젼이며, ListIterator는 Iterator의 기능을 향상 시킨 것이다.

Iterator

컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의하고, Collection인터페이스는 Iterator(Iterator를 구현한 클래스의 인스턴스)를 반환하는 iterator()를 정의하고 있다.

iterator()는 Collection인터페이스에 정의된 메서드이므로 Collection인터페이스의 자손인 List와 Set에도 포함되어 있다. 그래서 List나 Set인터페이스를 구현하는 컬렉션은 iterator()가 각 컬렉션의 특징에 알맞게 작성되어 있다. 컬렉션 클래스에 대해 iterator()를 호출하여 Iterator를 얻응 다음 반복문,주로 while문을 사용해서 컬렉션 클래스의 요소들을 읽어올 수 있다.

메서드설명
boolean hasNext()읽어 올 요소가 남아있는지 확인한다. 있으면 true
Object next()다음 요소를 읽어온다. (호출전 hasNext를 호출하여 읽어 올 요소 확인
void remove()next()로 일어 온 요소를 삭제한다. next()를 호출한 다음에 remove()를 호출해야 한다. (선택적 기능 )

ArrayList에 저장된 요소들을 출력하기 위한 코드는 다음과 같다.

Collection c = new ArrayList();
Iterator it = c.iterator();

while(it.hasNext()){
	System.out.println(it.next());
}

다른 컬렉션 클래스에 대해서도 이와 동일한 코드를 사용할 수 있다.

Map인터페이스를 구현한 클래스는 키(key)와 값(value)의 쌍(pair)으로 저장하고 있기 때문에 iterator()를 직접 호출할 수 없고, 그 대신 keySet() 이나 entrySet() 과 같은 메서드를 통해서 키와 값을 각각 따로 Set의 형태로 얻어 온 후에 다시 iterator()를 호출해야 Iterator를 얻을 수 있다.

Map map = new HashMap();
...
Iterator it = map.entrySet().iterator();

List클래스들은 저장순서를 유지하기 때문에 Iterator를 이용해서 읽어 온 결과 역시 저장순서와 동일하지만 Set클래스들은 각 요소간의 순서가 유지되지 않기 때문에 Iterator를 이용해서 저장된 요소들을 읽어 와도 처음에 저장된 수넛와 같지 않다.

ListIterator와 Enumeration

Enumeration은 Iterator의 구버전이므로 Iterator를 사용하자.

ListIterator는 Iterator를 상속받아서 기능을 추가한 것으로, 컬렉션의 요소에 접근할 때 Iterator는 단방향으로만 이동할 수 있는데 반해 ListIterator는 양방향으로의 이동이 가능하다. (List인터페이스를 구현한 컬렉션에서만 가능 )

1.6 ] Arrays

Arrrays클래스에는 배열을 다루는데 유용한 메서드가 정의되어 있다. Arrays에 정의되어 있는 toString()에는 모든 기본형 배열과 참조형 배열 별로 하나씩 정의되어 있다. 앞으로 매개변수의 타입이 int배열인 메서드에 대한 사용법만 살펴본다.

배열의 복사 - copyOf(),copyOfRange()

copyOf()는 배열전체를 copyOfRange()는 배열의 일부를 복사해서 새로운 배열을 만들어 반환한다. (copyOfRange()의 지정된 범위의 끝은 포함되지 않는다. )

배열 채우기 - fill(),setAll()

fill()은 배열의 모든 요소를 지정된 값으로 채운다. setAll()은 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다. 이 메서드 호출시 함수형 인터페이스를 구현한 객체나 람다식을 매개변수로 지정해야 한다.

int[] arr = new int[5];
Arrays.fill(arr,9); // arr = [9,9,9,9,9]
Arrays.setAll(arr,()->(int)(Math.random()*5)+1); 

setAll()메서드는 이 람다식이 반환한 임의의 정수로 배열을 채운다.

배열의 정렬과 검색 - sort(),binarySearch()

sort()는 배열을 정렬할 떄 , binarySearch()는 배열에 저장된 요소를 검색할 때 사용한다. binarySearch()는 배열에서 지정된 값이 저장된 위치를 찾아서 반환하는데, 반드시 배열이 정렬된 상태이어야 올바른 결과를 얻는다. ( 만일 여러개 일 경우 어떤것이 반환될지 모름 )


int[] arr = {3,2,0,1,4};
Arrays.sort(arr); //정렬
int idx = Arrays.binarySearch(arr,2); // idx = 2

이진 검색

배열의 검색할 범위를 반복적으로 절반씩 줄여가면서 검색하기 때문에 검색의속도가 상당히 빠르다. 배열의 길이가 10배가 늘어나도 검색횟수는 3~4회 밖에 늘어나지 않으므로 큰 배열의 검색에 유리하다. 단, 배열정렬 필수

배열의 비교와 출력 - equals(),toString()

toString()배열의 모든 요소를 문자열로 편하게 출력할 수 있다. (다차원 배열 - deepToString()) equals()는 두 배열에 저장된 모든 요소를 비교해서 같으면 true,다르면 false를 반환한다. ( 다차원 배열 - deepEquals() )

배열을 List로 반환 - asList(Object... a)

asList()는 배열을 List에 담아서 반환한다. 매개변수의 타입이 가변인수라서 배열 생성없이 저장할 요소들만 나열하는 것도 가능하다.

List list = Arrays.asList(new Integer[]{1,2,3,4,5});
List list = Arrays.asList(1,2,3,4,5);

asList()가 반환한 List의 크기를 변경할 수 없다. 즉 추가 또는 삭제가 불가하다. 저장 내용의 변경은 가능 ( 만약 크기 변경 List 필요시에는 List list = new ArrayList(Arrays.asList(1,2,3,4,5));를 사용한다. )

parallelXXX(),spliterator(),stream()

parallel로 시작하는 이름의 메서드들은 보다 빠른 결과를 얻기 위해 여러 쓰레드가 작업을 나누어 처리하도록 한다. splitrator()는 여러 쓰레드가 처리할 수 있게 하나의 작업을 여러 작업으로 나누는 Spliterator를 반환하며, stream()은 컬렉션을 스트림으로 변환한다.

profile
멋쟁이 토마토

0개의 댓글