✔️이 글은 [자바의 신 - 이상민 지음] 도서를 바탕으로 정리한 글입니다.
자바 컬렉션
자바에서 컬렉션이란 목록성 데이터를 처리하는 자료구조를 통칭한다.
자료구조는 여러 데이터를 담을 때 사용하는 것으로 배열이 가장 기본적인 자료구조이다.
배열은 크기가 정해져있을 때 성능상이나 메모리 효율면에서 가장 좋지만 크기를 모르는 경우엔 효율적이지 않다.
자료 구조
- 순서가 있는 목록(List) 형
- 순서가 중요하지 않은 셋(Set) 형
- 먼저 들어온 것이 먼저 나가는 큐(Queue)형
- 키-값(key-value)으로 저장되는 맵(Map)형
목록, 셋, 큐는 Collection이라는 인터페이스를 구현하고, 맵만 별도의 인터페이스로 구현되어 있다.
Collection 인터페이스
public interface Collection<E> extends Iterable<E>
Collection interface는 Iterable<E>라는 인터페이스를 확장했다. Iterable 인터페이스에는 Iterator<T> iterator()라는 메소드만 선언되어 있다.
Iterator 인터페이스에는 추가 데이터가 있는지 확인하는 hasNext(), 현재 위치를 다음 요소로 넘기고 그 값을 리턴해주는 next(), 데이터 삭제하는 remove() 메소드가 있다. Collection이 Iterable 인터페이스를 확장했다는 건 Iterator 인터페이스를 사용해 데이터를 순차적으로 가져올 수 있다는 의미이다.
List 인터페이스와 그 동생들
List는 배열과 비슷한 목록으로 배열처럼 순서가 있다.
목록(List)은 List 인터페이스로부터 시작되며, List는 Collection 인터페이스를 확장하였다.
List 인터페이스를 구현한 클래스에는 java.util 패키지에 있는 ArrayList, Vector, Stack, LinkedList 등이 있다.
- ArrayList : 확장 가능한 배열, JDK 1.2에서 추가됨, Not thread safe
- Vector : 확장 가능한 배열, Thread safe, ArrayList와 거의 유사함
- Stack : LIFO(Last In First Out)을 지원하는 클래스로 Vector 클래스를 확장함
- LinkedList : "목록"에도 속하지만 "큐"에도 속하는 클래스,
ArrayList에 대해서 파헤쳐보자
ArrayList 클래스의 상속 관계
java.lang.Object
ㄴjava.util.AbstractCollection<E>
ㄴjava.util.AbstractList<E>
ㄴjava.util.ArrayList<E>
AbstractCollection은 Collection 인터페이스 중 일부 공통적인 메소드를 구현, AbstractList는 List 인터페이스 중 일부 공통적인 메소드를 구현해 놓은 것이다.
ArrayList가 구현한 모든 인터페이스는 Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess이다.
Serializable은 원격으로 객체를 전송하거나 파일에 저장할 수 있음을 지정해주고, Cloneable은 Object 클래스의 clone() 메소드가 수행돼 복제가 가능한 객체임을 의미하고, RandomAccess는 목록형 데이터에 보다 빠르게 접근할 수 있도록 임의로 접근하는 알고리즘이 적용된다는 것을 지정해준다.
ArrayList의 생성자는 3개다
ArrayList는 확장 가능한 배열로 배열처럼 사용은 하지만 대괄호를 사용하지 않고, 메소드를 통해 객체를 삽입, 삭제, 조회한다.
생성자
- ArrayList() : 객체를 저장할 공간이 10개(기본)인 ArrayList생성
- ArrayList(Collection<? extends E> c) : 매개 변수로 넘어온 컬렉션 객체가 저장되어 있는 ArrayList 생성
- ArrayList(int initialCapacity) : 매개 변수로 넘어온 값만큼의 저장공간을 갖는 ArrayList 생성
보통은 ArrayList에 한가지 종류의 객체만 저장하는 것이 좋아 객체를 생성할 때 제네릭을 사용하여 선언하는 것을 권장한다.
여러 종류를 한 객체에 담을 땐 DTO객체를 사용하는 것이 좋다.
ArrayList 생성시 매개 변수를 지정해주지 않으면 기본이 10갠데 그 크기를 초과하면 크기를 늘이는 작업이 내부에서 자동으로 수행된다. 이런 작업이 자주 발생하면 성능에 악영향을 주기 때문에 크기를 지정하는 것을 권장한다.
ArrayList에 데이터를 담아보자
add()와 addAll() 메소드로 ArrayList 클래스에 데이터를 담을 수 있다. 첫번째 매개변수로 index를 추가로 넘겨주면 지정된 위치에 추가할 수도 있다.
ArrayList<String> list = new ArrayList<>();
ArrayList<String> list2 = list;
로 선언을 하면 list2에는 list객체의 값이 복사되는 것이 아닌 list 객체 자체를 참조하여 같은 hashcode() 값을 사용하게 된다. 이런 방식을 Shallow copy라고 하고, 객체의 값을 모두 복사해 복제된 객체에서 값을 변경해도 원본에 영향이 없도록 할때는 Deep copy를 수행한다.
Deep copy는 list2.addAll(list);나 list2 생성시 new ArrayList<String>(list);로 선언해주면 된다.
ArrayList에서 데이터를 꺼내자
- size() : ArrayList 객체에 들어가있는 데이터의 개수를 리턴
- get(int index) : ArrayList에 있는 값을 가져올 때 사용하는 메소드
- indexOf(Object o) : 객체 o와 동일한 데이터의 위치를 리턴
- lastIndexOf(Object o) : 객체 o와 동일한 마지막 데이터 위치를 리턴
ArrayList에는 중복된 데이터를 넣을 수 있어 앞에서부터 찾을 땐 indexOf()를 뒤에서부터 찾을 땐 lastIndexOf()를 사용한다.
ArrayList에 있는 데이터를 배열로 뽑아내는 메소드
- toArray() : ArrayList 안 데이터들을 Object[] 타입의 배열로 만듦
- toArray(T[] a) : ArrayList 안 데이터들을 매개변수로 넘어온 타입의 배열로 만듦
Java 8에서부터는 toArray()의 매개변수로 String 배열을 넘겨줄 때
- (배열의 크기 < ArrayList 크기) : ArrayList 크기에 맞는 새로운 배열을 생성해준다.
- (배열의 크기 > ArrayList 크기) : 남는 배열의 공간은 null 값으로 처리된다.
ArrayList에 있는 데이터를 삭제하자(변경도 포함)
ArrayList에 있는 데이터를 삭제하는 메소드
- clear() : 모든 데이터 삭제
- remove(int index) : 해당 위치에 있는 데이터 삭제하고 삭제한 데이터 리턴
- remove(Object o) : 객체 o와 동일한 첫번째 데이터 삭제
- removeAll(Collection<?> c) : 컬렉션 객체 c에 있는 데이터와 동일한 모든 데이터 삭제
ArrayList에 있는 값을 변경하는 메소드
- set(int index, E element) : 지정한 위치에 있는 데이터를 element로 변경하고 원래 그 위치에 저장된 값을 리턴
ArrayList 공간을 변경하는 메소드
- trimToSize()
- ArrayList 객체 공간의 크기를 데이터의 개수만큼으로 변경
- 저장할 수 있는 공간은 만들어 두었지만, 데이터가 저장되어 있지 않을때 해당 공간을 없애줌
- ArrayList 객체를 원격으로 전송하거나, 파일로 저장할 때 데이터 크기를 줄일 수 있다는 장점이 있음
Vector는 ArrayList와 달리 Thread safe한데 ArrayList도 다음과 같이 객체를 생성하면 Thread safe해질 수 있다.
List list = Collections.synchronizedList(new ArrayList(...));
Stack 클래스는 뭐가 다른데?
Stack은 마지막에 들어온 데이터를 가장 먼저 꺼내는 LIFO 기능을 구현하려고 할 때 쓰는 클래스이다.
LIFO 기능만 사용하기 위해서는 더 빠른 ArrayDequeue가 더 좋지만 쓰레드에 안전하지 못하기 때문에 쓰레드에 안전한 LIFO 기능을 원하면 Stack을 사용하면 된다.
Stack의 상속 관계
java.lang.Object
ㄴ java.util.AbstractCollection<E>
ㄴ java.util.AbstractList<E>
ㄴ java.util.Vector<E>
ㄴ java.util.Stack<E>
Stack의 부모 클래스는 Vector로 Vector의 메소드를 사용할 수 있다.
Stack에서 구현한 인터페이스는 Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess로 ArrayList와 동일하다.
Stack의 생성자
- Stack() : 아무 데이터도 없는 Stack 객체를 생성
Stack의 메소드
- empty() : 객체가 비었는지 확인
- peek() : 객체의 가장 위 데이터 리턴
- pop() : 객체의 가장 위 데이터 지우고 리턴
- push(E item) : item을 가장 위에 저장
- search(Object o) : o의 위치를 리턴
Stack의 LIFO를 생각하면 Vector에 속하면 안되지만 이 클래스가 초기부터 존재해서 자바의 하위 호완성을 위해 Vector의 상속을 받는다.
정리해 봅시다
1. Collection 인터페이스를 구현하는 대표적인 3개의 자료구조에는 어떤 것들이 있나요?
목록(List), 셋(Set), 큐(Queue)
2. 배열과 같이 순서가 있는 목록형을 나타내는 대표 인터페이스는?
List
3. ArrayList라는 클래스의 생성자 중 매개 변수가 없는 기본 생성자를 사용하면 기본적으로 몇 개의 저장 공간을 가지나요?
10개
4. 만약 ArrayList 클래스의 저장 공간 개수를 처음부터 지정하려면 어떤 생성자를 사용하면 되나요?
ArrayList(int initialCapacity)
5. ArrayList 객체를 생성할 때 제네릭을 사용하는 이유는?
제네릭 사용시 컴파일 시점에 타입을 잘못 지정한 부분을 걸러내 줄 수 있기 때문이다.
6. ArrayList에 데이터를 담는 메소드 이름 두 가지는?
add(), addAll()
7. Collection 인터페이스를 구현한 클래스의 객체에서 사용할 수 있는 for 루프의 구조는 어떻게 되나요?
for (타입 변수명 : 객체) { }
8. Collection 인터페이스를 구현한 클래스의 객체 크기를 확인하는 메소드는?
size()
9. ArrayList에서 특정 위치에 있는 데이터 확인 메소드는?
get()
10. ArrayList에서 특정 위치에 있는 데이터를 삭제하는 메소드는?
remove()
11. ArrayList에서 특정 위치에 있는 데이터를 수정하는 메소드는?
set()
12. java.util 패키지에 있는 Stack이라는 클래스는 어떤 클래스를 확장한 것인가요?
List 인터페이스를 구현하고 Vector 클래스를 확장했다.
13. Stack 클래스에서 데이터를 담는 메소드는?
push()
14. Stack 클래스에서 가장 위에 있는 데이터를 확인만 하는 메소드는?
peek()
15. Stack 클래스에서 가장 위에 있는 데이터를 삭제하고 리턴하는 메소드는?
pop()
'도서 > 자바의 신' 카테고리의 다른 글
[도서/자바의 신] #24 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part 3(Map) (1) | 2022.12.30 |
---|---|
[도서/자바의 신] #23 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part 2(Set과 Queue) (0) | 2022.12.30 |
[도서/자바의 신] #21 실수를 방지하기 위한 제네릭이라는 것도 있어요 (0) | 2022.12.29 |
[도서/자바의 신] #20 가장 많이 쓰는 패키지는 자바랭 (0) | 2022.12.29 |
[도서/자바의 신] #19 이쯤에서 자바의 역사와 JVM에 대해서 알아보자 (0) | 2022.12.29 |