[Java] 컬렉션(Collection) - 1. List(ArrayList)
컬렉션(Collection)이란?
- 여러 개의 다양한 데이터들을 쉽고 효과적으로 처리할 수 있도록 표준화된 방법을 제공하는 클래스들의 집합
(데이터를 효율적으로 저장하는 자료구조와 데이터를 처리하는 알고리즘이 이미 구현되어 있음)
-Java.util.패키지에 포함
컬렉션의 주요 인터페이스

인터페이스 분류 | 특징 | 구현 클래스 | |
Collection | List 계열 | 순서를 유지하고 저장 중복 저장 가능 |
ArrayList, Vector, LinkedList |
Set 계열 | 순서를 유지하지 X 중복 저장 안됨 |
HashSet, LinkedHashSet, TreeSet | |
Map 계열 | 키와 값이 쌍으로 저장 키는 중복 저장 안됨 |
HashMap, HashTable, TreeMap, Properties |
List
- 자료들을 순차적으로 나열한 자료구조로 인덱스로 관리되며 중복해서 인스턴스 저장 가능
·ArrayList
- 가장 많이 사용되는 컬렉션 클래스이다.
- JDK 1.2 부터 제공
- 내부적으로 배열을 이용하여 요소를 관리하며, 인덱스를 이용해 배열 요소에 빠르게 접근할 수 있다.
- 저장하는 크기의 제약이 없다.
- ArrayList는 배열의 단점을 보완하기 위해 만들어졌다.
- 배열은 크기를 변경할 수 없고, 요소의 추가, 삭제, 정렬 등이 복잡하다는 단점을 가지고 있다.
- ArrayList는 크기 변경(새로운 더 큰 배열을 만들고 데이터 옮기기), 요소의 추가, 삭제, 정렬 기능 처리가 간단하게 해결
(자료구조가 내장되어있어 따로 복잡한 알고리즘 불필요)
- 여러 타입의 데이터 저장 가능
(다만 인스턴스만 저장할 수 있기 때문에 기본 자료형을 저장해야 할 경우 Wrapper 클래스 사용)
- 자동적으로 수행되는 것이지 속도가 빨라지는 것은 아니다.
ArrayList 관련 코드
ArrayList는 인스턴스를 생성하게되면 내부적으로 10칸짜리 배열을 생성해서 관리한다.
ArrayList alist = new ArrayList();
다형성을 적용하여 상위 래퍼런스로 ArrayList 객체를 만들 수도 있다.
List 인터페이스 하위의 다양한 구현체들로 타입 변경이 가능하기 때문에 레퍼런스 타입은 List로 해두는 것이 더 유연한 코드를 작성하는 것이다.
List list = new ArrayList();
더 상위 타입인 Collection 타입을 사용할 수도 있다.
Collection clist = new ArrayList();
ArrayList는 저장 순번이 유지되며 index(순번)이 적용된다.
ArrayList는 Object 클래스의 하위 타입 인스턴스를 모두 저장할 수 있다.
출력해보면 저장 순서를 유지하고 있다. (toString메소드가 오버라이딩 되어 있다.)
alist.add("apple");
alist.add(123); //autoBoxint 처리됨 (값->객체)
alist.add(45.67);
alist.add(new Date());
System.out.println("alist : " + alist);
출력값=>
alist : [apple, 123, 45.67, Tue Jan 11 19:17:35 KST 2022]
size() 메소드는 배열의 크기가 아닌 요소의 갯수를 반환한다.
내부적으로 관리되는 배열의 사이즈는 외부에서 알 필요가 없기 때문에 기능을 제공하지 않는다.
System.out.println("alist의 size : " + alist.size()); //4
내부 배열에 인덱스가 저장되어 있기 때문에 for문으로도 접근 가능하다.
for(int i = 0; i < alist.size(); i++) {
/* 인덱스에 해당하는 값을 가져올 때는 get() 메소드를 사용한다. */
System.out.println(i + " : " + alist.get(i));
}
출력값=>
0 : apple
1 : 123
2 : 45.67
3 : Tue Jan 11 19:17:35 KST 2022
ArrayList는 데이터의 중복 저장을 허용한다.
배열과 같이 인덱스로 요소들을 관리하기 때문에 인덱스가 다른 위치에 동일한 값을 저장하는 것이 가능하다.
alist.add("apple");
System.out.println("alist : " + alist);
출력값=>
alist : [apple, 123, 45.67, Tue Jan 11 19:17:35 KST 2022, apple]
원하는 인덱스 위치에 값을 추가할 수도 있다.
값을 중간에 추가하는 경우 인덱스 위치에 덮어쓰는 것이 아니고 새로운 값이 들어가는 인덱스 위치에 값을 넣고 이후 인덱스는 하나씩 뒤로 밀리게 된다.
alist.add(1, "banana");
System.out.println("alist : " + alist);
출력값=>
alist : [apple, banana, 123, 45.67, Tue Jan 11 19:17:35 KST 2022, apple]
지정된 값을 삭제할 때는 remove() 메소드를 사용한다.
중간 인덱스 값을 삭제하는 경우 자동으로 인덱스를 하나씩 앞으로 당긴다.
alist.remove(2);
System.out.println("alist : " + alist);
출력값=>
alist : [apple, banana,123, 45.67, Tue Jan 11 19:17:35 KST 2022, apple]
지정된 위치의 값을 수정할 때에도 인덱스를 활용할 수 있으며 set() 메소드를 사용한다.
alist.set(1, true);
System.out.println("alist : " + alist);
출력값=>
alist : [apple, true, 45.67, Tue Jan 11 19:17:35 KST 2022, apple]
모든 컬렉션 프레임워크 클래스는 제네릭 클래스로 작성되어 있다.
List<String> stringList = new ArrayList<>();
stringList.add("apple");
제네릭 타입을 지정하면 타입 외의 인스턴스는 저장하지 못한다.
//stringList.add(123);
stringList.add("banana");
stringList.add("orange");
stringList.add("mango");
stringList.add("grape");
System.out.println("stringList : " + stringList);
출력값=>
stringList : [apple, banana, orange, mango, grape]
상단의 stringList를 오름차순 정렬해보자.
Collection 인터페이스가 아닌 Collections 클래스이다.
Collection에서 사용되는 기능들을 static메소드로 구현한 클래스이며 인터페이스명 뒤에 s가 붙은 클래스들은 관례상 비슷한 방식으로 작성된 클래스를 의미한다.
sort메소드를 사용하면 list가 오름차순 정렬 처리된 후 정렬 상태가 유지된다.
Collections.sort(stringList);
System.out.println("stringList : " + stringList);
출력값=>
stringList : [apple, banana, grape, mango, orange]
내림차순 정렬을 하려면 LinkedList로 변경해서 해야한다. (기본적으로 ArrayList에선 제공X)
stringList = new LinkedList<>(stringList);
Iterator 반복자 인터페이스를 활용해서 역순으로 정렬한다. 제네릭 적용하는 것이 좋다.
LinkedList 타입으로 형변환 한 후 descendingIterator() 메소드를 사용하면 내림차순으로 정렬된 Iterator 타입의 목록으로 반환해준다.
Iterator<String> dIter = ((LinkedList<String>)stringList).descendingIterator();
Iterator란?
- Collection 인터페이스의 iterator() 메소드를 이용해서 인스턴스를 생성할 수 있다.
- 컬렉션에서 값을 읽어오는 방식을 통일된 방식으로 제공하기 위해 사용한다.
- 반복자라고 불리며 반복문을 이용해서 목록을 하나씩 꺼내는 방식으로 사용한다.
- 인덱스로 관리되는 컬렉션이 아닌 경우에는 반복문을 사용해서 요소에 하나씩 접근할 수 없기 때문에 인덱스를 사용하지 않고도 반복문을 사용하기 위한 목록을 만들어주는 역할이다.
- hasNext() : 다음 요소를 가지고 있는 경우 true, 더 이상 요소가 없는 경우 false 반환
- next() : 다음 요소를 반환
한번 꺼내면 다시 쓸 수 없다.
while(dIter.hasNext()) {
System.out.println(dIter.next());
}
while(dIter.hasNext()) {
System.out.println(dIter.next());
} //오류는 안나지만 출력은 안 됨
출력값=>
orange
mango
grape
banana
apple
역순으로 정렬된 결과를 저장하기 위해서는 새로운 ArrayList를 만들어서 저장해두면 된다.
List<String> descList = new ArrayList<>();
while(dIter.hasNext()) {
descList.add(dIter.next());
}
System.out.println("descList : " + descList);
출력값=>
descList : [ ]
ArrayList로 여러 권의 책 목록을 관리하는 코드 구현
(기존에 데이터전송용클래스(BookDTO) 생성함)
Application 클래스
- ArrayList 인스턴스 생성, 도서 정보 추가
List<BookDTO> bookList = new ArrayList<>();
bookList.add(new BookDTO(1, "홍길동전", "허균", 50000));
bookList.add(new BookDTO(2, "목민심서", "정약용", 30000));
bookList.add(new BookDTO(3, "동의보감", "허준", 40000));
bookList.add(new BookDTO(4, "삼국사기", "김부식", 46000));
bookList.add(new BookDTO(5, "삼국유사", "일연", 58000));
AscendingPrice 클래스
가격 순으로 오름차순 정렬하기 위한 클래스
Comparator 인터페이스를 상속 받으면 오버라이딩 해야 하는 메소드가 강제화 된다.
- sort() 에서 내부적으로 사용하는 메소드
- 비교대상 두 인스턴스의 가격이 오름차순 정렬이 되기 위해서 앞의 가격이 더 작아야한다.
- 만약 뒤의 가격이 더 작은 경우 두 인스턴스의 순서를 바꿔준다.
- 그 때 두 값을 바꾸라는 신호로 양수를 보내주게 되면 정렬 시 순서를 바꾸는 조건으로 사용된다.
public class AscendingPrice implements Comparator<BookDTO> {
@Override
public int compare(BookDTO o1, BookDTO o2) {
/* 양수, 음수 형태로 두 비교 값이 순서를 바꿔야 하는지를 알려주기 위한 용도의 변수 */
int result = 0;
if(o1.getPrice() > o2.getPrice()) {
/* 오름차순을 위해 순서를 바꿔야 하는 경우 양수 반환 */
result = 1;
}else if(o1.getPrice() < o2.getPrice()) {
/* 이미 오름차순 정렬이 되어 있는 경우 음수를 반환 */
result = -1;
}else {
/* 두 값이 같은 경우 0을 반환 */
result = 0;
}
return result;
}
상단의 AscendingPrice 클래스에서 Comparator 인터페이스를 상속 받아 정렬 기준을 정해준 뒤 List의 sort() 메소드의 인자로 정렬 기준이 되는 인스턴스를 넣어주게 되면 내부적으로 우리가 오버라이딩한 메소드를 동작하여 그것을 정렬 기준으로 삼는다.
bookList.sort(new AscendingPrice());
System.out.println("가격 오름차순 정렬 ------");
for(BookDTO book : bookList) {
System.out.println(book);
}
출력값=>
가격 오름차순 정렬 ------
BookDTO [number=2, title=목민심서, author=정약용, price=30000]
BookDTO [number=3, title=동의보감, author=허준, price=40000]
BookDTO [number=4, title=삼국사기, author=김부식, price=46000]
BookDTO [number=1, title=홍길동전, author=허균, price=50000]
BookDTO [number=5, title=삼국유사, author=일연, price=58000]
인터페이스를 구현할 클래스를 재사용하는 경우 AscendingPrice 클래스처럼 작성하면 되지만
한 번만 사용하기 위해서는 익명클래스(Anonymous)를 이용한다.
익명클래스는 뒤에 { }를 만들어서 마치 Comparator 인터페이스를 상속 받은 클래스인데 이름이 없다고 생각하고 사용하는 것이다.
bookList.sort(new Comparator<BookDTO> () {
@Override
public int compare(BookDTO o1, BookDTO o2) {
/* 여기에 내림차순 정렬 조건을 넣어주면 된다.
* 아까와는 반대로 오름차순 정렬 된 상태인 경우 순서를 바꿔야 한다.
* 양수를 반환해서 순서를 바꾸라는 플래그로 이용했었다.
* */
return o2.getPrice() - o1.getPrice();
}
});
System.out.println("가격 내림차순 정렬 ------");
for(BookDTO book : bookList) {
System.out.println(book);
}
출력값=>
가격 내림차순 정렬 ------
BookDTO [number=5, title=삼국유사, author=일연, price=58000]
BookDTO [number=1, title=홍길동전, author=허균, price=50000]
BookDTO [number=4, title=삼국사기, author=김부식, price=46000]
BookDTO [number=3, title=동의보감, author=허준, price=40000]
BookDTO [number=2, title=목민심서, author=정약용, price=30000]
제목 오름차순 정렬
- 문자열은 대소 비교를 할 수 없다.
- 문자 배열로 변경 후 인덱스 하나하나를 비교해서 어느 것이 더 큰 값인지 확인해야 하는데 String 클래스의 compareTo() 메소드에서 이미 정의해 놓았다.
- 앞의 값이 더 작은 경우(즉, 바꾸지 않아도 되는 경우) 음수 반환, 같으면 0 반환,
- 앞의 값이 더 큰 경우 양수 반환(즉, 바꿔야 하는 경우)
bookList.sort(new Comparator<BookDTO>() {
@Override
public int compare(BookDTO o1, BookDTO o2) {
return o1.getTitle().compareTo(o2.getTitle());
}
});
System.out.println("제목 오름차순 정렬 ------");
for(BookDTO book : bookList) {
System.out.println(book);
}
출력값=>
제목 오름차순 정렬 ------
BookDTO [number=3, title=동의보감, author=허준, price=40000]
BookDTO [number=2, title=목민심서, author=정약용, price=30000]
BookDTO [number=4, title=삼국사기, author=김부식, price=46000]
BookDTO [number=5, title=삼국유사, author=일연, price=58000]
BookDTO [number=1, title=홍길동전, author=허균, price=50000]
제목 내림차순 설정
bookList.sort(new Comparator<BookDTO>() {
@Override
public int compare(BookDTO o1, BookDTO o2) {
return o2.getTitle().compareTo(o1.getTitle());
}
});
System.out.println("제목 내림차순 정렬 ------");
for(BookDTO book : bookList) {
System.out.println(book);
}
}
출력값=>
제목 내림차순 정렬 ------
BookDTO [number=1, title=홍길동전, author=허균, price=50000]
BookDTO [number=5, title=삼국유사, author=일연, price=58000]
BookDTO [number=4, title=삼국사기, author=김부식, price=46000]
BookDTO [number=2, title=목민심서, author=정약용, price=30000]
BookDTO [number=3, title=동의보감, author=허준, price=40000]
Comparable / Comparator 비교
Comparable | Comparator | |
패키지 | java.lang | java.util |
사용 메소드 | compareTo() | compare() |
정렬 | 기존의 정렬기준을 구현하는데 사용 | 그 외 다른 여러 기준으로 정렬하고자 할 때 사용 |
사용법 | 정렬하고자 하는 인스턴스에 Comparable를 상속받아 compareTo() 메소드를 오버라이딩해 기존의 정렬 기준 재정의 -> 한 개의 정렬만 가능 |
vo 패키지 안에 필요한 정렬 기준에 맞춘 클래스들을 생성하고 Comparator를 상속받아 compare() 메소드를 오버라이딩해 기존의 정렬 기준 재정의 -> 여러 개의 정렬 가능 |