Java List 초기화 가이드: 모범 사례, 예제 및 성능 팁

1. 소개

Java 프로그래밍을 할 때, “List 초기화”는 가장 기본적이고 중요한 개념 중 하나입니다. 배열과 달리 List는 동적 크기 조절이 가능하고 ArrayList, LinkedList와 같은 다양한 구현체를 지원하여 일상적인 개발 작업에서 자주 사용됩니다. 하지만 많은 개발자들이 “어떤 초기화 방법을 사용해야 할까?” 혹은 “각 접근 방식의 차이점은 무엇인가?”와 같은 질문에 어려움을 겪습니다.
이 글에서는 Java List의 핵심 특성, 초기화의 목적, 그리고 초보자에게 특히 유용한 다양한 초기화 방법을 명확히 설명합니다. 실제 프로젝트에서 흔히 쓰이는 실용적인 예시, 자주 발생하는 함정, 그리고 사용 사례에 따라 올바른 방법을 선택하는 방법까지 다룹니다. List를 최적의 방법으로 초기화하고 경쟁 기사보다 한 발 앞서고 싶다면, 이 가이드는 큰 도움이 될 것입니다.

2. List의 기본 개념과 초기화의 중요성

Java에서 List는 순서가 보장되고 가변 길이 데이터를 저장할 수 있는 컬렉션 타입입니다. 가장 많이 사용되는 구현체는 ArrayList이지만, LinkedList, Vector 등 여러 구현체가 존재합니다. 배열에 비해 List는 유연한 크기 조절과 add()·remove()와 같은 간단한 연산을 제공합니다.

【Features of Lists】

  • Order is preserved : 요소는 삽입된 순서를 유지합니다.
  • Duplicates are allowed : 동일한 값이 여러 번 저장될 수 있습니다.
  • Dynamic size : 미리 크기를 지정할 필요 없이 자유롭게 요소를 추가·제거할 수 있습니다.

하지만 초기화 방법마다 동작이 다르기 때문에 목표에 맞는 방식을 선택하는 것이 중요합니다.

【Why initialization matters】
올바른 List 초기화 기술을 선택하는 것은 사용 사례에 크게 좌우됩니다. 예를 들어:
– 빈 List를 만들 때는 간단한 방법이 필요합니다.
– 초기값이 있는 List를 만들 때는 다른 접근이 필요합니다.
– 불변 List가 필요하다면 특정 방법이 더 적합합니다.

또한 최신 Java 버전에서는 효율성을 높인 새로운 문법을 제공하고 있습니다. 이러한 옵션을 이해하면 생산성이 크게 향상됩니다.

【Difference between Lists and arrays】
Java의 배열은 고정 길이이며 선언 시 크기를 지정해야 합니다. 반면 List는 동적 크기 조절이 가능해 실무에서 선호됩니다. 하지만 초기화 기법과 내부 구현에 따라 성능 차이나 기능 제한이 발생할 수 있으므로 올바른 사용이 중요합니다.

3. List를 초기화하는 다섯 가지 방법

Java에서는 List를 초기화하는 여러 방법을 제공합니다. 최적의 방법은 사용 사례, Java 버전, 그리고 이후에 요소를 추가·제거할지 여부에 따라 달라집니다. 이 섹션에서는 가장 많이 쓰이는 다섯 가지 초기화 기법을 특성 및 권장 사용 상황과 함께 설명합니다.

3.1 빈 List 만들기

가장 기본적인 초기화 방식으로, 빈 List를 만든 뒤 나중에 값을 추가하고자 할 때 사용합니다.

List<String> list = new ArrayList<>();
  • Use case : 나중에 요소를 추가할 때.
  • Key point : 초기에는 비어 있지만 add()를 통해 자유롭게 요소를 추가할 수 있습니다. 필요에 따라 LinkedList 등 다른 구현체를 선택할 수도 있습니다.

3.2 Arrays.asList 사용하기

여러 값이나 배열로부터 빠르게 List를 만들고 싶을 때 Arrays.asList()가 편리합니다. 한 줄로 초기값이 포함된 List를 생성할 수 있습니다.

List<String> list = Arrays.asList("A", "B", "C");
  • Use case : 고정된 여러 값을 가지고 List를 만들 때.
  • Note : 이 방법으로 만든 List는 고정 크기이므로 add()·remove()로 요소를 추가하거나 제거할 수 없습니다. 다만 set()을 통해 기존 값을 수정할 수 있습니다.
  • If modification is required : 다음과 같이 새로운 ArrayList를 생성합니다.
    List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
    

3.3 List.of 사용 (Java 9 이상)

Java 9부터 List.of()를 사용하면 불변 리스트를 손쉽게 만들 수 있습니다.

List<String> list = List.of("A", "B", "C");
  • 사용 사례 : 내용이 고정되어 변경될 필요가 없을 때.
  • 특징 : 이 메서드로 만든 리스트는 완전 불변입니다. add(), remove(), set() 등 모든 수정이 허용되지 않습니다. 상수 및 안전이 중요한 데이터에 이상적입니다.

3.4 인스턴스 이니셜라이저 사용

