본문 바로가기
Study/JAVA

[Java] 11. 컬렉션 프레임웍(1)

by jeongwle 2022. 9. 27.
728x90
반응형

 

컬렉션 프레임웍(Collections Framework)

컬렉션 프레임웍은 '데이터 군을 저장하는 클래스들을 표준화한 설계'를 의미한다. JDK1.2 이전까지는 다수의 데이터를 저장할 수 있는 클래스들을 서로 다른 각자의 방식으로 처리해야 했다. JDK1.2부터 컬렉션 프레임웍이 등장하면서 모든 컬렉션 클래스를 표준화된 방식으로 다룰 수 있도록 체계화되었다.

 

1. 컬렉션 프레임웍의 핵심 인터페이스

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

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

컬렉션 프레임웍의 모든 컬렉션 클래스들은 List, Set, Map 중 하나를 구현하고 있으며, 구현한 인터페이스의 이름이 클래스의 이름에 포함되어있어 이름만으로도 클래스의 특징을 쉽게 알 수 있도록 되어있다. 그러나 Vector, Stack, Hashtable, Properties와 같은 클래스들은 컬렉션 프레임웍 이전부터 존재했기 때문에 컬렉션 프레임웍의 명명법을 따르지 않는다. Vector나 Hashtable과 같은 기존의 컬렉션 클래스들은 호환을 위해 설계를 변경해 남겨두었지만 가능하면 사용하지 않는 것이 좋다. 그 대신 새로 추가된 ArrayList와 HashMap을 사용하자.

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

메서드 설 명
 boolean add(Object o)
 boolean addAll(Collection c)
 지정된 객체(o) 또는 Collection(c)의 객체들을 Collection에 추가
 void clear()  Collection의 모든 객체를 삭제
 boolean contains(Object o)
 boolean contains(Collection c)
 지정된 객체(o) 또는 Collection의 객체들이
 Collection에 포함되어 있는지 확인
 boolean equals(Object o)  동일한 Collection인지 비교
 int hashCode()  Collection의 hash code를 반환
 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의 객체를 저장해서 반환
* JDK1.8부터 추가된 parallelStream, removeIf, stream, forEach 등은 14장에서 설명


List 인터페이스
List인터페이스는 중복을 허용하면서 저장 순서가 유지되는 컬렉션을 구현하는 데 사용된다. List인터페이스에 정의된 메서드를 살펴보자. Collection인터페이스로부터 상속받은 것들은 제외되어 있다.

메서드 설 명
 void add(int index, Object element)
 boolean addAll(int index, Collection c)
 지정된 위치(index)에 객체(element) 또는
 컬렉션에 포함된 객체들을 추가한다.
 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 toIndex)  지정된 범위(from ~ to)에 있는 객체를 반환한다.

Set 인터페이스
Set인터페이스는 중복을 허용하지 않고 저장 순서가 유지되지 않는 컬렉션 클래스를 구현하는 데 사용된다.

Map 인터페이스
Map인터페이스는 키(key)와 값(value)을 하나의 쌍으로 묶어서 저장하는 컬렉션 클래스를 구현하는 데 사용된다. 키는 중복될 수 없지만 값의 중복은 가능하다. Map인터페이스를 구현한 클래스로는 Hashtable, HashMap, LinkedHashMap, SortedMap, TreeMap 등이 있다.

메서드 설명
 void clear()  Map의 모든 객체를 삭제
 boolean containsKey(Object key)  지정된 key객체와 key 객체가 있는지 확인
 boolean containsValue(Object value)  지정된 value객체와 일치하는 객체가 있는지 확인
 Set entrySet()  Map에 저장된 key-value쌍을 Map.Entry타입의 객체로
 저장한 Set을 반환
 boolean equals(Object o)  동일한 Map인지 비교
 Object get(Object key)  지정한 key객체에 대응하는 value객체를 반환
 int hashCode()  해시코드를 반환
 boolean isEmpty()  Map이 비어있는지 확인
 Set keySet()  Map에 저장된 모든 key객체를 반환
 Object put(Object key, Object value)  Map에 key객체와 value객체를 연결(mapping)하여 저장
 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인터페이스에서 값은 중복을 허용하기 때문에 Collection타입으로 반환하고 키는 중복을 허용하지 않기 때문에 Set타입으로 반환한다.

