December 30, 2021
컬렉션 프레임웍이란, ‘데이터 군을 저장하는 클래스들을 표준화한 설계’를 뜻한다. 즉 다수의 데이터를 저장하는 표준화된 프로그래밍 방식을 말한다.
컬렉션 프레임웍에서는 컬렉션 데이터 그룹을 크게 3가지 타입이 존재한다고 인식하고 3개의 인터페이스를 정의하였다. 이중 List와 Set의 공통적인 부분을 뽑아서 Collection이라는 인터페이스를 정의하였고, Map인터페이스는 상속계층도에 포함하지 않았다.
List
Set
Map
컬랙션 프레임웍의 모든 컬랙션 클래스들은 List, Set, Map 중의 하나를 구현하고 있으며, 구현한 인터페이스의 이름이 클래스의 이름에 포함되어 있어서 이름만으로도 클래스의 특징을 쉽게 알 수 있다.
Collection 인터페이스는 다음과 같은 기능이 포함되어 있다.
List는 중복을 허용하면서 저장순서가 유지되는 컬렉션이다.
List인터페이스에 관해 공식문서에서 정의된 메서드들은 다음과 같다.
Set은 중복을 허용하지 않고 저장순서가 유지되지 않는 컬렉션 클래스이다. 아래와 같은 상속계층도를 따른다.
Map은 키(key)와 값(value)을 하나의 쌍으로 묶어서 저장하는 컬랙션 클래스이다.
키는 중복될 수 없지만 값은 중복을 허용한다.
Map.Entry 인터페이스는 Map인터페이스의 내부 인터페이스(innerInterface)이다. Map인터페이스를 구현하면 Map.Entry 인터페이스도 반드시 함께 구현해야 한다.
public interface Map{
...
public static interface Entry{
Object getKey(); // Entry의 key객체를 반환
Object getValue(); // Entry의 Value 반환
Object setValue(Object value); // Entry에 value 객체를 지정된 객체로 바꿔준다.
boolean equals(Object o); // 동일한 Entry인지 비교한다
int hashCode(); // Entry의 해쉬코드 반환
...
}
}
ArrayList에 있는 메서드이다. 더 자세한 내용은 공식문서를 참고하자.
배열은 모든 데이터가 연속적으로 존재하지만 링크드 리스트는 불연속적으로 존재하는 데이터들을 서로 연결(link)한 형태로 구성되어있다.
class Node{
Node next;
Object obj;
}
다음과 같이 한 Node에서 다음 Node의 주소를 참조하고 있는 데이터구조가 LikedList 이다.
하지만 LinkedList는 이동방향이 단방향이기 때문에 다음 요소에는 쉽게 접근할 수 있지만 이전 요소는 접근 하기 힘들다. 따라서 이를 극복하기 위해 더블 링크드 리스크(doubly liked list)가 생겼다.
class Node{
Node next;
Node previous;
Object obj;
}
다음과 같이 더블 링크드 리스트는 단순히 링크드 리스트에 참조변수를 하나 추가하여 이전 요소에 대해 참조 할 수 있게 한 구조이다.
실제 LikedList클래스는 이름과 달리 더블 링크드 리스트로 구현되어 있다.
boolean add(Object o)
// 지정된 객체(o)를 LinkedList 끝에 추가, 저장에 성공하면 true, 실패하면 false
void add(int index, Object element)
// 지정된 위치(index)에 객체(element)를 추가
void clear()
// LinkedList의 모든 요소를 삭제
Object peekFirst()
// LinkedList 의 첫번째 요소를 반환
Object pollFirst()
//LinkedList 의 첫번째 요소를 반환, LinkedList에서는 제거된다
Object peekLast()
// 마지막 요소를 반환
Object pollLast()
// 마지막 요소를 반환하면서 제거
위와 같은 함수들이 있고, 더 다양한 함수들에 대한 설명은 공식문서를 참고하자
ArrayList의 경우
인덱스가 n인 데이터의 주소 = 배열의 주소 + n * 데이터 타입의 크기
로 접근하면 되기 떄문에 속도는 O(1)이지만, LinkedList의 경우 필요한 데이터에 접근하기 위해 앞에서 부터 순차적으로 요소들을 확인해야하기 때문에 O(n)이 소요된다.
ArrayList
또한 처음에 데이터를 저장할 때는 ArrayList를 사용하다가, 데이터의 크기가 매우 커지면 LinkedList로 옮겨서 작업하는 것도 좋은 효율을 얻는 방법중의 하나이다.
스택(Stack) - LIFO(Last In First Out)
boolean empty() // Stack이 비어있는지 아려준다
Object peek() // Stack의 맨위의 객체 반환, pop과 달리 꺼내진 않음. 없을시 Exception 발생
Object pop() // Stack 맨위의 저장된 객체를 꺼낸다 (비어있으면 Exception )
Object push(Object item) // Stack에 객체(item)을 저장한다
int search(Object o)
// Stack에서 주어진 객체(o)를 찾아서 그 위치를 반환. 못찾으면 -1 반환
//(Stack의 위치는 배열과 달리 1부터 시작)
큐(Queue) - FIFO(First In First Out)
boolean add(Object o) // 지정된 객체를 Queue에 추가, 성공시 true 실패시 false
Object remove() // Queue에서 객체를 꺼내 반환, 비어있으면 예외 발생
Object element() // 삭제없이 요소만 읽어옴, 비어있으면 예외 발생
boolean offer(Object o) // queue에 객체를 저장, 성공시 true, 실패시 false
Object poll() // Queue에서 객체를 꺼내서 반환, 비어있으면 null 반환
Object peek() // 삭제없이 요소만 읽어옴. Queue가 비어있으면 null qksghks
자바에서 Stack은 Stack클래스로 구현하여 제공하지만 큐는 Queue인터페이스로 정의해 놓았을 뿐 별도의 클래스를 제공하고 있지 않다.
Queue q = new LinkedList();
import java.util.*;
class PriorityQueueEx{
public static void main(String[] args){
Queue pq = new PriorityQueue();
pq.offer(3);
pq.offer(1);
pq.offer(2);
pq.offer(5);
pq.offer(4);
System.out.println(pq);
Object obj = null;
// 가장 최상단의 element가 존재하지 않을 때 까지 계속
while((obj = pq.poll()) != null){
System.out.println(obj);
}
}
}
Dequeue dq = new LinkedList();
Dequeue dq = new Arraydequeue();
dq.offerLast() // 뒤에 값을 추가 stack의 push
dq.pollLast() // 뒤에 값을 제거 stack의 pop
dq.pollFirst() // 앞에 값을 확인, Queue의 poll
dq.peekFirst() // 앖의 값을 제거하고 받아옴, Queue의 peek함수
dq.peekLast() // 뒤에 값을 제거하고 받아옴
Enumeration 은 Iterator의 구버전이고, ListIterator는 Iterator의 기능을 향상 시킨 것이다.
boolean hasNext() // 읽어올 요소가 남아있는지 확인하고 있으면 true, 없으면 false
Object next() // 다음요소를 읽어온다. next() 호출하기 전에 hasNext로 다음 값이 있는지 확인해야한다
void remove() // next()로 읽어 온 요소를 삭제한다, 즉 next()를 호출한 다음에 remove()를 호출해야한다.
ArrayList에서 사용하는 예시는 다음과 같다
Collection c = new ArrayList();
Iterator it = c.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
이뿐만 아니라 다른 List, Set 인터페이스로 구현한 클래스들은 ArrayList 대신에 해당 클래스만 써주면 똑같이 사용할 수 있다.
Iterator를 이용해서 컬렉션의 요소를 읽어오는 방법을 표준화 했기 때문이다.
Map 인터페이스를 구현한 클래스는 키(key)와 값(value)을 쌍(pair)로 저장하기 때문에 iterator()를 직접 호출할 수 없고, keySet(), entrySet() 과 같은 메서드를 통해 key와 value를 각각 따로 Set의 형태로 받은 후 Iterator를 사용할 수 있다.
Map map = new HashMap();
...
Iterator it = map.entrySet().iterator();
//이런식으로 먼저 set을 만들고 그 후에 iterator를 반환하여 사용할 수 있다.
ArrayList list = new ArrayList();
list.add("1");
list.add("2");
ListIterator it = list.listIterator();
void add(Object o) // 컬렉션에 새로운 객체 (o)를 추가한다
boolean hasNext() // 읽어올 다음 요소가 남아 있는지 확인한다 있으면 true, 없으면 false
boolean hasPrevious() // 읽어올 이전 요소가 남아 있는지 확인한다 있으면 true, 없으면 false
Object next() //다음 요소를 반환한다.
Object previous() // 이전요소를 반환한다.
int nextIndex() // 다음 요소의 index를 반환한다
int previousIndex() // 이전 요소의 index를 반환한다.
void remove() // next() 또는 previous()로 읽어 온 요소를 삭제한다
void set(Object o) // next 또는 previous()로 읽어온 요소를 지정된 객체(o)로 변경한다.
배열의 모든 요소를 지정한 값으로 채운다.
int[] arr = new int[5];
Arrays.fill(arr, 9); // arr=[9,9,9,9,9]
배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받아서 배열을 채운다.
int[] arr = new int[5];
Arrays.setAll(arr, () => {(int)(Math.random()*5) + 1});
sort()는 배열을 정렬할 때 사용한다
int[] arr = {3, 2, 0, 1, 4};
Arrays.sort(arr); // [0, 1, 2, 3, 4];
sort함수에는 부분 정렬을 할 수 있다.
int[] arr = {3, 2, 0, 1, 4};
Arrays.sort(arr, 1, 4); //[3, 0, 1, 2, 4]
//1~3번index까지의 요소만 정렬한다
Comparator와 Comparable은 모두 인터페이스로 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있다.
public interface Comparator{
int compare(Object o1, Object o2);
boolean equals(Object obj);
}
public interface Comparable{
public int compareTo(Object o);
}
Compare()와 CompareTo()는 결국 두 객체를 비교한다는 같은 기능을 목적으로 고안된 것이다. compareTo()의 반환값은 int지만 실제로는 두 객체가 같으면 0, 작으면 음수, 크면 양수를 받환하도록 되어있다.
마찬가지로 compare()도 객체를 비교해서 음수, 0, 양수 중 하나를 반환하도록 구현해야 한다.
Comparable : 기본 정렬기준은 구현하는데 사용
Comparator : 기본 정렬기준 외에 다른 기준으로 정렬하고자 할 때 사용
import java.utill.*;
class Descending implements Comparator{
public int compare(Object o1, Object o2){
if(o1 instaceof Comparable && o2 instaceof Comparable){
Comparable c1 = (Comparable)o1;
Comparable c2 = (Comparable)o2;
return c1.compareTo(c2) * -1; // -1d을 곱해서 기본 정렬방식의 역으로 변경한다.
// 또는 c2.compareTo(c1)으로 하면 역순배열이 된다.
}
return -1;
}
}
class ComparatorEx{
public static void main(String[] args){
String[] strArr = {"cat", "Dog", "lion", "tiger"};
Array.sort(strArr); //String의 Comparable구현에 의한 정렬
Arrays.sort(strArr, new Descending()) // 역순 정렬
}
}
Comparator 객체는 메서드가 하나뿐인 함수형 인터페이스를 구현하기 때문에 람다함수로 대체가 가능하다
Collections.sort(playrs, (a,b) => b.getScore() - a.getScore()); //이런식으로 람다식을 인자로 넣어주느것으로 대체 가능하다.