소개
Java 프로그래밍에서 다양한 데이터 구조를 다루는 것은 효율적인 프로그램 개발에 필수적입니다. 배열과 리스트는 서로 다른 목적을 수행하는 두 가지 기본적인 데이터 구조입니다. 배열은 고정 크기 스토리지를 제공하는 반면, 리스트는 동적 크기 조정과 추가 유틸리티 메서드를 통해 유연성을 제공합니다.
이 튜토리얼은 원래 배열 데이터를 수정하지 않고 Java 배열을 가변 리스트로 변환하는 데 중점을 둡니다. 이 Lab 을 마치면 배열과 리스트 간의 관계를 이해하고 데이터 무결성을 유지하면서 두 구조 간을 변환하는 실용적인 기술을 배우게 됩니다.
Java 배열과 리스트 이해하기
이 단계에서는 Java 배열과 리스트의 기본 차이점을 예제를 통해 살펴봅니다. 또한, 요소에 접근하고 표시하는 방법도 살펴봅니다.
Java 프로젝트 생성하기
간단한 Java 파일을 생성하여 시작해 보겠습니다.
터미널을 열고 프로젝트 디렉토리로 이동합니다.
cd ~/projectJava 파일용 새 디렉토리를 생성합니다.
mkdir -p src/main/java cd src/main/javaWebIDE 를 사용하여
ArrayListDemo.java라는 새 Java 파일을 생성합니다. WebIDE 에서 탐색기 아이콘을 클릭하고project/src/main/java로 이동하여 마우스 오른쪽 버튼을 클릭하고 "New File"을 선택합니다. 이름을ArrayListDemo.java로 지정합니다.파일에 다음 코드를 추가합니다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo {
public static void main(String[] args) {
System.out.println("Java Arrays vs Lists Example");
System.out.println("============================");
// Creating an array of integers
int[] numbersArray = {1, 2, 3, 4, 5};
// Displaying array contents
System.out.println("Array contents: " + Arrays.toString(numbersArray));
// Accessing array elements by index
System.out.println("Array element at index 2: " + numbersArray[2]);
// Creating a List using ArrayList
List<Integer> numbersList = new ArrayList<>();
numbersList.add(1);
numbersList.add(2);
numbersList.add(3);
numbersList.add(4);
numbersList.add(5);
// Displaying list contents
System.out.println("List contents: " + numbersList);
// Accessing list elements by index
System.out.println("List element at index 2: " + numbersList.get(2));
// Demonstrating List flexibility - adding a new element
numbersList.add(6);
System.out.println("List after adding element: " + numbersList);
// Demonstrating List flexibility - removing an element
numbersList.remove(1); // Removes element at index 1
System.out.println("List after removing element at index 1: " + numbersList);
}
}
- Java 프로그램을 컴파일하고 실행합니다.
cd ~/project javac src/main/java/ArrayListDemo.java java -cp src/main/java ArrayListDemo
다음과 유사한 출력을 볼 수 있습니다.
Java Arrays vs Lists Example
============================
Array contents: [1, 2, 3, 4, 5]
Array element at index 2: 3
List contents: [1, 2, 3, 4, 5]
List element at index 2: 3
List after adding element: [1, 2, 3, 4, 5, 6]
List after removing element at index 1: [1, 3, 4, 5, 6]
배열과 리스트의 주요 차이점
크기 유연성:
- 배열은 생성 후 변경할 수 없는 고정된 크기를 갖습니다.
- 리스트는 필요에 따라 동적으로 늘리거나 줄일 수 있습니다.
사용 가능한 연산:
- 배열은 제한된 내장 기능을 갖습니다.
- 리스트는 요소를 추가, 제거 및 조작하기 위한 다양한 메서드를 제공합니다.
유형 제약 조건:
- 배열은 기본형 (primitive) 또는 객체를 저장할 수 있습니다.
- 리스트는 객체만 저장할 수 있습니다 (하지만 오토박싱 (autoboxing) 을 통해 간접적으로 기본형을 저장할 수 있습니다).
이러한 차이점을 이해하면 특정 요구 사항에 맞는 올바른 데이터 구조를 선택하는 데 도움이 됩니다.
배열을 리스트로 변환하기
이제 배열과 리스트의 기본 사항을 이해했으므로 배열을 리스트로 변환하는 다양한 방법을 살펴보겠습니다. 특히 배열에서 가변 리스트를 생성하는 데 중점을 둡니다.
새 Java 파일 생성하기
동일한 디렉토리에
ArrayToListConversion.java라는 새 Java 파일을 생성합니다.WebIDE 에서
project/src/main/java로 이동하여 마우스 오른쪽 버튼을 클릭하고 "New File"을 선택합니다. 이름을ArrayToListConversion.java로 지정합니다.파일에 다음 코드를 추가합니다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ArrayToListConversion {
public static void main(String[] args) {
System.out.println("Converting Arrays to Lists");
System.out.println("=========================");
// Create an array of Strings
String[] fruitsArray = {"Apple", "Banana", "Cherry", "Date", "Elderberry"};
System.out.println("Original array: " + Arrays.toString(fruitsArray));
// Method 1: Using Arrays.asList() - Creates a fixed-size list
System.out.println("\nMethod 1: Arrays.asList()");
List<String> fruitsListFixed = Arrays.asList(fruitsArray);
System.out.println("List created with Arrays.asList(): " + fruitsListFixed);
// Try to modify the list
System.out.println("Changing element at index 0 to 'Apricot'");
fruitsListFixed.set(0, "Apricot");
System.out.println("List after change: " + fruitsListFixed);
System.out.println("Original array after List change: " + Arrays.toString(fruitsArray));
// This will cause UnsupportedOperationException
try {
System.out.println("Trying to add a new element to the fixed-size list...");
fruitsListFixed.add("Fig");
} catch (UnsupportedOperationException e) {
System.out.println("Error: Cannot add to fixed-size list!");
}
// Reset the array for next example
fruitsArray[0] = "Apple";
// Method 2: Using new ArrayList<>(Arrays.asList()) - Creates a mutable list
System.out.println("\nMethod 2: new ArrayList<>(Arrays.asList())");
List<String> fruitsList = new ArrayList<>(Arrays.asList(fruitsArray));
System.out.println("List created with new ArrayList<>(Arrays.asList()): " + fruitsList);
// Modify the list
System.out.println("Adding 'Fig' to the list");
fruitsList.add("Fig");
System.out.println("List after adding 'Fig': " + fruitsList);
System.out.println("Original array after List modification: " + Arrays.toString(fruitsArray));
// Method 3: Using Stream API (Java 8+)
System.out.println("\nMethod 3: Using Stream API");
List<String> fruitsListStream = Arrays.stream(fruitsArray)
.collect(Collectors.toList());
System.out.println("List created with Stream API: " + fruitsListStream);
// Modify the list
System.out.println("Adding 'Grape' to the list");
fruitsListStream.add("Grape");
System.out.println("List after adding 'Grape': " + fruitsListStream);
System.out.println("Original array after List modification: " + Arrays.toString(fruitsArray));
}
}
- 프로그램을 컴파일하고 실행합니다.
cd ~/project javac src/main/java/ArrayToListConversion.java java -cp src/main/java ArrayToListConversion
다음과 유사한 출력을 볼 수 있습니다.
Converting Arrays to Lists
=========================
Original array: [Apple, Banana, Cherry, Date, Elderberry]
Method 1: Arrays.asList()
List created with Arrays.asList(): [Apple, Banana, Cherry, Date, Elderberry]
Changing element at index 0 to 'Apricot'
List after change: [Apricot, Banana, Cherry, Date, Elderberry]
Original array after List change: [Apricot, Banana, Cherry, Date, Elderberry]
Trying to add a new element to the fixed-size list...
Error: Cannot add to fixed-size list!
Method 2: new ArrayList<>(Arrays.asList())
List created with new ArrayList<>(Arrays.asList()): [Apple, Banana, Cherry, Date, Elderberry]
Adding 'Fig' to the list
List after adding 'Fig': [Apple, Banana, Cherry, Date, Elderberry, Fig]
Original array after List modification: [Apple, Banana, Cherry, Date, Elderberry]
Method 3: Using Stream API
List created with Stream API: [Apple, Banana, Cherry, Date, Elderberry]
Adding 'Grape' to the list
List after adding 'Grape': [Apple, Banana, Cherry, Date, Elderberry, Grape]
Original array after List modification: [Apple, Banana, Cherry, Date, Elderberry]
다양한 변환 방법 이해하기
Arrays.asList():
- 원래 배열을 기반으로 하는 고정 크기 리스트를 생성합니다.
- 리스트 크기는 변경할 수 없습니다 (요소 추가/제거 불가).
- 리스트 요소에 대한 변경 사항은 원래 배열에 영향을 미칩니다.
new ArrayList<>(Arrays.asList()):
- 배열의 모든 요소를 포함하는 새 ArrayList 를 생성합니다.
- 리스트는 가변적입니다 (요소를 추가/제거할 수 있음).
- 리스트에 대한 변경 사항은 원래 배열에 영향을 미치지 않습니다.
Stream API (Java 8+):
- 함수형 프로그래밍을 사용하는 보다 현대적인 접근 방식입니다.
- 완전히 독립적인 리스트를 생성합니다.
- 변환 중에 변환을 수행할 수 있는 유연성을 제공합니다.
초보자의 경우 두 번째 방법 (new ArrayList<>(Arrays.asList())) 이 일반적으로 가장 유용합니다. 이는 원래 배열에 영향을 미치지 않고 완전히 가변적인 리스트를 생성하기 때문입니다.
기본형 배열 작업하기
이전 단계에서는 참조 유형 (String) 의 배열로 작업했습니다. 그러나 Java 배열은 int, double 등과 같은 기본형도 포함할 수 있습니다. Java 제네릭은 참조 유형에서만 작동하므로 기본형 배열을 리스트로 변환하려면 추가 단계가 필요합니다.
이 프로세스를 보여주기 위해 새 예제를 만들어 보겠습니다.
새 Java 파일 생성하기
동일한 디렉토리에
PrimitiveArrayToList.java라는 새 Java 파일을 생성합니다.WebIDE 에서
project/src/main/java로 이동하여 마우스 오른쪽 버튼을 클릭하고 "New File"을 선택합니다. 이름을PrimitiveArrayToList.java로 지정합니다.파일에 다음 코드를 추가합니다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class PrimitiveArrayToList {
public static void main(String[] args) {
System.out.println("Converting Primitive Arrays to Lists");
System.out.println("===================================");
// Create a primitive int array
int[] numbersArray = {10, 20, 30, 40, 50};
System.out.println("Original primitive array: " + Arrays.toString(numbersArray));
// Method 1: Manual conversion
System.out.println("\nMethod 1: Manual conversion");
List<Integer> numbersList1 = new ArrayList<>();
for (int number : numbersArray) {
numbersList1.add(number); // Autoboxing converts int to Integer
}
System.out.println("List after manual conversion: " + numbersList1);
// Method 2: Using Java 8 Streams
System.out.println("\nMethod 2: Using Java 8 Streams");
List<Integer> numbersList2 = Arrays.stream(numbersArray)
.boxed() // Converts IntStream to Stream<Integer>
.collect(Collectors.toList());
System.out.println("List after stream conversion: " + numbersList2);
// Modify the lists to demonstrate independence from the array
System.out.println("\nModifying the lists:");
numbersList1.add(60);
numbersList1.set(0, 15);
numbersList2.add(70);
numbersList2.remove(0);
System.out.println("List 1 after modifications: " + numbersList1);
System.out.println("List 2 after modifications: " + numbersList2);
System.out.println("Original array after List modifications: " + Arrays.toString(numbersArray));
// Create and convert other primitive type arrays
System.out.println("\nOther primitive type examples:");
double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
List<Double> doubleList = Arrays.stream(doubleArray)
.boxed()
.collect(Collectors.toList());
System.out.println("Double array: " + Arrays.toString(doubleArray));
System.out.println("Double list: " + doubleList);
boolean[] boolArray = {true, false, true, true, false};
List<Boolean> boolList = new ArrayList<>();
for (boolean value : boolArray) {
boolList.add(value);
}
System.out.println("Boolean array: " + Arrays.toString(boolArray));
System.out.println("Boolean list: " + boolList);
}
}
- 프로그램을 컴파일하고 실행합니다.
cd ~/project javac src/main/java/PrimitiveArrayToList.java java -cp src/main/java PrimitiveArrayToList
다음과 유사한 출력을 볼 수 있습니다.
Converting Primitive Arrays to Lists
===================================
Original primitive array: [10, 20, 30, 40, 50]
Method 1: Manual conversion
List after manual conversion: [10, 20, 30, 40, 50]
Method 2: Using Java 8 Streams
List after stream conversion: [10, 20, 30, 40, 50]
Modifying the lists:
List 1 after modifications: [15, 20, 30, 40, 50, 60]
List 2 after modifications: [20, 30, 40, 50, 70]
Original array after List modifications: [10, 20, 30, 40, 50]
Other primitive type examples:
Double array: [1.1, 2.2, 3.3, 4.4, 5.5]
Double list: [1.1, 2.2, 3.3, 4.4, 5.5]
Boolean array: [true, false, true, true, false]
Boolean list: [true, false, true, true, false]
기본형 배열 변환 이해하기
기본형 배열로 작업할 때 두 가지 주요 고려 사항이 있습니다.
오토박싱 (Autoboxing): Java 는 컬렉션에 추가할 때 기본형 값을 해당 래퍼 클래스 객체로 자동 변환합니다. 예를 들어,
int는Integer로 변환됩니다.스트림용 박싱 메서드: 기본형 배열과 함께 스트림을 사용할 때는 기본형 스트림을 객체 스트림으로 변환하기 위해
.boxed()메서드를 호출해야 합니다.
변환 프로세스는 원래 배열과 독립적인 완전히 새로운 리스트를 생성합니다. 즉, 다음을 의미합니다.
- 리스트를 수정해도 원래 배열에는 영향을 미치지 않습니다.
- 리스트는 완전히 가변적입니다 (요소를 추가, 제거 또는 변경할 수 있음).
- 리스트의 각 요소는 배열의 값을 포함하는 새 객체 (래퍼) 입니다.
이러한 독립성은 원래 배열에 대한 변경 위험 없이 데이터를 조작해야 할 때 특히 유용합니다.
실용적인 응용 분야 및 흔한 함정
이제 배열을 가변 리스트로 변환하는 방법을 이해했으므로 몇 가지 실용적인 응용 분야와 흔한 함정을 살펴보겠습니다. 실제 사용 사례와 일반적인 실수를 방지하는 방법을 보여주는 최종 예제를 만들 것입니다.
새 Java 파일 생성하기
동일한 디렉토리에
ArrayListPractical.java라는 새 Java 파일을 생성합니다.WebIDE 에서
project/src/main/java로 이동하여 마우스 오른쪽 버튼을 클릭하고 "New File"을 선택합니다. 이름을ArrayListPractical.java로 지정합니다.파일에 다음 코드를 추가합니다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
public class ArrayListPractical {
public static void main(String[] args) {
System.out.println("Practical Applications and Common Pitfalls");
System.out.println("========================================");
// Use case 1: Creating a mutable list from a configuration array
String[] defaultSettings = {"dark-mode", "auto-save", "notifications"};
System.out.println("Default settings array: " + Arrays.toString(defaultSettings));
// Create a mutable list of user settings starting with defaults
List<String> userSettings = new ArrayList<>(Arrays.asList(defaultSettings));
System.out.println("Initial user settings list: " + userSettings);
// User can add or remove settings
userSettings.add("sync-enabled");
userSettings.remove("notifications");
System.out.println("Modified user settings: " + userSettings);
System.out.println("Original default settings (unchanged): " + Arrays.toString(defaultSettings));
// Use case 2: Working with legacy APIs that return arrays
System.out.println("\nUse case: Working with legacy APIs");
// Simulate a legacy API that returns an array
String[] legacyData = getLegacyData();
System.out.println("Data from legacy API (array): " + Arrays.toString(legacyData));
// Convert to a mutable list for modern processing
List<String> modernData = new ArrayList<>(Arrays.asList(legacyData));
// Process with modern methods
modernData.removeIf(item -> item.startsWith("OLD_"));
modernData.replaceAll(item -> item.toLowerCase());
System.out.println("Processed data (list): " + modernData);
// Common Pitfall 1: Forgetting that Arrays.asList creates a fixed-size list
System.out.println("\nCommon Pitfall 1: Fixed-size list");
Integer[] numberArray = {1, 2, 3, 4, 5};
// This creates a fixed-size list
List<Integer> wrongWay = Arrays.asList(numberArray);
System.out.println("Fixed-size list: " + wrongWay);
try {
wrongWay.add(6); // This will fail
} catch (UnsupportedOperationException e) {
System.out.println("Error: Cannot add to fixed-size list!");
}
// Correct way
List<Integer> rightWay = new ArrayList<>(Arrays.asList(numberArray));
rightWay.add(6); // This works fine
System.out.println("Mutable list: " + rightWay);
// Common Pitfall 2: Array of primitives
System.out.println("\nCommon Pitfall 2: Array of primitives");
int[] primitiveArray = {10, 20, 30};
// This won't compile: Arrays.asList(primitiveArray)
// List<Integer> primitiveList = Arrays.asList(primitiveArray);
// Correct ways
List<Integer> primitiveList1 = new ArrayList<>();
for (int value : primitiveArray) {
primitiveList1.add(value);
}
List<Integer> primitiveList2 = Arrays.stream(primitiveArray)
.boxed()
.toList();
System.out.println("Primitive array: " + Arrays.toString(primitiveArray));
System.out.println("Converted list (loop method): " + primitiveList1);
System.out.println("Converted list (stream method): " + primitiveList2);
}
// Simulate a legacy API that returns an array
private static String[] getLegacyData() {
return new String[] {"OLD_RECORD1", "ACTIVE_DATA", "OLD_RECORD2", "CURRENT_INFO"};
}
}
- 프로그램을 컴파일하고 실행합니다.
cd ~/project javac src/main/java/ArrayListPractical.java java -cp src/main/java ArrayListPractical
다음과 유사한 출력을 볼 수 있습니다.
Practical Applications and Common Pitfalls
========================================
Default settings array: [dark-mode, auto-save, notifications]
Initial user settings list: [dark-mode, auto-save, notifications]
Modified user settings: [dark-mode, auto-save, sync-enabled]
Original default settings (unchanged): [dark-mode, auto-save, notifications]
Use case: Working with legacy APIs
Data from legacy API (array): [OLD_RECORD1, ACTIVE_DATA, OLD_RECORD2, CURRENT_INFO]
Processed data (list): [active_data, current_info]
Common Pitfall 1: Fixed-size list
Fixed-size list: [1, 2, 3, 4, 5]
Error: Cannot add to fixed-size list!
Mutable list: [1, 2, 3, 4, 5, 6]
Common Pitfall 2: Array of primitives
Primitive array: [10, 20, 30]
Converted list (loop method): [10, 20, 30]
Converted list (stream method): [10, 20, 30]
실용적인 응용 분야의 주요 내용
구성 관리: 기본 설정에 배열을 사용하고 사용자별 설정에 리스트를 사용하면 기본값을 유지하면서 유연성을 확보할 수 있습니다.
레거시 API 작업: 많은 이전 Java API 는 배열을 반환하며, 이를 리스트로 변환하여 람다 표현식 및 메서드 참조와 같은 최신 Java 기능을 활용할 수 있습니다.
피해야 할 일반적인 함정:
Arrays.asList()는 크기를 늘리거나 줄일 수 없는 고정 크기 리스트를 생성한다는 점을 기억하십시오.- 기본형 배열은
Arrays.asList()에 직접 전달할 수 없습니다. 루프 또는 박싱이 있는 스트림을 사용해야 합니다.
모범 사례: 대부분의 사용 사례에서 가장 안전한 방법은 객체 배열의 경우
new ArrayList<>(Arrays.asList(array))를 사용하고 기본형 배열의 경우 스트림 메서드를 사용하는 것입니다.
이러한 패턴과 함정을 이해하면 Java 애플리케이션에서 배열과 리스트를 효과적으로 사용하여 각 상황에 맞는 올바른 데이터 구조와 변환 방법을 선택할 수 있습니다.
요약
이 랩에서는 원래 배열 데이터를 보존하면서 Java 배열에서 가변 리스트를 만드는 방법을 배웠습니다. 다음 내용을 살펴보았습니다.
구조, 기능 및 사용 사례를 포함하여 Java 배열과 리스트 간의 근본적인 차이점.
배열을 리스트로 변환하는 여러 가지 방법:
- 배열의 고정 크기 보기를 위해
Arrays.asList()사용 - 완전히 가변적인 리스트를 위해
new ArrayList<>(Arrays.asList())사용 - 기본형 배열에
.boxed()를 사용한 Stream API 사용
- 배열의 고정 크기 보기를 위해
래퍼 객체의 리스트로 변환하기 위해 명시적인 박싱이 필요한 기본형 배열에 대한 특별 고려 사항.
구성 관리 및 레거시 API 와의 상호 작용을 포함하여 배열 및 리스트로 작업할 때의 실용적인 응용 분야 및 일반적인 함정.
이러한 기술은 배열과 리스트를 모두 사용해야 하는 많은 실제 Java 애플리케이션에서 유용하며, 특히 조작을 위해 유연하고 가변적인 컬렉션을 만들면서 원래 데이터를 보존해야 할 때 유용합니다. 배열과 리스트 간의 관계를 이해하면 보다 효율적이고 유지 관리 가능한 Java 코드를 작성할 수 있습니다.