Map.Entry 인터페이스
Map.Entry인터페이스는 Map인터페이스의 내부 인터페이스다. 내부 클래스와 같이 인터페이스도 인터페이스 안에 인터페이스를 정의할 수 있다. Map에 저장되는 key-value쌍을 다루기 위해 정의되었기 때문에 Map인터페이스를 구현하는 클래스에서는 Map.Entry인터페이스도 함께 구현해야 한다.

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

 

2. ArrayList

ArrayList는 기존의 Vector를 개선한 것으로 Vector와 구현원리와 기능적인 측면에서 동일하다고 할 수 있다. ArrayList는 Object배열을 이용해서 데이터를 순차적으로 저장한다.

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

 

3. LinkedList

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

1. 크기를 변경할 수 없다.
  - 크기를 변경할 수 없어 새로운 배열을 생성하여 데이터를 복사해야한다.
  - 실행속도를 향상시키기 위해서는 충분히 큰 크기의 배열을 생성해야하기에 메모리가 낭비된다.
  
2. 비순차적인 데이터의 추가 또는 삭제에 시간이 많이 걸린다.
  - 차례대로 데이터를 추가하고 마지막에서부터 데이터를 삭제하는 것은 빠르지만
    배열의 중간에 데이터를 추가하려면 빈자리를 만들기 위해 다른 데이터들을 복사해서
    이동해야 한다.​


이러한 배열의 단점을 보완하기 위해 링크드리스트(Linked List)라는 자료구조가 고안되었다. 링크드 리스트는 불연속적으로 존재하는 데이터를 서로 연결(link)한 형태로 구성되어있다. 링크드 리스트의 각 요소(node)들은 자신과 연결된 다음 요소에 대한 참조(주소값)와 데이터로 구성되어 있다.

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


그렇기 때문에 데이터의 삭제가 간단하고 배열처럼 데이터를 이동하기 위해 복사하는 과정이 없어 처리속도가 빠르다. 링크드 리스트는 이동방향이 단방향이기 때문에 이전 요소에 대한 접근이 어렵다. 이 점을 보완한 것이 더블 링크드 리스트(doubly linked list)이다. 링크드 리스트의 요소에 이전 요소의 주소를 담는 참조변수 하나를 더 추가하였다. 그리고 마지막으로 이보다 접근성을 더 향상한 것이 더블 써큘러 링크드 리스트(doubly circular linked list)인데 단순히 더블 링크드 리스트의 첫 번째 요소와 마지막 요소를 서로 연결시킨 것이다. 마지막 요소의 다음 요소가 첫 번째 요소가 되고 첫 번째 요소의 이전 요소가 마지막 요소가 된다.

