강의 영상중에 List.of() 를 사용하는 구간이 있었는데 처음보는 메서드라서 정리할 겸 글을 쓴다.
List 는 배열의 한계 때문에 만들어진 자료형으로서 컬렉션 프레임워크의 주요 인터페이스 중 하나이다. 배열을 사용하기 위해서는 먼저 크기를 정해야 한다. 그런데 크기를 처음부터 알 수 없다면 어떨까? List 는 크기 지정 없이 객체를 생성할 수 있으며, 메모리가 허용하는 한 계속해서 추가할 수 있도록 만들어져 있다.
List 를 만들 때 우리는 보통 ArrayList 로 객체를 생성한다. 하지만 이 방법 말고도 Arrays.asList() 나 List.of() 메서드로도 생성할 수 있다. 일단 각 메서드가 속한 클래스는 아래와 같다. List.of() 는 Java 9 부터 도입된 메서드이다.
import java.util.ArrayList; // new ArrayList<>()
import java.util.Arrays; // Arrays.asList()
import java.util.List; // List.of()
Arrays.asList() 와 List.of() 의 차이점은 무엇일까? 표로 보면 아래와 같다.
원소 추가(add) / 삭제(remove) 여부 | 변경 (set) 여부 | null 허용 여부 | |
Arrays.asList() | X | O | O |
List.of() | X | X | X |
ArrayList 클래스를 들여다보면 AbstractList 클래스를 상속한 형태로 되어 있고, 이 추상클래스의 메서드를 오버라이딩한 목록이 있다. 그 중에 add() 와 remove() 는 오버라이딩을 하지 않고, set() 은 오버라이딩을 한 걸 확인할 수 있다. 부모 클래스인 AbstractList의 add()와 remove() 메서드를 확인해보면, 자식 클래스에서 해당 메서드를 오버라이딩 하지 않으면 UnsupportedOperationException 예외가 발생된다. 하지만 ArrayList 클래스에서는 추가 및 삭제 메서드를 오버라이딩하지 않았기 때문에 이 메서드는 원소를 추가 및 삭제하는 것은 불가능하지만 내부 원소들의 값을 변경하는 건 허용한다.
그리고 Arrays.asList() 는 배열을 그대로 반환한다는 개념으로, 배열의 변경사항이 있으면 그 변경 사항이 List 에도 반영이 된다. 하지만 List.of 는 그렇지 않다.
List.of() 메서드는 배열을 복사한 새로운 복사본을 만든다는 개념으로, 배열의 변경사항이 있어도 List.of() 로 만든 List 와는 아무런 상관이 없어진다. 그 이유는, List.of() 에는 인자값을 하나 또는 두 개를 받는 메서드와 세 개 이상을 받는 메서드로 나뉘어져 있는데, 이 메서드들은 AbstractImmutableList 라는 추상 클래스를 상속 받았다. 그래서 List.of() 메서드에 리턴되는 List 는 불변하다. 또한 생성자 인자로 들어오는 값들을 하나씩 null 값이 오는 걸 체크하고, null 값이 오면 NPE 예외를 발생시킨다.
//배열의 변경과 List 의 변경의 상관관계
Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // [1, 10, 3] 값이 변함
Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // [1, 2, 3] 값이 변하지 않음
//set 사용 가능 여부
List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK
List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException
//null 허용 여부
List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException
//List.of() 는 null 이 들어있는지를 체크하는 것 조차 허용하지 않는다.
List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false
List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException
마지막으로 다시 한번 목록으로 정리해보자면 이렇다.
Arrays.asList()
- add(), remove() 는 불가능하다.
- set() 은 가능하다.
- null 도 허용한다.
- '반환'의 개념으로서, 참조한 원본 배열의 값이 바뀌면 List 의 값도 바뀌고, 이 List 의 값이 바뀌면 원본 배열의 값도 바뀐다.
List.of()
- add(), remove(), set() 은 불가능하다.
- null 을 허용하지 않는다.
- '새로운 복사본' 의 개념으로서, 참조한 원본 배열의 값이 바뀌어도 List 의 값은 바뀌지 않는다.
테스트 코드를 작성할 때 배열의 size 가 변하면 안 되거나, 변할 필요가 없을 때 List.of() 를 사용한다. 하지만 null 값을 테스트 해야하는 경우에는 Arrays.asList() 를 사용한다.
'TIL' 카테고리의 다른 글
코딩도장 시간 활용하기 (22.09.13 TIL) (1) | 2022.09.13 |
---|---|
작은 성공 만들기 (22.09.12 TIL) (0) | 2022.09.12 |
약간의 뽀모도로를 곁들인 (22.09.10 TIL) (0) | 2022.09.10 |
추상화 레벨이란? (22.09.09 TIL) (0) | 2022.09.09 |
JUnit5 살펴보기 (22.09.08 TIL) (0) | 2022.09.08 |
댓글