22장 - 자바랭 다음으로 많이 쓰는 애들은 컬렉션 ― Part1 (List)
자바의 컬렉션
- 목록성 데이터를 처리하는 자료 구조를 통칭
자료 구조
- Data Structure
- 어떤 정보를 담는 것
- 하나의 데이터가 아닌 여러 데이터 담을 때 사용
- ex) 배열, DTO, ...
배열
- 가장 기본적인 자료 구조
- 성능상이나 메모리 효율면에서 가장 좋음
- But 그 크기가 정해져 있있을 때 유용
자바의 데이터 담는 자료 구조
List | Set | Queue | Map |
순서 있는 목록 | 순서 중요하지 않음 | 먼저 들어온 것이 먼저 나감 | 키-값(key-value)으로 저장됨 |
Collection
Collection 인터페이스 선언
public interface Collection<E> extends Iterable<E>
Iterable 인터페이스의 메서드
리턴 타입 | 메서드 이름 및 매개 변수 |
Iterator<T> | iterator() |
Iterator 인터페이스의 메서드
hasNext() | next() | remove() |
추가 데이터가 있는지 확인 | 현재 위치를 다음 요소로 넘기고 그 값 리턴 | 데이터 삭제 |
- Collection 인터페이스가 Iterable 인터페이스를 확장했다
= Iterator 인터페이스를 사용해 데이터를 순차적으로 가져올 수 있다
Collection 인터페이스의 주요 메서드 목록
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
boolean | add(E e) | 요소 추가 |
boolean | addAll(Collection) | 매개 변수로 넘어온 컬렉션의 모든 요소 추가 |
void | clear() | 컬렉션에 있는 모든 요소 데이터 삭제 |
boolean | contains(Object) | 매개 변수로 넘어온 객체가 해당 컬렉션에 있는지 확인 동일한 값 있으면 true 리턴 |
boolean | containsAll(Collection) | 매개 변수로 넘어온 객체들이 해당 컬렉션에 있는지 확인 매개 변수로 넘어온 컬렉션에 있는 요소들과 동일한 값들이 모두 있으면 true 리턴 |
boolean | equals(Object) | 매개 변수로 넘어온 객체와 같은 객체인지 확인 |
int | hashCode() | 해시 코드값 리턴 |
boolean | isEmpty() | 컬렉션이 비어있는지 확인 비어있으면 true 리턴 |
Iterator | iterator() | 데이터를 한 건씩 처리하기 위한 Iterator 객체 리턴 |
boolean | remove(Object) | 매개 변수와 동일한 객체 삭제 |
boolean | removeAll(Collection) | 매개 변수로 넘어온 객체들을 해당 컬렉션에서 삭제 |
boolean | retainAll(Collection) | 매개 변수로 넘어온 객체들만을 컬렉션에 남겨둠 |
int | size() | 요소의 개수 리턴 |
Object[] | toArray() | 컬렉션에 있는 데이터들을 배열로 복사 |
<T> t[] | toArray(T[]) | 컬렉션에 있는 데이터들을 지정한 타입의 배열로 복사 |
List 인터페이스
- Collection 인터페이스 확장함
- 배열처럼 순서 있음
- java.util 패키지에서 ArrayList, Vector, Stack, LinkedList 많이 사용
ArrayList | Vector |
Thread safe X | Thread safe |
사용법 및 기능 거의 비슷함 확장 가능한 배열이라 생각하면 됨 |
Thread safe한 ArrayList 객체 생성 방법
List list = Collections.synchronizedList(new ArrayList(...));
- 위와 같이 생성하지 않을 시 원치 않는 데이터 나올 수도 있음
Stack 클래스
- Vector 클래스 확장함
- LIFO (Last In First Out; 가장 마지막에 추가한 값을 가장 처음 빼내는 것) 지원
- 프로그래밍 언어에서의 스택 : 보통 메서드가 호출된 순서를 기억하는 장소를 일컬음
- 컬렉션, 쓰레드, IO, 네트워크 등의 관련 클래스 사용할 때 상속 관계 살펴보는 게 좋음
- 그 클래스의 API에 있는 메서드나 상수 뿐만 아니라 부모 클래스에 선언된 메서드도 사용할 수 있기에
ArrayList 클래스
ArrayList 상속 관계
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.ArrayList<E>
ArrayList가 구현한 인터페이스
인터페이스 | 용도 |
Serializable | 원격으로 객체 전송 및 파일에 저장할 수 있음을 지정 |
Cloneable | Object 클래스의 clone() 메서드가 제대로 수행될 수 있음을 지정 ➡ 복제가 가능한 객체임을 의미 |
Iterable<E> | 객체가 foreach 문장 사용할 수 있음을 지정 |
Collection<E> | 여러 개의 객체를 하나의 객체에 담아 처리할 때의 메서드 지정 |
List<E> | 목록형 데이터를 처리하는 것과 관련된 메서드 지정 |
RandomAccess | 목록형 데이터에 보다 빠르게 접근할 수 있게 임의로(random하게) 접근하는 알고리즘이 적용된다는 것을 지정 |
ArrayList의 생성자
생성자 | 설명 |
ArrayList() | 객체 저장할 공간이 10개인 ArrayList 만듦 |
ArrayList(Collection<? extends E> c) | 매개 변수로 넘어온 컬렉션 객체가 저장된 ArrayLIst 만듦 |
ArrayList(int initialCapacity) | 매개 변수로 넘어온 initialCapacity 개수만큼 저장 공간을 갖는 ArrayList 만듦 |
- ArrayList는 대부분 서로 다른 종류의 객체를 하나의 배열에 넣지 않고, 한 가지 종류의 객체만 저장
- 여러 종류를 하나의 객체에 담을 때에는 되도록 DTO 객체를 하나 만들어 담는 것이 좋음
- ➡ 컬렉션 관련 클래스의 객체 선언 시 제네릭 사용하여 선언하는 것이 좋음
- ArrayList 객체 선언 시 매개 변수 넣지 않으면 초기 크기 = 10
- ➡ 10개 이상의 데이터가 들어가면 크기를 늘이는 작업이 ArrayList 내부에서 자동으로 수행됨
- ➡ 애플리케이션 성능에 영향을 주게 됨
- ➡ 저장되는 데이터 크기가 어느 정도 예측 가능하다면 예측한 초기 크기를 지정하는 게 좋음
ArrayList에 데이터 담는 메서드
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
boolean | add(E e) | 매개 변수로 넘어온 데이터를 가장 끝에 담음 |
void | add(int index, E e) | 매개 변수로 넘어온 데이터를 지정된 index 위치에 담음 |
boolean | addAll(Collection<? extends E> c) | 매개 변수로 넘어온 컬렉션 데이터를 가장 끝에 담음 |
boolean | addAll(int index, Collection<? extends E> c) | 매개 변수로 넘어온 컬렉션 데이터를 index에 지정된 위치부터 담음 |
- ArrayList는 확장된 배열 타입 ➡ 순서가 매우 중요
- 배열처럼 가장 첫 위치는 0부터 시작
add(int index, E e)
- 지정된 위치에 데이터 담음
- ➡ 지정된 위치에 있던 기존 데이터들은 위치가 하나씩 뒤로 밀려남
IndexOutOfBoundsException
- ArrayList 작업 시 위치(index)를 잘못 지정하면 실행 시 예외 발생
- ex) 위치가 ArrayList 길이보다 큰 숫자인 경우 등
- Collection 인터페이스 구현한 모든 클래스에서 사용할 수 있는 향상된 for문
for(타입이름 임시변수명 : 반복대상객체) {
}
addAll()
public void checkArrayList3() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.add(1, "A1");
ArrayList<String> list2 = new ArrayList<String>();
list2.add("0 ");
list2.addAll(list);
for(String tempData : list2) {
System.out.println("List2 " + tempData);
}
}
//결과
//List2 0
//List2 A
//List2 A1
//List2 B
//List2 C
//List2 D
//List2 E
- 비어있는 list2 객체에 list 값들이 들어감
- list에 있는 내용들이 list2에 복사됨
- 만약 list의 값을 list2에 복사해야 할 일이 생길 때 생성자 이용하면 편리함
ArrayList<String> list2 = new ArrayList<String>(list);
- ArrayList에는 Collection 인터페이스를 구현한 어떤 클래스도 포함시킬 수 있는 생성자가 있기 때문
public void checkArrayList4() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
ArrayList<String> list2 = list;
list.add("Ooops");
for(String tempData : list2) {
System.out.println("List2 " + tempData);
}
}
//결과
//List2 A
//List2 Ooops
- list2는 list 값만 사용하겠다는 게 아님
- list라는 객체가 생성되어 참조되고 있는 주소까지도 사용하겠다는 의미
- (자바의 모든 객체는 생성되면 그 객체가 위치하는 주소가 내부적으로 할당됨)
- ➡ list2 = list: 두 객체의 변수는 다르나, 하나의 객체가 변경되면 다른 이름의 변수를 갖는 객체의 내용도 바뀜
Shallow copy & Deep copy
Shallow copy | Deep copy |
list2 = list 같이 다른 객체에 원본 객체의 주소값만 할당하는 것 | 객체의 모든 값을 복사해 복제된 객체에 있는 값을 변경해도 원본에 영향이 없도록 할 때 수행 ex) 배열 복사할 때 System 클래스에 있는 arraycopy() 같은 메서드 이용 시 Deep copy 쉽게 처리 가능 |
- ➡ 하나의 Collection 관련 객체를 복사할 일 있을 때 이처럼 생성자 사용하거나 addAll() 메서드 사용하는 게 좋음
ArrayList에서 데이터 꺼내기
size()
- ArrayList 객체에 들어있는 데이터의 개수 가져오는 메서드
배열.length | size() |
배열의 저장 공간 개수 | 들어있는 데이터 개수 |
- ArrayList 초기 크기 지정할 때
- 그 크기는 ArrayList에 담을 수 있는 공간의 개수일 뿐
- 데이터가 들어있는 개수를 의미하는 게 아님
public void checkArrayList5() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
int listSize = list.size();
for(int loop = 0; loop < lilstSize; loop++) {
System.out.println("list.get(" + loop + ") = " + list.get(loop));
}
}
- get() 메서드로 ArrayList 객체에 있는 값 가져옴
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
int | size() | ArrayList 객체에 들어있는 데이터의 개수 리턴 |
E | get(int index) | 매개 변수에 지정한 위치에 있는 데이터 리턴 |
int | indexOf(Object o) | 매개 변수로 넘어온 객체와 동일한 데이터의 위치 리턴 |
int | lastIndexOf(Object o) | 매개 변수로 넘어온 객체와 동일한 마지막 데이터의 위치 리턴 |
- ArrayList는 중복된 데이터 넣을 수 없기에 indexOf()와 lastIndexOf() 메서드 필요
toArray()
- ArrayList 객체에 있는 데이터 ➡ 배열로 뽑아낼 때
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
Object[] | toArray() | ArrayList 객체에 있는 값들을 Object[] 타입의 배열로 만듦 |
<T> T[] | toArray(T[] a) | ArrayList 객체에 있는 값들을 매개 변수로 넘어온 T 타입의 배열로 만듦 |
- Object 타입의 배열로만 리턴
- ➡ 제네릭 사용해 선언한 ArrayList 객체를 배열로 생성할 때 이 메서드 사용하는 건 좋지 않음(아래 메서드 활용하기)
public void checkArrayList6() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
String[] strList = list.toArray(new String[0]);
System.out.println(strList[0]);
}
- toArray() 메서드의 매개 변수에 변환하려는 타입의 배열 지정
- ArrayList 객체의 데이터 크기가 매개 변수로 넘어간 배열 객체의 크기보다 클 때 매개 변수인 배열의 모든 값이 null로 채워짐
- 여기서의 new String[0] : 위의 상황인 배열의 모든 값이 null로 지정되는 걸 방지함
public void checkArrayList7() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
String[] tempArray = new String[5];
String[] strList = list.toArray(tempArray);
for(String temp : strList) {
System.out.println(temp);
}
}
//결과
//A
//B
//C
//null
//null
public void checkArrayList7() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
String[] tempArray = new String[2];
String[] strList = list.toArray(tempArray);
for(String temp : strList) {
System.out.println(temp);
}
}
//결과
//null
//null
- toArray() 사용 시 new String[0]처럼 크기가 0인 배열 넘겨주는 게 Best
ArrayList에 있는 데이터 삭제
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
void | clear() | 모든 데이터 삭제 |
E | remove(int index) | 매개 변수에서 지정한 위치에 있는 데이터 삭제 후, 삭제한 데이터 리턴 |
boolean | remove(Object o) | 매개 변수로 넘어온 객체와 동일한 첫 번째 데이터 삭제 |
boolean | removeAll(Collection<?> c) | 매개 변수로 넘어온 컬렉션 객체에 있는 데이터와 동일한 모든 데이터 삭제 |
remove(int index)
public void checkArrayList8() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("A");
System.out.println("Removed " + list.remove(0));
for(int loop = 0; loop < list.size(); loop++) {
System.out.println("list.get(" + loop + ") = " + list.get(loop));
}
}
//결과
//Removed A
//list.get(0) = B
//list.get(1) = C
//list.get(2) = A
remove(Object o)
public void checkArrayList8() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("A");
System.out.println(list.remove("A"));
for(int loop = 0; loop < list.size(); loop++) {
System.out.println("list.get(" + loop + ") = " + list.get(loop));
}
}
//결과
//true
//list.get(0) = B
//list.get(1) = C
//list.get(2) = A
removeAll(Collection c)
public void checkArrayList8() {
ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("A");
ArrayList<String> temp = new ArrayList<String>();
temp.add("A");
list.removeAll(temp);
for(int loop = 0; loop < list.size(); loop++) {
System.out.println("list.get(" + loop + ") = " + list.get(loop));
}
}
//결과
//list.get(0) = B
//list.get(1) = C
ArrayList 객체에 있는 값 변경
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
E | set(int index, E element) | 지정한 위치에 있는 데이터를 두 번째 매개 변수로 넘긴 값으로 변경 이후 해당 위치에 있던 변경되기 전의 데이터 리턴 |
trimToSize()
- ArrayList 객체 공간의 크기를 데이터 개수만큼으로 변경
- String 클래스의 trim() 메서드와 같은 역할
- 저장할 수 있는 공간은 만들었으나 데이터가 저장되어 있지 않을 때 해당 공간 없애줌
- ex) ArrayList의 객체를 원격 전송 및 파일로 저장하는 경우 이 메서드 통해 데이터 크기 줄일 수 있음
Stack 클래스
- List 인터페이스 구현
- LIFO (Last In First Oust): 마지막에 들어온 데이터를 가장 처음에 꺼냄
LIFO 기능 가능한 클래스 | |
Stack | ArrayDeque |
ArrayDeque보다 성능 약간 떨어짐 Thread safe |
Stack보다 LIFO 빠름 Thread safe X |
Stack 클래스의 상속 관계
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.Vector<E>
java.util.Stack<E>
- Vector: Stack의 부모 클래스 ➡ Stack은 Vector 클래스에서 제공하는 모든 메서드 사용 가능
- Stack 클래스는 자바에서 상속을 잘못 받은 클래스임(Vector에 속하면 안 됨, but 자바의 하위 호환성 위해 이 상속 관계 계속 유지 중)
Stack 클래스의 생성자
생성자 | 설명 |
Stack() | 아무 데이터도 없는 Stack 객체 만듦 |
Stack 클래스의 메서드
리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
boolean | empty() | 객체가 비어있는지 확인 |
E | peek() | 객체의 가장 위에 있는 데이터 리턴 |
E | pop() | 객체의 가장 위에 있는 데이터 지운 후 해당 값 리턴 |
E | push(E item) | 매개 변수로 넘어온 데이터를 가장 위에 저장 |
int | search(Object o) | 매개 변수로 넘어온 데이터의 위치 리턴 |
package d.collection;
import java.util.Stack;
public class StackSample {
public static void main(String[] args) {
StackSample sample = new StackSample();
sample.checkPeek();
}
public void checkPeek() {
Stack<Integer> intStack = new Stack<Integer>();
for(int loop = 0; loop < 5; loop++) {
intStack.push(loop);
System.out.println(intStack.peek());
}
System.out.println("size = " + intStack.size());
}
}
//결과
//0
//1
//2
//3
//4
//size = 5
- Stack에서 값 하나씩 확인하며 제거하려면
public void checkPop() {
Stack<Integer> intStack = new Stack<Integer>();
for(int loop = 0; loop < 5; loop++) {
intStack.push(loop);
System.out.println(intStack.pop());
}
System.out.println("size = " + intStack.size());
}
//결과
//0
//1
//2
//3
//4
//size = 0
'Backend > Java' 카테고리의 다른 글
[자바의 신] 24장 (0) | 2022.08.09 |
---|---|
[자바의 신] 23장 (0) | 2022.08.09 |
[자바의 신] 21장 (0) | 2022.08.09 |
[자바의 신] 20장 (0) | 2022.07.26 |
[자바의 신] 19장 (0) | 2022.07.26 |
댓글