생성자 또는 메서드 설 명
 LinkedList()  LinkedList 객체 생성
 LinkedList(Collection c)  주어진 컬렉션을 포함하는 LinkedList객체 생성
 boolean add(Object o)  지정된 객체를 끝에 추가
 void add(int idx, Object element)  지정된 위치에 객체를 추가
 boolean addAll(Collection c)  주어진 컬렉션의 모든 요소를 끝에 추가
 boolean addAll(int idx, Collection c)  지정된 위치에 주어진 컬렉션의 모든 요소를 추가
 void clear()  LinkedList의 모든 요소 삭제
 boolean contains(Object o)  지정된 객체가 포함되어있는지의 여부
 boolean containsAll(Collection c)  지정된 컬렉션의 모든 요소가 포함되어있는지의 여부
 Object get(int index)  지정된 위치의 객체를 반환
 int IndexOf(Object o)  지정된 객체의 위치를 앞에서부터 탐색하여 반환
 boolean isEmpty()  비어있는지의 여부
 Iterator iterator()  Iterator를 반환
 int lastIndexOf(Object o)  지정된 객체의 위치를 뒤에서부터 탐색하여 반환
 ListIterator listIterator()  ListIterator를 반환
 ListIterator listIterator(int index)  지정된 위치에서부터 시작하는 ListIterator를 반환
 Object remove(int index)  지정된 위치의 객체를 제거
 boolean remove(Object o)  지정된 객체를 제거
 boolean removeAll(Collection c)  지정된 컬렉션의 요소와 일치하는 요소 모두 제거
 boolean retainAll(Collection c)  지정된 컬렉션의 요소와 일치하는 요소 제외 모두 제거
 Object set(int index, Object element)  지정된 위치의 객체를 주어진 객체로 교체
 int size()  LinkedList에 저장된 객체의 수를 반환
 List subList(int from, int to)  from부터 to사이의 객체를 List로 반환
 Object[] toArray()  LinkedList에 저장된 객체를 배열로 반환
 Object[] toArray(Object[] a)  LinkedList에 저장된 객체를 배열 a에 담아 반환
 Object element()  맨 처음 요소를 반환
 boolean offer(Object o)  지정된 객체를 끝에 추가
 Objeckt peek()  첫 번째 요소를 반환
 Object poll()  첫 번째 요소를 반환하고 LinkedList에서 제거
 Object remove()  첫 번째 요소 제거
 void addFirst(Object o)  지정된 객체를 맨 앞에 추가
 void addLast(Object o)  지정된 객체를 맨 뒤에 추가
 Iterator descendingIterator()  역순 이터레이터 반환
 Object getFirst()  첫 번째 요소 반환
 Object getLast()  마지막 요소 반환
 boolean offerFirst(Object o)  맨 앞에 객체를 추가
 boolean offerLast(Object o)  맨 뒤에 객체를 추가
 Object peekFirst()  첫 번째 요소를 반환
 Object peekLast()  마지막 요소를 반환
 Object pollFirst()  첫 번째 요소를 반환하면서 제거
 Object pollLast()  마지막 요소를 반환하면서 제거
 Object pop()  첫 번째 요소를 제거
 void push(Object o)  맨 앞에 객체를 추가
 Object removeFirst()  첫 번째 요소를 제거
 Object removeLast()  마지막 요소를 제거
 boolean removeFirstOccurrence(Object o)  첫 번째로 일치하는 객체를 제거
 boolean removeLastOccurrence(Obejct o)  마지막으로 일치하는 객체를 제거

 

4. Stack과 Queue

스택은 마지막에 저장한 데이터를 가장 먼저 꺼내게 되는 LIFO(Last In First Out)구조로 되어 있고, 큐는 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out)구조로 되어 있다.

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

메서드 설 명
 boolean add(Object o)  지정된 객체를 Queue에 추가
 저장공간 부족 시 IllegalStateException 발생
 Object remove()  Queue에서 객체를 꺼내 반환
 비어있을 경우 NoSuchElementException 발생
 Object element()  삭제없이 요소를 읽어온다
 비어있을 경우 NosuchElementException 발생
 boolean offer(Object o)  Queue에 객체를 저장
 Object poll()  Queue에서 객체를 꺼내서 반환
 비어있을 경우 null을 반환
 Object peek()  삭제없이 요소를 읽어온다
 비어있을 경우 null을 반환

스택과 큐의 활용

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


PriorityQueue
Queue인터페이스의 구현체 중 하나이다. 저장한 순서에 관계없이 우선순위(priority)가 높은 것 부터 꺼내게 된다는 특징이 있다. 그리고 null을 저장할 수 없다. 만일 저장을 시도한다면 NullPointerException이 발생한다. PriorityQueue는 저장공간으로 배열을 사용하고 각 요소를 힙(heap)이라는 자료구조의 형태로 저장한다. 힙은 이진 트리의 한 종류로 가장 큰 값이나 가장 작은 값을 빠르게 찾을 수 있다는 특징이 있다. 우선순위는 숫자가 작을수록 높다. 숫자 뿐만 아니라 객체도 저장이 가능하다. 하지만 객체를 저장할 떄에는 각 객체의 크기를 비교할 수 있는 방법을 제공해야한다.

Deque(Double-Ended Queue)
Queue의 변형으로 한 쪽 끝으로만 추가/삭제할 수 있는 Queue와 달리 Deque은 양쪽 끝에 추가/삭제가 가능하다. Deque의 상위는 Queue이며 구현체로는 ArrayDeque과 LinkedList 등이 있다. 덱은 스택과 큐를 하나로 합쳐놓은 것과 같으며 스택으로 사용할 수도 있고 큐로 사용할 수도 있다.

