컬렉션 프레임워크는 주요 인터페이스로 List,Set,Map을 제공한다. 각각의 인터페이스를 간단하게 요약하면
ArrayList는 List 인터페이스를 구현한 클래스로, 컬렉션 프레임워크에서 가장 많이 사용된다. ArrayList에 객체를 추가하면 객체가 인덱스로 관리되어 배열과 유사하다. 하지만 배열은 생성될 때 크기가 고정되고, 크기를 변경할 수 없는 반면에 ArrayList는 자동으로 저장용량이 증가된다. 또한 데이터의 순서를 유지한다.
ArrayList<타입 매개변수> 객체명 = new ArrayList<타입 매개변수>(초기 저장 용량);
ArrayList<String> container1 = new ArrayList<String>();
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량이 인자로 전달되지 않으면 기본적으로 10으로 지정됩니다.
ArrayList<String> container2 = new ArrayList<String>(30);
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량을 30으로 지정하였습니다.
LinkedList 컬렉션은 데이터를 효율적으로 추가,삭제,변경하기 위해 사용한다. 배열에는 모든 데이터가 연속적으로 존재하지만, LinkedList에는 불연속적으로 존재하며, 이 데이터는 서로 연결되어 있다.
LinkedList에서 데이터를 삭제하려면 , 삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하면 된다. 배여러럼 데이터를 이동하기 위해 복사할 필요가 없기 때문에 처리 속도가 훨씬 빠르다.
아래의 예제를 통해 LinkedList를 활용해보면
public class LinkedListExample {
public static void main(String[] args) {
// Linked List를 생성하여 list에 할당
LinkedList<String> list = new LinkedList<>();
// String 타입의 데이터를 LinkedList에 추가
list.add("Java");
list.add("egg");
list.add("tree");
// 저장된 총 객체 수 얻기
int size = list.size();
// 0번 인덱스의 객체 얻기
String skill = list.get(0);
// 저장된 총 객체 수 만큼 조회
for(int i = 0; i < list.size(); i++){
String str = list.get(i);
System.out.println(i + ":" + str);
}
// for-each문으로 순회
for (String str: list) {
System.out.println(str);
}
// 0번 인덱스 객체 삭제
list.remove(0);
}
}
Iterator은 직역하면 반복자라는 의미를 가지고, 컬렉션에 저장된 요소들을 순차적으로 읽어오는 역할을 한다.
이러한 Iteraotr의 컬렉션 순회 기능은 Iterator 인터페이스에 정의되어져 있으며, Collection 인터페이스에는 Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 메서드인 Iterator()가 정의되어져 있다.
즉, Collection 인터페이스에 정의된 Iterator()을 호출하면, Iterator 타입의 인스턴스가 반환된다.
다음은 Iterator 인터페이스에 정의된 메서드이다.
다음은 List에서 String 객체들을 반복해서 하나씩 가져오는 코드 예제이다.
ArrayList<String> list = ...;
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) { // 읽어올 다음 객체가 있다면
String str = iterator.next(); // next()를 통해 다음 객체를 읽어옵니다.
...
}
Iterator를 사용하지 않더라도 for-each 문을 이용해서 전체 객체를 대상으로 반복할 수 있다.
ArrayList<String> list = ...;
for(String str : list) {
...
}
Set은 요소의 중복을 허용하지 않고, 저장 순서를 유지하지 않는 컬렉션이다. 대표적인 Set을 구현한 클래스에는 HashSet, TreeSet이 있다.
HashSet은 Set 인터페이스를 구현한 가장 대표적인 컬렉션 클래스이다. 따라서 Set 인터페이스의 특성을 그대로 물려받으므로 중복된 값을 허용하지 않으며, 저장 순서를 유지하지 않는다.
아래의 예제를 통해 Set 사용을 알아보면
import java.util.*;
public class Main {
public static void main(String[] args) {
// HashSet 생성
HashSet<String > languages = new HashSet<String>();
// HashSet에 객체 추가
languages.add("Java");
languages.add("Python");
languages.add("Javascript");
languages.add("C++");
languages.add("Kotlin");
languages.add("Ruby");
languages.add("Java"); // 중복
// 반복자 생성하여 it에 할당
Iterator it = languages.iterator();
// 반복자를 통해 HashSet을 순회하며 각 요소들을 출력
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
TreeSet은 이진 탐색 트리 형태로 데이터를 저장한다. 데이터의 중복 저장을 허용하지 않고 저장 순서를 유지하지 않는 Set 인터페이스의 특징은 그대로 유지된다.
Map 인터페이스는 키와 값으로 구성된 객체를 저장하는 구조를 가지고 있다. 여기서 이 객체를 Entry 객체라고 하는데, 이 Entry 객체는 키와 값을 각각 키 객체와 값 객체로 저장한다.
다음은 Map 인터페이스를 구현한 클래스에서 공통적으로 사용 가능한 메서드이다. List가 인덱스를 기준으로 관리되는 반면에, Map은 키로 객체드를 관리하기 때문에 키를 매개값으로 갖는 메서드가 많다.
HashMap은 Map 인터페이스를 구현한 대표적인 클래스이다.
HashMap은 해시 함수를 통해 키와 값이 저장되는 위치를 결정하므로, 사용자는 그 위치를 알 수 없고, 삽입되는 순서와 위치 또한 관계가 없다.
Map.Entry 인터페이스에는 다음과 같은 메서드가 정의 되어 있다.
HashMap<String, Integer> hashmap = new HashMap<>();
해시 맵을 생성할 때에는 위 코드처럼 키와 값의 타입을 따로 지정해주어야 한다.