덜 일반적인 이 기법은 익명 클래스 내부에 인스턴스 이니셜라이저를 사용합니다. 하나의 표현식으로 여러 줄 또는 복잡한 초기화를 할 수 있습니다.

List<String> list = new ArrayList<>() {{
    add("A");
    add("B");
    add("C");
}};
  • 사용 사례 : 많은 요소나 복잡한 초기화 로직이 필요할 때.
  • 주의 : 익명 클래스를 생성하므로 메모리 및 유지보수 측면에서 대규모 프로젝트나 성능이 중요한 환경에는 권장되지 않습니다.

3.5 초기 용량을 지정한 ArrayList 생성

List<String> list = new ArrayList<>(100);
  • 사용 사례 : 많은 요소를 삽입할 것으로 예상되고 대략적인 크기를 이미 알고 있을 때.
  • 이점 : 내부 리사이징 작업을 줄여 성능을 향상시킵니다.

이와 같은 다양한 초기화 방법을 통해 Java 개발자는 상황에 맞는 가장 효율적인 방식을 선택할 수 있습니다.

4. 각 초기화 방법 비교

이 섹션에서는 앞서 소개한 다섯 가지 초기화 기법을 비교합니다. 정리된 개요를 통해 어떤 방법을 사용해야 할지 판단하는 데 도움이 됩니다.
【주요 비교 포인트】

Initialization MethodAdd/RemoveModify ValuesImmutabilityRecommended Use Case
new ArrayList<>()YesYesNoGeneral List operations
Arrays.asList(…)NoYesPartial (fixed size)When converting an array to a List and only modifying existing values
new ArrayList<>(Arrays.asList(…))YesYesNoWhen you need both initial values and modifiable size
List.of(…)NoNoExcellentWhen a fully immutable constant List is required
Instance initializerYesYesNoWhen initializing complex or multi-line values at once
new ArrayList<>(initial capacity)YesYesNoWhen handling many elements and optimizing performance

【핵심 선택 가이드라인】

  • 추후에 요소를 추가하거나 제거해야 할 경우new ArrayList<>() 또는 new ArrayList<>(Arrays.asList(...))
  • 고정된 값으로 리스트를 만들고 추가/제거를 원하지 않을 경우Arrays.asList(...)
  • 완전 불변 리스트가 필요할 경우(안전이 중요한 경우)List.of(...) (Java 9+)
  • 다중 라인 또는 복잡한 초기화 로직이 필요할 경우 ⇒ 인스턴스 이니셜라이저
  • 많은 요소가 예상되고 성능을 개선하고 싶을 경우new ArrayList<>(initial capacity)

【참고】

  • Arrays.asList로 만든 리스트는 고정 크기이며, 요소를 추가하거나 제거하면 UnsupportedOperationException이 발생합니다.
  • List.of는 0개 또는 다수의 요소를 지원하지만 불변이며, add, remove, set을 사용할 수 없습니다.
  • 인스턴스 이니셜라이저는 강력하지만 익명 클래스를 생성하므로 가독성과 성능에 영향을 줄 수 있습니다.

용도, 안전성, 성능을 고려해 적절한 초기화 방법을 선택하는 것이 효과적인 Java 개발에 필수적입니다.

5. 실용 사용 예시

이 섹션에서는 앞서 소개한 각 List 초기화 방법에 대한 실용적인 예시를 제공합니다. 구체적인 상황을 검토함으로써 어떤 방법이 자신의 사용 사례에 적합한지 더 잘 이해할 수 있습니다.

5.1 빈 리스트 생성 후 나중에 값 추가

List<String> names = new ArrayList<>();
names.add("Satou");
names.add("Suzuki");
names.add("Takahashi");
System.out.println(names); // Output: [Satou, Suzuki, Takahashi]

설명:
가장 기본적인 사용법입니다. 사용자 입력이나 반복문 등으로 나중에 값을 추가하기 위해 미리 빈 List를 준비하고자 할 때 유용합니다.

5.2 Arrays.asList 로 고정 크기 리스트 생성

List<String> fruits = Arrays.asList("Apple", "Banana", "Mikan");
System.out.println(fruits); // Output: [Apple, Banana, Mikan]
// fruits.add("Grape"); // ← This will cause an error
fruits.set(0, "Pineapple"); // This is allowed
System.out.println(fruits); // Output: [Pineapple, Banana, Mikan]

설명:
고정된 데이터셋을 다루거나 값을 즉시 처리하고자 할 때 유용한 방법입니다. 다만 요소를 추가하거나 제거할 수 없으므로 주의가 필요합니다.

5.3 List.of (Java 9+) 로 불변 리스트 생성

List<String> colors = List.of("Red", "Blue", "Green");
System.out.println(colors); // Output: [Red, Blue, Green]
// colors.add("Yellow"); // ← Will throw an exception

설명:
이것은 상수 목록이나 수정해서는 안 되는 값에 이상적이며, 특히 안전성과 불변성이 중요한 경우에 적합합니다.

5.4 인스턴스 초기화자를 사용한 복합 초기값 설정