Deque Queue Stack
 offerLast()  offer()  push()
 pollLast()  -  pop()
 pollFirst()  poll()  -
 peekFirst()  peek()  
 peekLast()  -  peek()

 

5. Iterator, ListIterator, Enumeration

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

Iterator
컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의하고 Collection인터페이스에는 Iterator를 반환하는 iterator()를 정의하고 있다. iterator()는 Collection인터페이스에 정의된 메서드이므로 Collection인터페이스의 하위인 List와 Set에도 포함되어 있다.

메서드 설 명
 boolean hasNext()  읽어 올 요소가 남아있는지 확인
 Object next()  다음 요소를 읽어온다. hasNext()로 확인 후 가져오는 것이 안전
 void remove()  next()로 읽어온 요소를 삭제한다. next()를 호출 후 remove()를 호출해야함

Collection c = new ArrayList();	// 다른 컬렉션으로 변경 시 이부분만 고치면 된다.
Iterator it = c.iterator();

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

Map map = new HashMap();

Iterator it = map.entrySet().iterator();

Iterator를 사용하는 간단한 예시이다. map은 키와 값을 쌍으로 저장하고 있기 때문에 iterator()을 직접 호출할 수 없다. 그래서 ketSet()이나 entrySet()을 이용해 키와 값을 각각 따로 Set의 형태로 얻은 후 iterator()를 호출해서 Iterator을 얻을 수 있다.

ListIterator와 Enumeration
Enumeration은 컬렉션 프레임웍이 만들어지기 전에 사용하던 것으로 Iterator의 구버전이라고 생각하면 된다. 이전 버전의 소스와의 호환을 위해 남겨두고 있을 뿐이므로 가능하면 Iterator를 사용하자. ListIterator는 Iterator를 상속받아서 기능을 추가한 것으로 Iterator는 단방향으로만 이동할 수 있지만 ListIterator는 양방향으로 이동이 가능하다. 다만 ArrayList나 LinkedList와 같이 List인터페이스를 구현한 컬렉션에서만 사용할 수 있다.

메서드(Enumeration) 설 명
 boolean hasMoreElements()  읽어 올 요소가 남아있는지 확인
 Object nextElement()  다음 요소를 읽어옴


메서드(ListIterator) 설 명
 void add(Object o)  컬렉션에 새로운 객체(o)를 추가
 boolean hasNext()  읽어 올 다음 요소가 남아있는지 확인
 boolean hasPrevious()  읽어 올 이전 요소가 남아있는지 확인
 Object next()  다음 요소를 읽어옴
 Object previous()  이전 요소를 읽어옴
 int nextIndex()  다음 요소의 index를 반환
 int previousIndex()  이전 요소의 index를 반환
 void remove()  next() 또는 previous()로 읽어 온 요소를 삭제
 void set(Object o)  next() 또는 previous()로 읽어 온 요소를 지정된 객체(o)로 변경

 

6. Arrays

Arrays클래스에는 배열을 다루는데 유용한 메서드가 정의되어 있다. int로 설명을 하지만 모든 기본형 배열과 참조형 배열 모두 동일하다.

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

int[] arr = {0, 1, 2, 3, 4};
int[] arr2 = Arrays.copyOf(arr, arr.length);	// arr2=[0,1,2,3,4]
int[] arr3 = Arrays.copyOf(arr, 3);		// arr3=[0,1,2]
int[] arr4 = Arrays.copyOf(arr, 7);		// arr4=[0,1,2,3,4,0,0]
int[] arr5 = Arrays.copyOfRange(arr, 2, 4);	// arr5=[2,3]
int[] arr6 = Arrays.copyOfRange(arr, 0, 7);	// arr6=[0,1,2,3,4,0,0]


배열 채우기 - 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);	// arr=[1,5,2,1,1]​

위에서 사용된 () -> (int)(Math.random() * 5) + 1은 람다식(lambda expression)이다. 지금은 그냥 이런게 있구나 하고 넘어가자.

배열의 정렬과 검색 - sort(), binarySearch()
sort()는 배열을 정렬할 때, binarySearch()는 배열에 저장된 요소를 검색할 때 사용한다. binarySearch()는 배열에서 지정된 값이 저장된 위치(index)를 찾아서 반환하는데 반드시 배열이 정렬된 상태이어야 올바른 결과를 얻는다. 만약 검색한 값과 일치하는 요소들이 여러개 있다면 이 중 어떤 요소의 위치가 반환될지는 알 수 없다.

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

