https://dev-dx2d2y-log.tistory.com/141
[CS] 자바의 정석 독서 #22 - Arrays로 배열 다루기
어쩌다보니 프로그래밍 언어와 CS 지식도 쓰리트랙으로 나눠서 배우게 되었다. 자바, 자바스크립트, C 계열언어.. 특히 자바는 자바의 정석과 JVM 구동방식을 나눠서 배우다보니 정작 자바에 쏟을
dev-dx2d2y-log.tistory.com
저번에 Arryas 클래스에 대해서 다뤘다. 배열에 대해서 몇 가지 메서드를 지원하는데,

위와같이 '배열'에만 적용시킬 수 있기 때문에 컬렉션에서는 적용시킬 수 없다고 나와있다.
그래서 컬렉션에 적용시킬 수 있는 몇 가지 기능들이 바로 Collections.
컬렉션 기능1 - 동기화
(※동기화는 멀티스레드 환경에서 공유되는 객체에 대해서 주기적인 정보 업데이트가 필요하는데, 이를 동기화라고 칭한다. 에 대해서는 뒷장에서 다룸)
컬렉션이 추가된 JDK1.2 이전에 등장한 클래스들 (Vector, Hashtable 등)은 자체적인 동기화처리가 되어있는 반면, 컬렉션 프레임웍과 함께 추가된 List, Set, Map들은 자체적인 동기화 기능을 제공하지 않는다.
List<Integer> l = new ArrayList<>();
List<Integer> l2 = Collections.synchronizedList(new ArrayList<>());
List<Integer> l3 = Collections.synchronizedList(l);
Collections 클래스에서는 이렇게 동기화를 통한 컬렉션 프레임웍을 생성하는 코드를 제공한다. List가 아닌 Set이나 Map이 필요하다면 List 부분을 원하는 자료구조 형태로 바꿔 사용하면 된다.
컬렉션 기능2 - 불변컬렉션 만들기
방어적 복사 등을 수행할 때, 보안 취약점을 막기 위해 컬렉션을 절대 불변으로 만들 필요가 있다. 이러한 점은 이펙티브 자바를 읽다보면 주로 나온다.
배열과 내부적으로 배열을 사용하는 컬렉션은 final 키워드를 사용하더라도 가끔식 변경이 되는 경우가 있다.

파트스터디에서 한 번 다룬 적이 있어서 가져왔는데. 외부에서 배열에 접근한다면 배열이 final로 선언되더라도 배열값이 변경될 수 있다.

가장 기본적인 해결책은 배열을 private final로 설정하여 완전불변으로 설정하는 것인데, 이러면 public 으로 접근해야하는 배열에 접근하지 못할 수 있기 때문에 방어적 복사를 수행하는 것이 좋다.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static final List<Integer> ints = new ArrayList<Integer>();
public static void main(String[] args) {
for (int i = 0; i < ints.size(); i++) {
ints.add(i);
}
ttt ttt = new ttt();
ttt.medify();
}
}
class ttt {
Main stack = new Main();
public void medify() {
Integer hi = stack.ints.get(1);
System.out.println("원래값 : " + hi);
stack.ints.set(1,3);
System.out.println("변경된값 : " + stack.ints.get(1));
}
}

