자바의 여러가지 컬렉션 프레임워크에 대해서 공부하면서 궁금한 점이 생겨서 배운점들을 토대로 글을 정리해보려고 한다.
자바에서 지원하는 여러 자료구조들이 존재하는데, 각각의 자료구조마다 데이터를 접근하여 순회하는 방법이 달랐다.
크게 List, Set, Map에 대해서 살펴보자.
List
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//list.add() 생략
for (Integer integer : list) {
System.out.println(integer);
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list);
}
}
List 자료구조를 보면 데이터를 순회하는 방식이 총 2가지가 있다.
i 인덱스를 통한 전통적인 for문 방식과, forEach를 통하여 접근하는 방식
Set
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
// add 생략
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
for (Integer integer : set) {
System.out.println(integer);
}
}
Set같은 경우에는 index로 접근할 수 없었다. Set은 값의 중복과, 요소들의 순서를 허용하지 않기 때문에!!
List는 요소들의 순서와 중복을 허용하기 때문에, 요소를 꺼낼 때에도 인덱스를 통해 값을 꺼내온다.
만약 List가 중복을 허용하면서 인덱스를 통해 값을 꺼내오는게 아닌, Map과 Set처럼 키를 통해 데이터를 찾는 구조일 경우, 중복을 허용하기 때문에, 중복되는 값들 중 어떠한 값을 꺼내올지 모르게 된다.
(만약 list.add("A"),list.add("A")을 통해 "A"을 두 번 저장, list.get("A")로 접근할 경우 "A"이란 데이터는 총 2개)
이러한 특성 때문에 List는 인덱스를 통해 접근이 가능하기 때문에 전통적인 for문이 가능하지만 Map과 Set은 Key를 가지고 데이터를 찾아오기 때문에 전통적인 for문으로 데이터의 접근이 불가능 한것.
Map
public class Main {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
// put 생략
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
int value = entry.getValue();
System.out.println(entry.getKey() + ": " + value);
}
}
}
Map은 데이터의 저장 구조 Key-Value 구조이다.
해당 데이터 구조 또한 데이터를 꺼내올 때 index가 아닌 Set과 마찬가지로 Key를 통해 데이터에 접근할 수 있다.
그리고 Key와 Value 저장 구조이기 때문에 데이터에 접근해서 출력하기 위해서는 entrySet메서드를 통해 접근할 수 있다.
공통점
자바의 대표적인 자료구조 3가지를 보면서 데이터의 순회방식을 살펴보았다.
List와 Set,Map 세 가지 다 데이터를 순회하는 방식이 묘하게 달랐지만 공통점이 하나 있다.
각자 다른 방식으로 데이터를 저장하고 꺼내오는 방식도 각자 다른지만 향상된 for문으로 모든 자료구조의 데이터를 접근할 수 있다는 것이다.
자바 또한 각자 다른방식으로 데이터를 순회하게 된다면 매우 불편하고, 자료구조의 구현과 관계없이 모든 자료구조를 동일한 방식으로 순회할 수 있는 일관성있는 방법을 생각해 냈을 것이다.
자바는 이런 문제를 해결하기 위해 Iterable 과 Iterator 인터페이스를 제공한다.
Iterable 인터페이스의 주요 메서드
public interface Iterable<T> {
Iterator<T> iterator();
//...
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
그리고 Iterable은 Iterator를 반활할 뿐이다. 여기서 Iterator는 실제로 데이터를 순회하는 역할을 수행하기 위한 메서드를 제공할 뿐이다.
Iterator 인터페이스의 주요 메서드
public interface Iterator<E> {
boolean hasNext();
E next();
}
자료구조의 데이터를 순회하는 방법
로직 자체는 매우 간단하다. 해당 자료구조에 데이터를 가지고 있는지 물어보고, 있으면 요소를 꺼내오고, 없으면 그만둘 뿐이다.
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
해당 메서드는 Iterable의 인터페이스가 제공하는 defualt 메서드이다.
해당 메서드 덕분에 while문을 이용한 순회가 향상된 for문으로 대체될 수 있는 것이다.
모든 자료구조가 향상된 for문을 사용할 수 있는 이유

Collection의 최상위에는 Iterable이 있는걸 볼 수 있다.
즉 자바의 모든 컬렉션 프레임워크들은 데이터를 접근해서 순회할 수 있다는 뜻이다.
Map의 경우 Key 뿐만아니라 Value 까지 있기 때문에 바로 순회를 할 수는 없다.(Map은 Collection인터페이스를 상속받지 않는다.)
대신에 Key 나 Value 를 정해서 순회할 수 있는데, keySet() , values() 를 호출하면 Set , Collection 을 반환하기 때문에 Key 나 Value 를 정해서 순회할 수 있다.
물론 Entry 를 Set 구조로 반환하는 entrySet() 도 순회가 가능하다.
Collection Framework의 최상위 계층에는 결국 Iterable이 있기 때문에 이런식으로도 코드를 짤 수 있다.
import java.util.*;
public class JavaIterTestMain {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
printAll(list.iterator());
forEachPrintAll(list);
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
printAll(set.iterator());
forEachPrintAll(set);
}
private static void printAll(Iterator<Integer> iterator) {
System.out.println("Iter = " + iterator.getClass());
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
private static void forEachPrintAll(Iterable<Integer> iterable) {
System.out.println("Iterable = " + iterable.getClass());
for (Integer integer : iterable) {
System.out.println(integer);
}
}
}
prtinAll 메서드와 forEachPrintAll 메서드의 매개변수로 Iterable을 받는걸 볼 수 있다.
printAll 메서드에서는 List와 Set 모두 Iterable을 상속받고 Iterator를 반환 하기 때문에, 다형성을 활용하여 while문을 통해 데이터를 순회할 수 있게 해주었고, forEachPrintAll 메서드는 Iterable이 제공하는 foreach 문을 사용할 수 있기 때문에 예제 코드와 같은 방식으로 사용할 수 있다.
이렇게 했을 경우, 어떠한 자료구조가 들어와도 해당 함수를 통해 데이터에 접근할 수 있게 된 것이다.
정리
List,Set,Map(entrySet()) 데이터 접근 방식이 다르지만, Iterable를 상속받고 있기 때문에 일관성 있게 향상된 for문으로 자신의 데이터를 순회할 수 있다.
'Java > Collection' 카테고리의 다른 글
| [Collection] Comparable, Comparator 알아보기 (0) | 2024.06.04 |
|---|---|
| [Collection] Java의 Map (0) | 2024.05.29 |
| [Collection] Java의 Set과 hashCode() (0) | 2024.05.29 |
| [Collection] Java의 TreeSet의 특징 및 구조 (0) | 2024.05.29 |
| [Collection] 자바의List 그리고 ArrayList,LinkedList 실제 성능 테스트 (0) | 2024.05.24 |