배열의 첫 번째 요소부터 순서대로 하나씩 검색하는 것을 순차 검색(linear search)이라 하는데 이 방법은 배열이 정렬되어 있을 필요는 없지만 시간이 많이 걸린다. 반면에 이진 검색(binary search)은 배열의 검색할 범위를 반복적으로 절반씩 줄여가면서 검색하기 때문에 검색속도가 빠르다. 다만 배열이 정렬이 되어 있을 경우에만 사용할 수 있다.

배열의 비교와 출력 - equals(), toString()
toString()은 배열의 모든 요소를 문자열로 편하게 출력할 수 있다. 일차원 배열에서만 사용할 수 있으므로 다차원 배열에서는 deepToString()을 사용하자. equals()는 두 배열에 저장된 모든 요소를 비교해서 같은 true, 다르면 false를 반환한다. equals()또한 일차원 배열에서만 사용가능하고 다차원 배열의 비교는 deelEquals()를 사용하자.

배열을 List로 변환 - asList(Object... a)
asList()는 배열을 List에 담아서 반환한다. 매개변수의 타입이 가변인수라서 배열을 생성하지 않고 저장할 요소들만 나열하는 것도 가능하다. 다만 asList()로 반환된 List의 크기를 변경할 수 없다. 추가 / 삭제가 불가능하다. 크기를 변경하고 싶다면 새롭게 만들면 된다.

List list = Arrays.asList(1, 2, 3, 4, 5);	// list = [1, 2, 3, 4, 5]
list.add(6);					// UnsupportedOperationException

List list = new ArrayList(Arrays.asList(1,2,3,4,5));
list.add(6);					// OK


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

 

7. Comparator와 Comparable

Arrays.sort()를 호출하면 배열을 알아서 정렬하는 것처럼 보이지만 Character클래스의 Comparable의 구현에 의해 정렬되는 것이다. Comparator와 Comparable은 모두 인터페이스로 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있고 Comparable을 구현하고 있는 클래스들은 같은 타입의 인스턴스끼리 서로 비교할 수 있는 클래스들, 주로 Integer와 같은 wrapper클래스와 String, Date, File과 같은 것들이며 기본적으로 오름차순으로 정렬되도록 구현되어 있다. 그래서 Comparable을 구현한 클래스는 정렬이 가능하다는 것을 의미한다.

Comparable 기본 정렬기준을 구현하는데 사용
Comparator 기본 정렬기준 외에 다른 기준으로 정렬하고자 할 때 사용​


import java.util.Arrays;
import java.util.Comparator;

public class ComparatorEx {
    public static void main(String[] args) {
        String[] strArr = {"cat", "Dog", "lion", "tiger"};

        Arrays.sort(strArr);
        System.out.println("Arrays.toString(strArr) = " + Arrays.toString(strArr));
        // Dog, cat, lion, tiger

        Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); // 대소문자 구분안함
        System.out.println("Arrays.toString(strArr) = " + Arrays.toString(strArr));
        // cat, Dog, lion, tiger

        Arrays.sort(strArr, new Descending());
        System.out.println("Arrays.toString(strArr) = " + Arrays.toString(strArr));
        // tiger, lion, cat, Dog
    }
}

class Descending implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        if (o1 instanceof Comparable && o2 instanceof Comparable) {
            Comparable c1 = (Comparable) o1;
            Comparable c2 = (Comparable) o2;
            // -1을 곱해서 기본 정렬방식의 역으로 변경한다.
            // c2.compareTo(c1) 처럼 순서를 바꿔도 된다.
            return c1.compareTo(c2) * -1;
        }
        return -1;
    }
}​

 

728x90
반응형

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

[Java] 12-1. 지네릭스(Generics)  (2) 2022.10.05
[Java] 11. 컬렉션 프레임웍(2)  (0) 2022.09.30
[Java] 10-3. java.time 패키지  (2) 2022.09.19
[Java] 10-1, 2. 날짜와 시간 & 형식화  (0) 2022.09.16
[Java] 9-2. 유용한 클래스  (0) 2022.09.14

댓글