List<Integer> numbers = new ArrayList<>() {{
    for (int i = 1; i <= 5; i++) {
        add(i * i); // 1, 4, 9, 16, 25
    }
}};
System.out.println(numbers); // Output: [1, 4, 9, 16, 25]

설명:
루프, 조건문 또는 복잡한 로직이 필요한 초기화에 유용합니다. 강력하지만 익명 클래스 오버헤드 때문에 대규모 시스템에서는 권장되지 않습니다.

5.5 초기 용량을 지정하여 대량 데이터 추가

List<Integer> bigList = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
    bigList.add(i);
}
System.out.println(bigList.size()); // Output: 1000

설명:
대용량 데이터 세트를 처리할 때 초기 용량을 지정하면 리사이징 작업이 감소하고 성능이 향상됩니다. 실제 시나리오에 맞는 초기화 방법을 선택하면 가독성, 성능 및 유지보수성이 개선됩니다.

6. 요약

이 글에서는 Java에서 List를 초기화하는 다양한 접근 방식을 살펴보았습니다—기본 개념과 실용 예제부터 비교 및 모범 사례까지. List 초기화는 겉보기엔 간단해 보이지만, 최적의 방법은 사용 사례와 요구 사항에 따라 크게 달라집니다.
핵심 포인트 요약:

  • List는 순서가 보장되고 중복을 허용하며 동적 크기 조절이 가능해 배열보다 유연합니다.
  • Java는 다양한 초기화 방법을 제공합니다: 빈 List, 초기값이 있는 List, 불변 List, 지정된 초기 용량을 가진 List 등.
  • 올바른 방법을 선택하려면 요소를 추가/제거해야 하는지, 고정 데이터를 다루는지, 불변성을 보장해야 하는지, 대용량 데이터를 효율적으로 관리해야 하는지를 고려해야 합니다.
  • Arrays.asListList.of는 각각 고정 크기와 완전 불변이라는 특정 제한이 있으므로, 그 동작을 정확히 이해하는 것이 중요합니다.

실제 개발이나 학습 중 List 초기화를 마주할 때, 이 가이드를 참고해 가장 적합한 방법을 선택하시기 바랍니다. 이 글이 더 안전하고 효율적인 Java 코드를 작성하는 데 도움이 되길 바랍니다.

7. 자주 묻는 질문 (FAQ)

Q1: Arrays.asList 로 만든 List에 요소를 추가할 수 있나요?
A1: 아니요, 추가할 수 없습니다. Arrays.asList 로 만든 List는 고정 크기를 가지므로 add()remove() 를 호출하면 UnsupportedOperationException 이 발생합니다.
하지만 set() 을 사용해 기존 값을 덮어쓸 수는 있습니다.
수정 가능한 List가 필요하다면 다음과 같이 변환하세요:
new ArrayList<>(Arrays.asList(...))

Q2: List.ofArrays.asList 의 차이점은 무엇인가요?
A2:

  • List.of (Java 9 이상)는 완전 불변 List를 생성합니다. set() 조차도 허용되지 않습니다.
  • Arrays.asList고정 크기 List를 생성합니다. 요소를 추가하거나 제거할 수는 없지만 set() 으로 값을 덮어쓸 수 있습니다.

두 경우 모두 add()remove() 가 금지되지만, List.of 가 더 강력한 불변성을 제공합니다.
안전성과 불변성이 우선이라면 List.of 를 권장합니다.

Q3: List를 초기화할 때 초기 용량을 지정하면 어떤 이점이 있나요?
A3:
ArrayList 를 사용할 때 초기 용량을 지정하면 많은 요소를 추가할 것으로 예상될 경우 내부 배열의 리사이징 작업을 줄여 성능을 향상시킵니다.
미리 대략적인 요소 수를 알고 있다면 초기 용량을 설정해 불필요한 메모리 재할당을 방지할 수 있습니다.

Q4: 초기화에 인스턴스 초기화자를 사용할 때 주의해야 할 점은 무엇인가요?
A4:
인스턴스 초기화자는 내부적으로 익명 클래스를 생성합니다. 이로 인해 다음과 같은 문제가 발생할 수 있습니다:

  • 메모리 사용량 증가
  • 유지보수성 저하
  • 직렬화 시 잠재적인 문제

따라서 사용 시 이러한 단점을 충분히 고려해야 합니다.

짧고 복잡한 초기화에는 편리하지만, 대규모 또는 성능에 민감한 환경에는 이상적이지 않습니다.

Q5: LinkedList를 어떻게 초기화해야 하나요?
A5:
LinkedList 초기화는 ArrayList와 유사하게 작동합니다. 예를 들어:
List<String> list = new LinkedList<>();

다른 방법으로도 LinkedList를 초기화할 수 있습니다:

  • new LinkedList<>(existingList)
  • Arrays.asList 또는 List.of를 사용하고 변환을 결합하는 방법

이 FAQ 섹션이 여러분의 질문에 답하는 데 도움이 되길 바랍니다.
List 초기화를 이해하면 더 깔끔하고 안전하며 효율적인 Java 개발을 지원할 수 있습니다.