컬렉션도 크게 다르지 않다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
private static final List<Integer> ints = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
ints.add(i);
}
ttt ttt = new ttt();
ttt.medify();
}
public List<Integer> getInts() {
return Collections.unmodifiableList(ints);
}
}
class ttt {
Main stack = new Main();
public List<Integer> ints = stack.getInts();
public void medify() {
Integer hi = ints.get(1);
System.out.println("원래값 : " + hi);
ints.set(1,3);
System.out.println("변경된값 : " + ints.get(1));
}
}
방어적복사를 통해 불변배열을 반환할 때 사용하기 좋다. 외부에서 컬렉션을 변경할 수 없는데다가 public 키워드를 사용하기 때문에 외부에서 자유롭게 접근이 가능하다.
List<Integer> l = Collections.unmodifiableList(Lists list);
가장 기본적인 사용법은 이렇고, 원하는 컬렉션 자료구조에 따라 알맞게 바꿔 사용하면된다.
컬렉션 기능 3 - 한 종류의 객체만 저장하도록하기
원래 컬렉션을 사용할 때 제너릭을 사용하는데, 제너릭의 꺽쇠괄호에 아무것도 넣어주지 않으면 모든 객체를 컬렉션에 저장가능하다. 이를 방지하고 싶다면
List<> l1 = new ArrayList<>();
List<> list = Collections.checkedList(l1, Integer.class);
list.add(1);
list.add("ab"); //여기서 에러
위와같이 Collections.checkedList()를 사용하면 된다. checkList 메서드를 통해 만들어진 메서드들은 주어진 타입 이외의 타입이 추가되면 에러를 발생시킨다.
사실 이런 기능은 거의 제너릭이 담당하기 때문에 필요가 없지만, 제너릭이 등장하기 이전 JDK5버전에서 작성된 컬렉션들은 이 메서드를 이용해야한다. 일종의 하위호환성.
컬렉션 기능 4 - 싱글톤 컬렉션 만들기
디자인 패턴에서 싱글톤이란 클래스 인스턴스를 단 한 개만 생성한다는 의미지만, 싱글톤 컬렉션은 요소가 단 한 개 뿐인 컬렉션을 싱글톤 컬렉션이라 한다.
Integer i = 1;
List<Integer> list = Collections.singletonList(i);
이처럼 사용할 수 있다. 매개변수는 Object 타입을 받기 때문에 아무거나 넣어주어도 된다.
알아두어야할 것은 Map의 경우에는 key와 value를 모두 넣어주어야한다는 것이고,
Integer i = 1;
Set<Integer> s = Collections.singleton(i);
Set의 경우에는 Collections.singleton() 메서드를 사용해야한다.
매개변수로 저장할 요소를 넘겨주면 그 컬렉션은 불변이 된다.
컬렉션 기능 5 - 빈 컬렉션 만들기
빈 컬렉션을 사용해야할 때, null로 사용해도 되지만 NPE를 걱정해야할 때가 있다. 매번 NPE 테스트를 하기엔 코드도 좀 늘어나고
List<Integer> list = Collections.EMPTY_LIST;
List<Integer> l2 = Collections.EMPTY_LIST;
list.add(3);
Collections.EMPTY_LIST;를 사용하면 된다.
public static final List EMPTY_LIST = new EmptyList<>();
EMPTY_LIST는 Collections에 선언된 인스턴스 변수로, 불변 클래스이다. 그래서 저렇게 빈 컬렉션을 만들고 값을 수정하려하면 에러가 발생한다.
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
빈 배열을 반환하는 메서드도 있는데, 어차피 반환되는 것은 EMPTY_LIST이기 때문에 값 변경은 불가능하다.
기타 메서드들
이외에도 컬렉션이 기본으로 제공하는 get, sort 등의 메서드도 지원하고, binarySearch 등의 메서드도 지원한다.
//역순정렬
public static void reverse(List<?> list)
//저장된 요소의 위치를 임의로 변경
public static void shuffle(List<?> list)
//저장된 요소의 인덱스 위치를 서로 변경
public static void swap(List<?> list, int i, int j)
//리스트를 obj로 채우기
public static <T> void fill(List<? super T> list, T obj)
//리스트 복사. 매개변수로 복사할 리스트와 복사당할(?) 리스트를 넣는다.
public static <T> void copy(List<? super T> dest, List<? extends T> src)
//최소값 반환
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
//최대값 반환
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
//distance만큼 오른족으로 리스트 이동
public static void rotate(List<?> list, int distance)
등이 있다. 솔직히 이렇게 적어놔봐야 외워서 쓰지도 못하고 필요할 때마다 알아두는 것이 좋을 것이다.
어쨌든 이렇게 컬렉션에 대해서 여러 가지 기능을 지원하는 클래스 Collections에 대해서 알아보았다.
여러가지 메서드들이 있으므로 그때그때 찾아가며 확인하는 것이 좋을 것이다.
'언어공부 > Java | Kotlin' 카테고리의 다른 글
| [Java] 자바의 정석 독서 #27 - 제너릭 기초 (1) | 2025.12.18 |
|---|---|
| [Java] 자바의정석 독서 #26 - 컬렉션 마무리하기 (0) | 2025.12.04 |
| [Java] 자바의 정석 독서 #24 - 해싱과 해싱함수 (0) | 2025.12.03 |
| [Java] 자바의 정석 독서 #23 - HashSet과 HashMap (0) | 2025.12.01 |
| [CS] 자바의 정석 독서 #22 - Arrays로 배열 다루기 (0) | 2025.11.28 |
