Введение
В программировании на Java работа с различными структурами данных имеет важное значение для эффективной разработки программ. Массивы (Arrays) и списки (Lists) — две фундаментальные структуры данных, служащие разным целям. В то время как массивы обеспечивают хранение фиксированного размера, списки предлагают гибкость с динамическим изменением размера и дополнительными служебными методами.
Этот учебник посвящен преобразованию массивов Java в изменяемые списки без изменения исходных данных массива. К концу этой лабораторной работы вы поймете взаимосвязь между массивами и списками и изучите практические методы преобразования между ними, сохраняя целостность данных.
Понимание массивов и списков Java
На этом шаге мы рассмотрим основные различия между массивами и списками Java, создав примеры обоих. Мы также рассмотрим, как получить доступ к их элементам и отобразить их.
Создание Java-проекта
Давайте начнем с создания простого Java-файла для работы:
Откройте терминал и перейдите в каталог проекта:
cd ~/projectСоздайте новую директорию для наших Java-файлов:
mkdir -p src/main/java cd src/main/javaСоздайте новый Java-файл с именем
ArrayListDemo.javaс помощью WebIDE. Щелкните значок Explorer в 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]
Основные различия между массивами и списками
Гибкость размера:
- Массивы имеют фиксированный размер, который нельзя изменить после создания.
- Списки могут динамически расти или сжиматься по мере необходимости.
Доступные операции:
- Массивы имеют ограниченную встроенную функциональность.
- Списки предоставляют множество методов для добавления, удаления и манипулирования элементами.
Ограничения по типу:
- Массивы могут хранить примитивы или объекты.
- Списки могут хранить только объекты (но автоупаковка позволяет косвенно хранить примитивы).
Понимание этих различий поможет вам выбрать правильную структуру данных для ваших конкретных нужд.
Преобразование массивов в списки
Теперь, когда мы понимаем основы массивов и списков, давайте рассмотрим различные способы преобразования массива в список. Мы сосредоточимся, в частности, на создании изменяемых списков из массивов.
Создание нового Java-файла
Создайте новый Java-файл с именем
ArrayToListConversion.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-файла
Создайте новый Java-файл с именем
PrimitiveArrayToList.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.Boxing Methods for Streams (Методы упаковки для потоков): При использовании потоков с массивами примитивных типов необходимо вызвать метод
.boxed(), чтобы преобразовать потоки примитивных типов в потоки объектов.
Процесс преобразования создает совершенно новые списки, которые не зависят от исходных массивов. Это означает:
- Изменение списка не повлияет на исходный массив.
- Списки полностью изменяемы (вы можете добавлять, удалять или изменять элементы).
- Каждый элемент в списке является новым объектом (оберткой), который содержит значение из массива.
Эта независимость особенно полезна, когда вам нужно манипулировать данными, не рискуя внести изменения в исходный массив.
Практическое применение и распространенные ошибки
Теперь, когда мы понимаем, как преобразовывать массивы в изменяемые списки, давайте рассмотрим некоторые практические применения и распространенные ошибки. Мы создадим окончательный пример, который демонстрирует реальные варианты использования и способы избежать распространенных ошибок.
Создание нового Java-файла
Создайте новый Java-файл с именем
ArrayListPractical.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: Многие старые API Java возвращают массивы, которые можно преобразовать в списки, чтобы использовать современные функции Java, такие как лямбда-выражения и ссылки на методы.
Распространенные ошибки, которых следует избегать:
- Помните, что
Arrays.asList()создает список фиксированного размера, который не может расти или сжиматься. - Массивы примитивных типов нельзя напрямую передавать в
Arrays.asList()- необходимо использовать циклы или потоки с упаковкой.
- Помните, что
Рекомендации: Для большинства вариантов использования наиболее безопасным подходом является использование
new ArrayList<>(Arrays.asList(array))для массивов объектов и методов потоков для массивов примитивных типов.
Понимая эти шаблоны и ошибки, вы можете эффективно работать как с массивами, так и со списками в своих Java-приложениях, выбирая правильную структуру данных и метод преобразования для каждой ситуации.
Резюме
В этой лабораторной работе вы узнали, как создавать изменяемые списки (Lists) из массивов Java, сохраняя при этом данные исходного массива. Вы изучили:
Фундаментальные различия между массивами Java и списками (Lists), включая их структуру, возможности и варианты использования.
Несколько методов преобразования массивов в списки (Lists):
- Использование
Arrays.asList()для представления массива фиксированного размера. - Использование
new ArrayList<>(Arrays.asList())для полностью изменяемого списка (List). - Использование Stream API с
.boxed()для массивов примитивных типов.
- Использование
Особые соображения для массивов примитивных типов, которые требуют явной упаковки (boxing) для преобразования в списки объектов-оберток.
Практическое применение и распространенные ошибки при работе с массивами и списками (Lists), включая управление конфигурацией и взаимодействие с устаревшими API.
Эти методы ценны во многих реальных Java-приложениях, где вам нужно работать как с массивами, так и со списками (Lists), особенно когда вам нужно сохранить исходные данные при создании гибких, изменяемых коллекций для манипулирования. Понимание взаимосвязи между массивами и списками (Lists) позволяет писать более эффективный и удобный в обслуживании код на Java.



