Java의 정석 의 책을 읽고 정리한 내용입니다.
컬레션 프레임웍 : 데이터 군을 저장하는 클래스들을 표준화한 설계를 뜻한다.
컬렉션 : 다수의 데이터, 데이터 그룹
프레임 웍 : 표준화된 프로그램 방식을 의미
컬렉션 클래스 : Vector
와 같이 다수의 데이터를 저장할 수 있는 클래스
💡 참고
JDK1.5부터 Iterable인터페이스가 추가되고 이를 Collection인터페이스가 상속받도록 변경되었으나 이것은 단지 인터페이스들의 공통적인 메서드인 iterator()를 뽑아서 중복을 제거하기 위한 것에 불과하므로 상속계층도에서 별 의미가 없다.
인터페이스 | 특징 | 구현 클래스 |
---|---|---|
List | 순서가 있는 데이터의 집합, 데이터의 중복을 허용한다. ex) 대기자 명단 | ArrayList, LinkedList, Stack, Vector 등 |
Set | 순서를 유지하지 않는 데이터의 집합, 데이터의 중복을 허용하지 않는다. ex) 집합 | HashSet, TreeSet 등 |
Map | 키(key)와 값(value)의 쌍(pair)으로 이루어진 데이터 집합, 순서는 유지되지 않고, 키는 중복을 허용하지 않고, 값은 중복을 허용한다. ex)우편번호, 전화번호 | HashMap, TreeMap, Hashtable, Properties 등 |
💡 참고
키(Key)란, 데이터 집합 중에서 어떤 값(value)을 찾는데 열쇠(key)가 된다는 의미에서 붙여진 이름이다. 그래서 키(Key)는 중복을 허용하지 않는다.
List
, Set
, Map
중의 하나를 구현하고 있으며, 구현한 인터페이스의 이름이 클래스의 이름에 포함되어 있어서 이름만으로도 클래스의 특징을 쉽게 알 수 있도록 되어있다.Vector
, Stack
, Hashtable
, Properties
와 같은 클래스들은 컬렉션 프레임웍이 만들어지기 이전부터 존재하던 것이기 때문에 컬렉션 프레임웍의 명명법을 따르지 않는다. Vector
나 Hashtable
과 같은 기존의 컬렉션 클래스들은 호환을 위해, 설계를 변경해서 남겨두었지만 가능하면 사용하지 않는 것이 좋다.ArrayList
나 HashMap
을 사용하는 것이 좋다.
✔️ Collection인터페이스
메서드 | 설명 |
---|---|
boolean add(Object o), boolean addAll(Collection c) | 지정된 객체(o) 또는 Collection(c)의 객체들을 Collection에 추가한다. |
void clear() | Collection의 모든 객체를 삭제한다. |
boolean contains(Object o), boolean containsAll(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의 객체를 저장해서 반환한다. |
Java API문서를 보면, 위와 같이 사용된 Object
가 아닌 E
로 표기되어있는데, E는 특정 타입을 의미하는 것으로 JDK1.5부터 추가된 지네릭스(Generics)에 의한 표기이다.
E
, I, K, V
를 사용하는 경우도 있는데 모두 Object타입이다.
✔️ List 인터페이스
List인터페이스는 중복을 허용하면서 저장순서가 유지되는 컬렉션을 구현하는데 사용된다.
메서드 | 설명 |
---|---|
void add(int index, Object element), boolean addAll(int index, Collection c) | 지정된 위치(Index)에 객체(element) 또는 컬렉션에 포함된 객체들을 추가한다. |
Object get(int index) | 지정된 위치(Index)에 있는 객체를 반환한다. |
int indexOf(Object o) | 지정된 객체의 위치(Index)를 반환한다. (List의 첫번째 요소부터 순방향으로 찾는다.) |
int lastIndexOf(Object o) | 지정된 객체의 위치(Index)를 반환한다. (List의 마지막 요소부터 역방향으로 찾는다.) |
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) | 지정된 범위(fromIndex부터 toIndex)에 있는 객체 반환한다. |
✔️ Set인터페이스
Set인터페이스는 중복을 허용하지 않고 저장순서가 유지되지 않는 컬렉션 클래스들을 구현하는데 사용된다. 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객체에 대응하는 value객체를 찾아서 반환한다. |
int hashCode() | 해시코드를 반환한다. |
boolean isEmpty() | Map이 비어있는지 확인한다. |
Set KeySet() | Map에 저장된 모든 key객체를 반환한다. |
Object put(Object key, Object value) | Map에 value객체를 key객체에 연결(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()
메서드에서 값(value
)은 중복을 허용하기 때문에 Collection
타입으로 반환한다.ketSet()
메서드에서 키(key
)는 중복을 허용하지 않기 때문에 Set
타입으로 반환한다.
✔️ Map.Entry인터페이스
Map.Entry
인터페이스는 Map인터페이스의 내부 인터페이스이다.- 내부 클래와 같이 인터페이스도 인터페이스 안에 인터페이스를 정의하는 내부 인터페이스(inner interface)를 정의하는 것이 가능하다.
메서드 | 설명 |
---|---|
boolean equals(Object o) | 동일한 Entry인지 비교한다. |
Object getKey() | Entry의 key 객체를 반환한다. |
Object getValue() | Entry의 value 객체를 반환한다. |
int hashCode() | Entry의 해시코드를 반환한다. |
Object setValue(Object value) | Entry의 value객체를 지정된 객체로 바꾼다. |
ArrayList
는 List인터페이스를 구현하기 때문에 데이터의 저장순서가 유지되고 중복을 허용한다.ArrayList
는 기존의Vector
를 개선한 것으로Vector
와 구현원리와 기능적인 측면에서 동일하다고 할 수 있다.ArrayList
는 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(Object o) | 객체(o)가 지정된 위치를 끝부터 역방향으로 검색해서 반환 |
ListIterator listIterator() | ArrayList의 ListIterator을 반환 |
ListIterator listIterator(int index) | ArrayList의 지정된 위치부터 시작하는 ListIterator를 반환 |
Object remove(int index) | 지정된 위치(index)에 있는 객체를 제거 |
boolean remove(Object o) | 지정된 객체를 제거 (성공하면 true, 실패하면 false) |
boolean removeAll(Collection c) | 지정한 컬렉션에 저장된 것과 동일한 객체들을 ArrayList에서 제거 |
boolean retainAll(Collection c) | ArrayList에 저장된 객체 중에서 주어진 컬렉션과 공통된 것들만을 남기고 나머지는 삭제 |
Object set(int index, Object element) | 주어진 객체(element)를 지정된 위치(index)에 저장한다. |
int size() | ArrayLIst에 저장된 객체의 개수를 반환한다. |
void sort(Comparator c) | 지정된 정렬기준으로 ArrayList를 정렬 |
List subList (int fromIndex, int toIndex) | fromIndex~toIndex사이에 저장된 객체를 반환 |
Object[] toArray() | ArrayList에 저장된 모든 객체들을 객체배열로 반환 |
Object[] toArray(Object[] a) | ArrayList에 저장된 모든 객체들을 객체배열 a에 담아 반환 |
void trimToSize() | 용량을 크기에 맞게 줄인다(빈공간을 없앤다.) |
⚠️ 주의
Collection
은 인터페이스이고,Collections
는 클래스이다.
&nbps;
for (int i = list2.size() - 1; i >= 0; i--) {
if (list1.contains(list2.get(i)))
list2.remove(i);
}
처음에 인스턴스를 생성할 때, 저장할 데이터의 개수를 잘 고려하여 충분한 용량의 인스턴스를 생성하는 것이 좋다.
💡 참고
인터페이스를 구현할 때 인터페이스에 정의된 모든 메서드를 구현해야 한다. 일부 메서드만 구현했다면 추상클래스로 선언해야한다. 그러나 JDK1.8부터 List인터페이스에 3개의 디폴트 메서드가 추가되었으며, 이 들은 구현하지 않아도 된다.
✔️ ArrayList remove과정
- 만일 삭제할 객체가 마지막 데이터라면, 복사할 필요없이 단순히 null로 변경해준다.
System.arraycopy(data, 3, data, 2, 2)
data[size-1] = null;
size--;
System.arraycopy()
를 호출해서 다른 데이터의 위치를 이동시켜 줘야 하기 때문에 다루는 데이터의 개수가 많을수록 작업시간이 오래걸린다.
- 배열은 가장 기본적인 형태의 자료구조로 구조가 간단하며 사용하기 쉽고 데이터를 읽어 오는데 걸리는 시간(접근 시간, access time)이 가장 빠르다는 장점을 가지고 있지만 다음과 같은 단점도 가지고 있다.
1. 크기를 변경할 수 없다.
- 크기를 변경할 수 없으므로 새로운 배열을 생성해서 데이터를 복사해야한다.
- 실행속도를 향상시키기 위해서는 충분히 큰 크기의 배열을 생성해야 하므로 메모리가 낭비된다.
2. 비순차적인 데이터의 추가 또는 삭제에 시간이 많이 걸린다.
- 차례대로 데이터를 추가하고 마지막에서부터 데이터를 삭제하는 것은 빠르지만
- 배열의 중간에 데이터를 추가하려면, 빈자리를 만들기 위해 다른 데이터들을 복사해서 이동해야 한다.
✔️ 더블 링크드 리스트
class Node{
Node next; //다음 요소의 주소를 저장
Node previous; //이전 요소의 주소를 저장
Object obj; //데이터를 저장
}
LinkedList
클래스는 이름과 달리 링크드 리스트
가 아닌, 더블 링크드 리스트
로 구현되어 있다. (링크드 리스트의 단점인 낮은 접근성을(accessability
) 높이기 위해)
생성자 또는 메서드 | 설명 |
---|---|
LinkedList() | LinkedList 객체를 생성 |
LinkedList(Collection c) | 주어진 컬렉션을 포함하는 LinkedList 객체를 생성 |
boolean add(Object o) | 지정된 객체(o)를 LinkedList의 끝에 추가, 저장에 성공하면 true, 실패하면 false |
void add(int index, Object element) | 지정된 위치에 객체를 추가 |
boolean addAll(Collection c) | 주어진 컬렉션에 포함된 모든 요소를 LinkedList의 끝에 추가한다. 성공하면 true, 실패하면 false |
boolean addAll(int index, Collection c) | 지정된 위치에 주어진 컬렉션에 포함된 모든 요소를 추가, 성공하면 true, 실패하면 false |
void clear() | LinkedList의 모든 요소 삭제 |
boolean contains(Object o) | 지정된 객체가 LinkedList에 포함되어 있는지 알려줌 |
boolean containsAll(Collection c) | 지정된 컬렉션의 모든 요소가 포함되어 있는지 알려줌 |
Object get(int index) | 지정된 위치의 객체를 반환 |
int indexOf(Object o) | 지정된 객체가 저장된 위치(앞에서 몇 번째)를 반환 |
boolean isEmpty() | LinkedList가 비어있는지 알려준다. 비어있으면 true |
Iterator iterator() | Iterator를 반환한다. |
int lastIndexOf(Object o) | 지정된 객체의 위치(index)를 반환(끝부터 역순검색) |
ListIterator listIterator() | ListIterator를 반환한다. |
ListIterator listIterator(int index) | 지정된 위치에서부터 시작하는 ListIterator를 반환 |
Object remove(int index) | 지정된 위치의 객체를 LinkedList에서 제거 |
boolean remove(Object o) | 지정된 객체를 LinkedList에서 제거, 성공하면 true, 실패하면 false |
boolean removeAll(Collection c) | 지정된 컬렉션의 요소와 일치하는 요소를 모두 삭제 |
boolea retainAll(Collection c) | 지정된 컬렉션의 모든 요소가 포함되어 있는지 확인 |
Object set(int index, Object element) | 지정된 위치의 객체를 주어진 객체로 바꿈 |
int size() | LinkedList에 저장된 객체의 수를 반환 |
List subList(int fromIndex, int toIndex) | LinkedList의 일부를 List로 반환 |
Object[] toArray() | LinkedList에 저장된 객체를 배열로 반환 |
Object[] toArray(Obejct[] a) | LinkedList에 저장된 객체를 주어진 배열에 저장하여 반환 |
void addFirst(Object o) | LinkedList의 맨 앞에 객체를 추가 |
void addLast(Object o) | LinkedList의 맨 끝에 객체를 추가 |
Iterator descendingIterator() | 역순으로 조회하기 위한 DescendingIterator를 반환 |
Object getFirst() | LinkedList의 첫번쨰 요소를 반환 |
Obejct getLast() | LinkedList의 마지막 요소를 반환 |
boolean offerFirst(Object o) | LinkedList의 맨 앞에 객체를 추가, 성공하면 true |
boolean offerLast(Object o) | LinkedList의 맨 끝에 객체를 추가, 성공하면 true |
Object peekFirst() | LinkedList의 첫번쨰 요소를 반환 |
Object peekLast() | LinkedList의 마지막 요소를 반환 |
Object pollFirst() | LinkedList의 첫번쨰 요소를 반환하면서 제거 |
Object pollLast() | LinkedList의 마지막 요소를 반환하면서 제거 |
Object pop() | removeFirst()와 동일 |
void push(Object o) | addFirst()와 동일 |
Object removeFirst() | LinkedLIst의 첫번째 요소를 제거 |
Object removeLast() | LinkedList의 마지막 요소를 제거 |
boolean removeFirstOccurrence(Object o) | LinkedList에서 첫번째로 일치하는 객체를 제거 |
boolean removeLastOccurerence(Object o) | LinkedList에서 마지막으로 일치하는 객체를 제거 |
🔔 결론
1. 순차적으로 추가/삭제하는 경우에는ArrayList
가LinkedList
보다 빠르다.
- 중간에 데이터를 추가/삭제하는 경우에는
LinkedList
가ArrayList
보다 빠르다.
배열의 경우 인덱스가 n인 요소의 값을 얻어 오고자 한다면 단순히 아래와 같은 수식을 계산함으로써 해결된다.
인덱스가 n인 데이터의 주소 = 배열의 주소 + n * 데이터의 타입 크기
LinkedList
는 불연속적으로 위치한 각 요소들이 서로 연결된 것이라, 처음부터 n번째 데이터까지 차례로 따라가야만 원하는 값을 얻을 수 있다.LinkedList
는 저장해야하는 데이터의 개수가 많아질수록 데이터를 읽어오는 시간, 즉 접근시간이 길어진다는 단점이 있다.컬렉션 | 읽기(접근시간) | 추가/삭제 | 비 고 |
---|---|---|---|
ArrayList | 빠르다 | 느리다 | 순차적인 추가삭제는 더 빠름. 비효율적인 메모리사용 |
LinkedList | 느리다 | 빠르다 | 데이터가 많을수록 접근성이 떨어짐 |
📣 그러면?
ArrayList
: 다루고자 하는 데이터의 개수가 변하지 않는 경우 사용한다.
LInkedList
: 데이터 개수의 변경이 잦을 때 사용한다.
✔️ 두 클래스의 장점을 이용해서 두 클래스를 조합해서 사용하는 방법
ArrayList
를 사용한 다음, 작업할 때는 LinkedList
로 데이터를 옮겨서 작업하면 좋은 효율을 얻을 수 있다.ArrayList al = new ArrayList(1000000);
for(int i=0; i<100000;i++) al.add(i+"");
LinkedList ll = new LinkedList(al);
for(int i=0; i<100000;i++) ll.add(500,"X");
컬렉션 프레임웍에 속한 대부분의 컬렉션 클래스들은 이처럼 서로 변환이 가능한 생성자를 제공한다. 이를 이용하면 간단히 다른 컬렉션 클래스로 데이터를 옮길 수 있다.
- 스택은
ArrayList
와 같은 배열 기반의 컬렉션 클래스가 적합하다.- 큐는 데이터의 추가/삭제가 쉬운
LinkedList
로 구현하는 것이 더 적합하다.
✔️ Stack의 메서드
메서드 | 설명 |
---|---|
boolean empty() | Stack이 비어있는지 알려준다. |
Object peek() | Stack의 맨 위에 저장된 객체를 반환, pop()과 달리 Stack에서 객체를 꺼내지 않음.(비어있을 때는 EmptyStackException 발생) |
Object pop() | Stack의 맨 위에 저장된 객체를 꺼낸다. (비어있을 때는EmptyStackException 발생) |
Object push(Object item) | Stack에 객체(item)를 저장한다. |
int search(Object o) | Stack에서 주어진 객체를찾아서 그 위치를 반환, 못찾으면 -1을 반환 (배열과 달리 위치는 0이 아닌 1부터 시작) |
✔️ Queue의 메서드
메서드 | 설명 |
---|---|
boolean add(Object o) | 지정된 객체를 Queue에 추가한다. 성공하면 true를 반환, 저장공간이 부족하면 IIIegalStateException 발생 |
Object remove() | Queue에서 객체를 꺼내 반환, 비어있으면 NoSurchElementException발생 |
Object element() | 삭제없이 요소를 익어온다. peek과 달리 Queue가 비어있을 떄 NoSuchElementException 발생 |
boolean offer(Object o) | Queue에 객체를 저장, 성공하면 true, 실패하면 false를 반환 |
Object poll() | Queue에서 객체를 꺼내서 반환, 비어있으면 null을 반환 |
Object peek() | 삭제없이 요소를 읽어온다. Queue가 비어있으면 null을 반환 |
✔️ 스택과 큐의 활용
스택의 활용 예 - 수식계산, 수식괄호검사, 워드프로세서의 undo/redo, 웹브라우저의 뒤로/앞으로
큐의 활용 예 - 최근사용문서, 인쇄작업 대기목록, 버퍼(buffer)
✔️ PriorityQueue
Queue
인터페이스의 구현체 중의 하나로, 저장한 순서에 관계없이 우선순위(priority
)가 높은 것 부터 꺼내게 된다는 특징이 있다.null
을 저장할 수 없다.null
을 저장하면NullPointerException
이 발생한다.PriorityQueue
는 저장공간으로 배열을 사용하며, 각 요소를 힙(heap
)이라는 자료구조의 형태로 저장한다. (힙 : 가장 큰 값이나 가장 작은 값을 빠르게 찾을 수 있다는 특징이 있다.)
💡 참고
- 자료구조 힙(heap)은 JVM의 힙(heap)과 이름만 같을 뿐 다른것이다.
import java.util.*;
public class PriorityQueueEx {
public static void main(String[] args) {
Queue pq = new PriorityQueue();
pq.offer(3); // pq.offer(new Integer(3)); 오토박싱
pq.offer(1);
pq.offer(5);
pq.offer(2);
pq.offer(4);
System.out.println(pq); // pq의 내부 배열을 출력
Object obj = null;
// PriorityQueue에 저장된 요소를 하나씩 꺼낸다.
while ((obj = pq.poll()) != null)
System.out.println(obj);
}
}
Integer
로 오토박싱 해준다.Integer
와 같은 Number
의 자손들은 자체적으로 숫자를 비교하는 방법을 정의하고 있기 때문에 비교 방법을 지정해주지 않아도 된다.
✔️ 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() |
offer()
: 저장poll()
: 추출offerLst
: 끝에 저장pollLast
: 끝에서 삭제pollFirst
: 앞에서 삭제offerFirst
: 앞에 저장
Iterator
,ListIterator
,Enumeration
모두 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다.Enumeration
은Iterator
의 구버전이며,ListIterator
는Iterator
의 기능을 향상 시킨 것이다.
✔️ Iterator
메서드 | 설명 |
---|---|
boolean hasNext() | 읽어 올 요소가 남아있는지 확인한다. 있으면 true, 없으면 false를 반환한다. |
Object next() | 다음 요소를 읽어 온다. next()를 호출하기 전에 hasNext()를 호출해서 읽어 올 요소가 있는지 확인하는 것이 안전하다. |
void remove() | next()로 읽어 온 요소를 삭제한다. next()를 호출한 다음에 remove()를 호출해야한다. (선택적 가능) |
Iterator it = map.entrySet().iterator();
➡
Set eSet = map.entrySet();
Iterator it = eSet.iterator();
StringBuffer sb = new StringBuffer();
sb.append("A");
sb.append("B");
➡
StringBuffer sb = new StringBuffer();
sb.append("A").append("B"); // StringBuffer append(String str)
append
메서드가 수행 결과로 StringBuffer
를 리턴하기 때문에 가능하다.
✔️ ListIterator와 Enumeration
Enumeration
은 컬렉션 프레임웍이 만들어지기 전에 사용하던 것으로Iterator
의 구버전이다.Enumeration
대신,Iterator
를 사용하자!ListIterator
:Iterator
를 상속받아서 기능을 추가한 것
Emumeration : Iterator의 구버전
ListIterator : Iterator에 양방향 조회기능추가(List를 구현한 경우만 사용가능)
Enumeration
메서드 | 설명 |
---|---|
boolean hasMoreElements() | 읽어 올 요소가 남아있는지 확인한다. 있으면 true, 없으면 false를 반환한다. Iterator의 hasNext()와 같다. |
Object nextElement() | 다음 요소를 읽어온다. nextElement()를 호출하기전에 hasMoreElements()를 호출해서 읽어올 요소가 남아있는지 확인하는 것이 안전한다. Iterator의 next()와 같다. |
ListIterator
메서드 | 설명 |
---|---|
void add(Obejct o) | 컬렉션에 새로운 객체(o)를 추가한다. (선택적 기능) |
boolean hasNext() | 읽어 올 다음 요소가 있는지 확인한다. 있으면 true, 없으면 false를 반환 |
boolean hasPrevious() | 읽어 올 이전 요소가 남아있는지 확인한다. 있으면 true, 없으면 false를 반환 |
Object next() | 다음 요소를 읽어온다.next()를 호출하기 전에 hasNext()를 호출해서 읽어올 요소가 있는지 확인하는 것이 안전하다. |
Object previous() | 이전 요소를 읽어온다. previous()를 호출하기 전에 hasPrevious()를 호출해서 읽어 올 요소가 있는지 확인하는 것이 안전하다. |
int nextIndex() | 다음 요소의 index를 반환한다. |
int previousIndex() | 이전 요소의 index를 반환한다. |
void remove() | next()또는 previous()로 읽어 온 요소를 삭제한다. 반드시 next()나 previous()를 호출한 후에 이 메서드를 호출해야 한다.(선택적 기능) |
void set(Object o) | next()또는 previous()로 읽어 온 요소를 지정된 객체(o)로 변경한다. 반드시 next()나 previous()를 먼저 호출한 다음에 이 메서드를 호출해야한다. (선택적 기능) |
ListIterator
는 양방향으로 이동하기 때문에, 이동하기 전에 반드시 has Next()
나 hasPrevious()
를 호출해서 이동할 수 있는지 확인해야 한다.import java.util.*;
public class ListIteratorEx1 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
ListIterator it = list.listIterator();
while (it.hasNext()) {
System.out.print(it.next()); // 순방향 진행
}
System.out.println();
while (it.hasPrevious()) {
System.out.print(it.previous()); // 역방향
}
System.out.println();
}
}
Iterator
를 어떻게 구현하는지 예제
import java.util.*;
public class MyVector2 extends MyVector implements Iterator {
int cursor = 0;
int lastRet = -1;
public MyVector2(int capacity) {
super(capacity);
}
public MyVector2() {
this(10);
}
public String toString() {
String tmp = "";
Iterator it = iterator();
for(int i=0; it.hasNext();i++) {
if(i!=0) tmp+=", ";
tmp += it.next(); // tmp += next().toString();
}
return "["+ tmp +"]";
}
public Iterator iterator() {
cursor=0; // cursor와 lastRet를 초기화 한다.
lastRet = -1;
return this;
}
public boolean hasNext() {
return cursor != size();
}
public Object next(){
Object next = get(cursor);
lastRet = cursor++;
return next;
}
public void remove() {
// 더이상 삭제할 것이 없으면 IllegalStateException를 발생시킨다.
if(lastRet==-1) {
throw new IllegalStateException();
} else {
remove(lastRet);
cursor--; // 삭제 후에 cursor의 위치를 감소시킨다.
lastRet = -1; // lastRet의 값을 초기화 한다.
}
}
}
remove()
는 next()
로 읽어 온 객체를 삭제하는 것이기 때문에 remove()
를 호출하기 전에는 반드시 next()
가 호출된 상태이어야 한다.
import java.util.*;
public class MyVector2Test {
public static void main(String[] args) {
MyVector2 v = new MyVector2();
v.add("0");
v.add("1");
v.add("2");
v.add("3");
v.add("4");
System.out.println("삭제 전 : "+v);
Iterator it = v.iterator();
it.next();
it.remove();
it.next();
it.remove();
System.out.println("삭제 후 : "+v);
}
}
삭제 전 : [0, 1, 2, 3, 4]
삭제 후 : [2, 3, 4]
✔️ 배열의 복사-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] <-4는 불포함
int[] arr6 = Arrays.copyOfRange(arr,0,7); //arr6 =[0,1,2,3,4,0,0]
✔️ 배열 채우기 - fill(), setAll()
fill()
: 배열의 모든 요소를 지정된 값을 채운다.setAll()
: 배열을 채우는데 사용할 함수형 인터페이스를 매개변수로 받는다.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]
✔️ 배열의 정렬과 탐색 - sort(), binarySearch()
sort()
는 배열을 정렬할 때, 그리고 배열에 저장된 요소를 검색할 때는 binarySearch()
를 사용한다. binarySearch()
는 배열에서 지정된 값이 저장된 위치(index
)를 찾아서 반환하는데, 반드시 배열이 정렬된 상태이어야 올바른 결과를 얻는다. int[] arr= {3,2,0,1,4};
int idx = Arrays.binarySearch(arr, 2) : // idx = -5 ⬅ 잘못된 결과
Arrays.sort(arr); //배열 arr을 정렬한다.
System.out.println(Arrays.toString(arr)); //[0,1,2,3,4]
int idx =Arrays.binarySearch(arr, 2); //idx=2 ⬅ 올바른 결과
✔️ 배열의 비교와 출력 - equals(), toString()
toString()
은 배열의 모든 요소를 문자열로 편하게 출력할 수 있다.toString()
은 일차원 배열에만 사용할 수 있으므로, 다차원배열에는 deepTo String()
을 사용해야 한다.int[] arr= {0,1,2,3,4};
int[][] arr2D = {{11,12}, {21,22}};
System.out.println(Arrays.toString(arr)); //[0,1,2,3,4]
System.out.println(Arrays.deepToString(arr2D)); //[[11,12], [21,22]]
String[][] str2D = new String[][] {{"aaa","bbb"}, {"AAA","BBB"}};
String[][] str2D2 = new String[][] {{"aaa","bbb"}, {"AAA","BBB"}};
System.out.println(Arrays.equals(str2D, str2D2)); //false
System.out.println(Arrays.deepEquals(str2D, str2D2)); //true;
배열의 배열
의 형태로 구성하기 때문에 equals()
로 비교하면 ,문자열을 비교하는 것이 아니라 배열에 저장된 배열의 주소
를 비교하게 된다.false
를 결과로 얻은다.
✔️ 배열을 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);
list.add(6); // UnsupportedOperationException 예외발생
asList()
가 반환한 List
의 크기를 변경할 수 없다는 것이다. List
가 필요하다면 다음과 같이 하면된다.List list= new ArrayList(Arrays.asList(1,2,3,4,5));
✔️ parallelXXX(), spliterator(), stream()
parallel
로 시작하는 이름의 메서드들이 있는데, 이 메서드들은 보다 빠른 결과를 얻기 위해 여러 쓰레드가 작업을 나누어 처리하도록 한다. spliterator()
는 여러 쓰레드가 처리할 수 있게 하나의 작업을 여러 작업으로 나누는 Spliterator
를 반환하며 stream()
은 컬렉션을 스트림으로 변환한다.
import java.util.*;
public class ArraysEx {
public static void main(String[] args) {
int[] arr = {0,1,2,3,4};
int[][] arr2D = {{11,12,13}, {21,22,23}};
System.out.println("arr="+Arrays.toString(arr));
System.out.println("arr2D="+Arrays.deepToString(arr2D));
int[] arr2= Arrays.copyOf(arr,arr.length);
int[] arr3 = Arrays.copyOf(arr, 3);
int[] arr4 = Arrays.copyOf(arr, 7);
int[] arr5= Arrays.copyOfRange(arr, 2, 4);
int[] arr6= Arrays.copyOfRange(arr, 0, 7);
System.out.println("arr2="+Arrays.toString(arr2));
System.out.println("arr3="+Arrays.toString(arr3));
System.out.println("arr4="+Arrays.toString(arr4));
System.out.println("arr5="+Arrays.toString(arr5));
System.out.println("arr6="+Arrays.toString(arr6));
int[] arr7 = new int[5];
Arrays.fill(arr7, 9); //arr=[9,9,9,9,9]
System.out.println("arr7"+Arrays.toString(arr7));
Arrays.setAll(arr7, i -> (int)(Math.random()*6)+1);
System.out.println("arr7"+Arrays.toString(arr7));
for(int i : arr7){
char[] graph = new char[i];
Arrays.fill(graph, '*');
System.out.println(new String(graph)+i);
}
String[][] str2D = new String[][] {{"aaa","bbb"}, {"AAA","BBB"}};
String[][] str2D2 = new String[][] {{"aaa","bbb"}, {"AAA","BBB"}};
System.out.println(Arrays.equals(str2D, str2D2)); //false
System.out.println(Arrays.deepEquals(str2D, str2D2)); //true
char[] chArr ={'A','D','C','B','E'};
System.out.println("chArr="+Arrays.toString(chArr));
System.out.println("index of B ="+Arrays.binarySearch(chArr, 'B'));
System.out.println("After Sorting");
Arrays.sort(chArr);
System.out.println("chArr="+Arrays.toString(chArr));
System.out.println("index of B ="+Arrays.binarySearch(chArr, 'B'));
}
}
arr=[0, 1, 2, 3, 4]
arr2D=[[11, 12, 13], [21, 22, 23]]
arr2=[0, 1, 2, 3, 4]
arr3=[0, 1, 2]
arr4=[0, 1, 2, 3, 4, 0, 0]
arr5=[2, 3]
arr6=[0, 1, 2, 3, 4, 0, 0]
arr7[9, 9, 9, 9, 9]
arr7[1, 2, 1, 6, 1]
****1
*2
**1
***6
*****1
false
true
chArr=[A, D, C, B, E]
index of B =-2
After Sorting
chArr=[A, B, C, D, E]
index of B =1