Java實現(xiàn)List與數(shù)組互相轉(zhuǎn)換的兩種方法
在 Java 編程中,List 和數(shù)組(Array)是兩種常用的數(shù)據(jù)結(jié)構(gòu)。List 提供了動態(tài)大小、豐富的操作方法,而數(shù)組則以其高效的隨機訪問和內(nèi)存效率著稱。在實際開發(fā)中,我們經(jīng)常需要在這兩者之間進行轉(zhuǎn)換。本文將深入探討 List 與數(shù)組之間的相互轉(zhuǎn)換,重點介紹 Arrays.asList 和 Collectors.toList 這兩種常用且重要的方法,并分析它們的特點、適用場景及注意事項。
一、引言
在 Java 應(yīng)用程序開發(fā)中,數(shù)據(jù)的表示和處理是核心環(huán)節(jié)。List 接口(如 ArrayList、LinkedList)和數(shù)組(Array)是 Java 中最基礎(chǔ)、最常用的數(shù)據(jù)容器。List 提供了諸如動態(tài)擴容、豐富的遍歷和修改方法(如 add, remove, get)等特性,使其非常適合處理動態(tài)變化的數(shù)據(jù)集合。而數(shù)組則以其緊湊的內(nèi)存布局和快速的索引訪問能力,在需要高性能、固定大小數(shù)據(jù)處理的場景下表現(xiàn)出色。
然而,在實際項目中,我們常常面臨需要在 List 和數(shù)組之間進行轉(zhuǎn)換的需求。例如:
- API 接口調(diào)用:某些舊版 API 或第三方庫可能期望接收數(shù)組作為參數(shù),而我們內(nèi)部的數(shù)據(jù)結(jié)構(gòu)可能是
List。 - 性能優(yōu)化:在特定算法中,數(shù)組的隨機訪問性能優(yōu)于
List。 - 數(shù)據(jù)序列化/反序列化:某些序列化框架或協(xié)議可能基于數(shù)組。
- 兼容性處理:處理遺留代碼或與不支持
Collection接口的庫交互。
因此,熟練掌握 List 與數(shù)組之間的轉(zhuǎn)換方法至關(guān)重要。本文將聚焦于兩個核心方法:Arrays.asList 和 Collectors.toList,解析它們的工作原理、優(yōu)缺點以及最佳實踐。
1.1 為什么需要 List 與數(shù)組互轉(zhuǎn)?
- 接口適配:API 設(shè)計可能要求不同的輸入格式。
- 性能考量:數(shù)組在某些場景下提供更好的性能。
- 數(shù)據(jù)共享:在不同組件或模塊間傳遞數(shù)據(jù)時,可能需要轉(zhuǎn)換格式。
- 歷史代碼兼容:與舊代碼或遺留系統(tǒng)的交互。
1.2 核心概念:Arrays.asList 與 Collectors.toList
我們將重點關(guān)注以下兩個方法:
Arrays.asList(...):這是一個靜態(tài)方法,它將可變參數(shù)列表(通常是數(shù)組)包裝成一個固定大小的List。這個List是Arrays.ArrayList的實例,它并非java.util.ArrayList,而是Arrays類內(nèi)部的一個私有類。Collectors.toList():這是 Java 8 Stream API 中的一個收集器(Collector),用于將流(Stream)中的元素收集到一個java.util.ArrayList中。
二、Arrays.asList 的詳解
2.1 基本用法
Arrays.asList 是 java.util.Arrays 類中的一個靜態(tài)方法,它可以將一個或多個對象作為參數(shù),包裝成一個固定大小的 List。這個 List 的底層實際上是傳入的數(shù)組。
import java.util.*;
public class ArraysAsListExample {
public static void main(String[] args) {
// 從數(shù)組轉(zhuǎn)換為 List
String[] array = {"apple", "banana", "cherry"};
List<String> listFromArr = Arrays.asList(array);
System.out.println("從數(shù)組轉(zhuǎn)換的 List: " + listFromArr); // 輸出: [apple, banana, cherry]
// 從可變參數(shù)創(chuàng)建 List
List<Integer> listFromVarArgs = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("從可變參數(shù)創(chuàng)建的 List: " + listFromVarArgs); // 輸出: [1, 2, 3, 4, 5]
// 直接使用
List<String> directList = Arrays.asList("a", "b", "c");
System.out.println("直接使用: " + directList); // 輸出: [a, b, c]
// 修改原始數(shù)組會影響 List
array[0] = "orange";
System.out.println("修改數(shù)組后 List: " + listFromArr); // 輸出: [orange, banana, cherry]
}
}
2.2 Arrays.asList 的內(nèi)部機制與特點
Arrays.asList 返回的是 Arrays.ArrayList,這是一個 Arrays 類內(nèi)部的私有靜態(tài)類,它繼承自 AbstractList,但與我們常見的 java.util.ArrayList 有本質(zhì)區(qū)別。
import java.util.*;
public class ArraysAsListInternal {
public static void main(String[] args) {
String[] array = {"apple", "banana", "cherry"};
List<String> list = Arrays.asList(array);
System.out.println("List 類型: " + list.getClass().getName()); // 輸出: java.util.Arrays$ArrayList
System.out.println("List 是否為 ArrayList: " + (list instanceof ArrayList)); // 輸出: false
System.out.println("List 是否為 Arrays.ArrayList: " + (list.getClass().getSimpleName().equals("Arrays.ArrayList"))); // 輸出: true
// 嘗試添加元素會拋出 UnsupportedOperationException
try {
list.add("date"); // 這會拋出異常
} catch (UnsupportedOperationException e) {
System.err.println("嘗試添加元素失敗: " + e.getMessage());
}
// 嘗試刪除元素會拋出 UnsupportedOperationException
try {
list.remove(0); // 這會拋出異常
} catch (UnsupportedOperationException e) {
System.err.println("嘗試刪除元素失敗: " + e.getMessage());
}
// 但是可以修改列表中的元素(因為底層是同一個數(shù)組)
list.set(0, "grape");
System.out.println("修改 List 元素后: " + list); // 輸出: [grape, banana, cherry]
System.out.println("原始數(shù)組也被修改: " + Arrays.toString(array)); // 輸出: [grape, banana, cherry]
// 嘗試替換整個 List 會拋出 UnsupportedOperationException
try {
List<String> anotherList = Arrays.asList("x", "y", "z");
list.clear(); // 清空 List
list.addAll(anotherList); // 添加新元素
} catch (UnsupportedOperationException e) {
System.err.println("嘗試替換 List 內(nèi)容失敗: " + e.getMessage());
}
}
}
重要特性:
- 固定大小:
Arrays.asList返回的List是一個固定大小的視圖。嘗試調(diào)用add()、remove()等會修改List大小的方法會導(dǎo)致UnsupportedOperationException。 - 共享引用:
List和原始數(shù)組共享底層數(shù)據(jù)。修改數(shù)組元素會影響List,反之亦然。 - 非通用 ArrayList:返回的
List類型是Arrays.ArrayList,而不是java.util.ArrayList。
2.3 Arrays.asList 的局限性
- 不可變性:無法通過
add、remove等方法動態(tài)調(diào)整大小。 - 不適用于所有場景:如果需要一個真正的
ArrayList來進行后續(xù)操作,Arrays.asList返回的對象就不夠用了。
import java.util.*;
public class ArraysAsListLimitations {
public static void main(String[] args) {
String[] array = {"apple", "banana"};
List<String> list = Arrays.asList(array);
// 1. 無法添加元素
try {
list.add("cherry"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("無法添加元素: " + e.getMessage());
}
// 2. 無法移除元素
try {
list.remove("apple"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("無法移除元素: " + e.getMessage());
}
// 3. 無法設(shè)置大小
try {
list.setSize(5); // 編譯錯誤,List 沒有 setSize 方法
} catch (Exception e) {
System.err.println("List 沒有 setSize 方法");
}
// 解決方案:創(chuàng)建一個新的 ArrayList
List<String> arrayList = new ArrayList<>(list);
arrayList.add("cherry");
System.out.println("新 ArrayList: " + arrayList); // 輸出: [apple, banana, cherry]
// 4. 無法使用需要動態(tài)大小的方法
// 例如,使用 Collections.sort(list) 通常是安全的(因為只是排序元素,不改變大小)
Collections.sort(arrayList);
System.out.println("排序后的 ArrayList: " + arrayList); // 輸出: [apple, banana, cherry]
}
}
技巧:如果你需要一個可以動態(tài)修改的 List,可以從 Arrays.asList 返回的 List 創(chuàng)建一個新的 ArrayList。
2.4 Arrays.asList 與泛型
Arrays.asList 支持泛型,可以處理任何類型的數(shù)組。
import java.util.*;
public class ArraysAsListGenerics {
public static void main(String[] args) {
// 字符串?dāng)?shù)組
String[] stringArray = {"hello", "world"};
List<String> stringList = Arrays.asList(stringArray);
System.out.println("String List: " + stringList);
// 整數(shù)數(shù)組
Integer[] integerArray = {1, 2, 3, 4, 5};
List<Integer> integerList = Arrays.asList(integerArray);
System.out.println("Integer List: " + integerList);
// 基本類型數(shù)組(需要裝箱)
int[] intArray = {10, 20, 30};
// int[] 不能直接傳給 Arrays.asList,需要手動包裝
List<Integer> intList = Arrays.stream(intArray).boxed().collect(Collectors.toList());
System.out.println("int[] 轉(zhuǎn) List: " + intList);
// 自定義對象數(shù)組
class Person {
String name;
int age;
Person(String name, int age) { this.name = name; this.age = age; }
@Override
public String toString() { return name + "(" + age + ")"; }
}
Person[] personArray = {new Person("Alice", 30), new Person("Bob", 25)};
List<Person> personList = Arrays.asList(personArray);
System.out.println("Person List: " + personList);
}
}
三、Collectors.toList() 的詳解
3.1 基本用法
Collectors.toList() 是 Java 8 Stream API 中的一個收集器,用于將流中的元素收集到一個 java.util.ArrayList 中。它通常與 Stream 結(jié)合使用。
import java.util.*;
import java.util.stream.Collectors;
public class CollectorsToListExample {
public static void main(String[] args) {
// 從 List 轉(zhuǎn)換為 List
List<String> sourceList = Arrays.asList("apple", "banana", "cherry");
List<String> targetList = sourceList.stream()
.collect(Collectors.toList());
System.out.println("轉(zhuǎn)換后的 List: " + targetList); // 輸出: [apple, banana, cherry]
// 從數(shù)組轉(zhuǎn)換為 List
String[] sourceArray = {"dog", "cat", "bird"};
List<String> arrayToList = Arrays.stream(sourceArray)
.collect(Collectors.toList());
System.out.println("數(shù)組轉(zhuǎn)換為 List: " + arrayToList); // 輸出: [dog, cat, bird]
// 從集合轉(zhuǎn)換為 List
Set<Integer> sourceSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> setToList = sourceSet.stream()
.collect(Collectors.toList());
System.out.println("Set 轉(zhuǎn)換為 List: " + setToList); // 輸出: [1, 2, 3, 4, 5] (順序可能不同)
// 復(fù)雜對象轉(zhuǎn)換
List<Person> people = Arrays.asList(
new Person("Charlie", 35),
new Person("Diana", 28)
);
List<String> names = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
System.out.println("提取名字: " + names); // 輸出: [Charlie, Diana]
}
static class Person {
String name;
int age;
Person(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() { return name + "(" + age + ")"; }
}
}
3.2 Collectors.toList() 的內(nèi)部機制與特點
Collectors.toList() 返回的是一個 Collector,它會將流中的元素收集到一個 java.util.ArrayList 中。這個 ArrayList 是一個標(biāo)準(zhǔn)的、可變的 List 實現(xiàn)。
import java.util.*;
import java.util.stream.Collectors;
public class CollectorsToListInternal {
public static void main(String[] args) {
List<String> originalList = Arrays.asList("a", "b", "c");
// 使用 Collectors.toList() 創(chuàng)建新的 ArrayList
List<String> newList = originalList.stream()
.collect(Collectors.toList());
System.out.println("原始 List 類型: " + originalList.getClass().getName()); // 輸出: java.util.Arrays$ArrayList
System.out.println("新 List 類型: " + newList.getClass().getName()); // 輸出: java.util.ArrayList
// 新 List 是可變的
newList.add("d");
System.out.println("新 List 添加元素后: " + newList); // 輸出: [a, b, c, d]
// 原始 List 不受影響
System.out.println("原始 List 保持不變: " + originalList); // 輸出: [a, b, c]
// 可以進行各種 List 操作
newList.remove("a");
Collections.sort(newList);
System.out.println("排序后的 List: " + newList); // 輸出: [b, c, d]
}
}
? 優(yōu)點:
- 可變性:返回的是標(biāo)準(zhǔn)的
java.util.ArrayList,可以自由地進行add、remove、set等操作。 - 靈活性:可以方便地與其他
Stream操作結(jié)合,如filter,map,sorted等。 - 通用性:返回的是標(biāo)準(zhǔn)的
List接口實現(xiàn),可以用于任何需要List的地方。
3.3 使用 Collectors.toList() 的注意事項
- 性能開銷:
Stream的創(chuàng)建和處理會有一定的性能開銷,對于簡單轉(zhuǎn)換,直接使用new ArrayList<>(sourceList)可能更快。 - 內(nèi)存占用:
Collectors.toList()會創(chuàng)建一個新的ArrayList,增加了內(nèi)存占用。
import java.util.*;
import java.util.stream.Collectors;
public class CollectorsToListConsiderations {
public static void main(String[] args) {
List<String> sourceList = Arrays.asList("apple", "banana", "cherry");
// 方式 1: 使用 Collectors.toList()
List<String> list1 = sourceList.stream().collect(Collectors.toList());
// 方式 2: 直接構(gòu)造
List<String> list2 = new ArrayList<>(sourceList);
// 方式 3: 使用 Arrays.asList() 后再復(fù)制(注意:這種方式是創(chuàng)建副本)
List<String> list3 = new ArrayList<>(Arrays.asList(sourceList.toArray()));
// 方式 4: 手動循環(huán)(對于大型列表,可能更慢)
List<String> list4 = new ArrayList<>();
for (String item : sourceList) {
list4.add(item);
}
System.out.println("方式 1 (Collectors): " + list1);
System.out.println("方式 2 (構(gòu)造): " + list2);
System.out.println("方式 3 (asList + toArray): " + list3);
System.out.println("方式 4 (循環(huán)): " + list4);
// 驗證是否為新對象
System.out.println("list1 == list2: " + (list1 == list2)); // false
System.out.println("list1 == list3: " + (list1 == list3)); // false
System.out.println("list1 == list4: " + (list1 == list4)); // false
}
}
四、List 與數(shù)組互轉(zhuǎn)的全面對比
4.1 List 轉(zhuǎn)數(shù)組
4.1.1 使用toArray()方法
這是 List 接口提供的標(biāo)準(zhǔn)方法。
import java.util.*;
public class ListToArrayExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 1. 無參 toArray() -> Object[]
Object[] objectArray = list.toArray();
System.out.println("Object[]: " + Arrays.toString(objectArray));
// 2. 有參 toArray(T[] a) -> T[]
String[] stringArray = list.toArray(new String[0]); // 傳入長度為0的數(shù)組
System.out.println("String[]: " + Arrays.toString(stringArray));
// 3. 有參 toArray(T[] a) -> T[] (指定大小)
String[] stringArrayWithSize = list.toArray(new String[list.size()]);
System.out.println("String[] (指定大小): " + Arrays.toString(stringArrayWithSize));
// 4. 從 ArrayList 轉(zhuǎn)換
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3));
Integer[] intArray = intList.toArray(new Integer[0]);
System.out.println("Integer[]: " + Arrays.toString(intArray));
// 5. 基本類型數(shù)組轉(zhuǎn)換 (需要手動處理)
List<Integer> intList2 = Arrays.asList(10, 20, 30);
int[] primitiveIntArray = intList2.stream().mapToInt(Integer::intValue).toArray();
System.out.println("primitive int[]: " + Arrays.toString(primitiveIntArray));
}
}
4.1.2 使用 Stream API
import java.util.*;
import java.util.stream.Collectors;
public class ListToArrayStream {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
// 轉(zhuǎn)換為 String[] (使用 Collectors.toArray)
String[] array1 = list.toArray(String[]::new);
System.out.println("Stream toArray(String[]::new): " + Arrays.toString(array1));
// 轉(zhuǎn)換為 Object[] (使用 Collectors.toArray)
Object[] array2 = list.stream().toArray(Object[]::new);
System.out.println("Stream toArray(Object[]::new): " + Arrays.toString(array2));
// 轉(zhuǎn)換為 int[] (使用 Stream)
List<Integer> intList = Arrays.asList(1, 2, 3);
int[] primitiveArray = intList.stream().mapToInt(Integer::intValue).toArray();
System.out.println("Stream to primitive int[]: " + Arrays.toString(primitiveArray));
}
}
4.2 數(shù)組轉(zhuǎn) List
4.2.1 使用Arrays.asList()
這是最常用的方式。
import java.util.*;
public class ArrayToListAsList {
public static void main(String[] args) {
String[] array = {"apple", "banana", "cherry"};
// 使用 Arrays.asList
List<String> list = Arrays.asList(array);
System.out.println("Arrays.asList 結(jié)果: " + list);
// 注意:返回的 List 是固定大小的
try {
list.add("date"); // 拋出 UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("添加元素失敗: " + e.getMessage());
}
// 修改原始數(shù)組會影響 List
array[0] = "orange";
System.out.println("修改數(shù)組后 List: " + list); // 輸出: [orange, banana, cherry]
// 如果需要可變 List,需要復(fù)制
List<String> mutableList = new ArrayList<>(list);
mutableList.add("date");
System.out.println("可變 List: " + mutableList); // 輸出: [orange, banana, cherry, date]
}
}
4.2.2 使用Collectors.toList()
import java.util.*;
import java.util.stream.Collectors;
public class ArrayToListStream {
public static void main(String[] args) {
String[] array = {"dog", "cat", "bird"};
// 使用 Stream 和 Collectors.toList()
List<String> list = Arrays.stream(array)
.collect(Collectors.toList());
System.out.println("Stream + Collectors.toList(): " + list);
// 可以進行鏈?zhǔn)讲僮?
List<String> filteredList = Arrays.stream(array)
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
System.out.println("過濾后: " + filteredList); // 輸出: [dog, bird]
// 轉(zhuǎn)換為不同類型的 List
List<Integer> lengths = Arrays.stream(array)
.map(String::length)
.collect(Collectors.toList());
System.out.println("長度列表: " + lengths); // 輸出: [3, 3, 4]
// 基本類型數(shù)組
int[] intArray = {1, 2, 3, 4, 5};
List<Integer> intList = Arrays.stream(intArray)
.boxed() // 裝箱
.collect(Collectors.toList());
System.out.println("int[] 轉(zhuǎn) List<Integer>: " + intList); // 輸出: [1, 2, 3, 4, 5]
}
}
4.3 性能對比與選擇建議
讓我們通過一個簡單的性能測試來比較不同方法的效率。
import java.util.*;
import java.util.stream.Collectors;
public class ConversionPerformanceTest {
public static void main(String[] args) {
// 準(zhǔn)備大量數(shù)據(jù)
int size = 1000000;
Integer[] array = new Integer[size];
for (int i = 0; i < size; i++) {
array[i] = i;
}
// 測試 1: Arrays.asList + new ArrayList
long start = System.nanoTime();
List<Integer> list1 = new ArrayList<>(Arrays.asList(array));
long time1 = System.nanoTime() - start;
// 測試 2: Stream + Collectors.toList
start = System.nanoTime();
List<Integer> list2 = Arrays.stream(array).collect(Collectors.toList());
long time2 = System.nanoTime() - start;
// 測試 3: 直接構(gòu)造 ArrayList (最高效)
start = System.nanoTime();
List<Integer> list3 = new ArrayList<>(Arrays.asList(array));
long time3 = System.nanoTime() - start;
// 測試 4: 手動循環(huán) (通常較慢)
start = System.nanoTime();
List<Integer> list4 = new ArrayList<>();
for (Integer i : array) {
list4.add(i);
}
long time4 = System.nanoTime() - start;
System.out.println("Arrays.asList + new ArrayList: " + time1 / 1_000_000 + " ms");
System.out.println("Stream + Collectors.toList: " + time2 / 1_000_000 + " ms");
System.out.println("Direct constructor: " + time3 / 1_000_000 + " ms");
System.out.println("Manual loop: " + time4 / 1_000_000 + " ms");
// 驗證結(jié)果一致性
System.out.println("List1 size: " + list1.size());
System.out.println("List2 size: " + list2.size());
System.out.println("List3 size: " + list3.size());
System.out.println("List4 size: " + list4.size());
}
}
?? 性能結(jié)論:
- 簡單轉(zhuǎn)換:直接使用
new ArrayList<>(Arrays.asList(array))或new ArrayList<>(sourceList)通常是最快的。 - 復(fù)雜操作:如果需要在轉(zhuǎn)換過程中進行過濾、映射等操作,Stream API 的
Collectors.toList()更具優(yōu)勢和可讀性。 - 內(nèi)存考慮:
Arrays.asList()返回的是視圖,不會創(chuàng)建新數(shù)組;而Collectors.toList()和new ArrayList()會創(chuàng)建新的ArrayList。
五、實際應(yīng)用場景
5.1 API 接口兼容
假設(shè)你需要調(diào)用一個老 API,它期望接收一個 String[] 參數(shù)。
import java.util.*;
import java.util.stream.Collectors;
public class ApiCompatibilityExample {
// 模擬的老 API
public static void processStringArray(String[] data) {
System.out.println("處理數(shù)組: " + Arrays.toString(data));
}
public static void main(String[] args) {
// 我們內(nèi)部使用 List
List<String> internalList = Arrays.asList("item1", "item2", "item3");
// 方式 1: 使用 Arrays.asList + new ArrayList (如果需要修改)
List<String> mutableList = new ArrayList<>(internalList);
mutableList.add("item4");
processStringArray(mutableList.toArray(new String[0]));
// 方式 2: 直接使用 toArray (如果不需要修改)
processStringArray(internalList.toArray(new String[0]));
// 方式 3: 使用 Stream (如果需要鏈?zhǔn)讲僮?
List<String> processedList = internalList.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
processStringArray(processedList.toArray(new String[0]));
}
}
5.2 集合操作與數(shù)據(jù)處理
在數(shù)據(jù)處理場景中,經(jīng)常需要將數(shù)據(jù)從一種形式轉(zhuǎn)換為另一種形式。
import java.util.*;
import java.util.stream.Collectors;
public class DataProcessingExample {
public static void main(String[] args) {
// 假設(shè)有一個訂單列表
List<Order> orders = Arrays.asList(
new Order("A001", 100.0),
new Order("A002", 200.0),
new Order("A003", 150.0)
);
// 1. 獲取所有訂單號
List<String> orderNumbers = orders.stream()
.map(Order::getOrderNumber)
.collect(Collectors.toList());
System.out.println("訂單號: " + orderNumbers);
// 2. 獲取所有訂單金額
List<Double> amounts = orders.stream()
.map(Order::getAmount)
.collect(Collectors.toList());
System.out.println("訂單金額: " + amounts);
// 3. 過濾金額大于 150 的訂單
List<Order> highValueOrders = orders.stream()
.filter(o -> o.getAmount() > 150)
.collect(Collectors.toList());
System.out.println("高價值訂單: " + highValueOrders);
// 4. 將訂單轉(zhuǎn)換為數(shù)組 (例如,傳遞給某個需要數(shù)組的函數(shù))
Order[] orderArray = orders.toArray(new Order[0]);
System.out.println("訂單數(shù)組長度: " + orderArray.length);
// 5. 轉(zhuǎn)換為 Map (鍵為訂單號,值為金額)
Map<String, Double> orderMap = orders.stream()
.collect(Collectors.toMap(Order::getOrderNumber, Order::getAmount));
System.out.println("訂單 Map: " + orderMap);
}
static class Order {
private String orderNumber;
private double amount;
public Order(String orderNumber, double amount) {
this.orderNumber = orderNumber;
this.amount = amount;
}
public String getOrderNumber() { return orderNumber; }
public double getAmount() { return amount; }
@Override
public String toString() {
return "Order{" +
"orderNumber='" + orderNumber + '\'' +
", amount=" + amount +
'}';
}
}
}
5.3 配置管理
在配置文件處理中,有時需要將配置項轉(zhuǎn)換為數(shù)組或列表。
import java.util.*;
import java.util.stream.Collectors;
public class ConfigManagementExample {
public static void main(String[] args) {
// 模擬配置項(例如,逗號分隔的字符串)
String configString = "database.host, database.port, database.name";
// 轉(zhuǎn)換為 List
List<String> configList = Arrays.stream(configString.split(","))
.map(String::trim)
.collect(Collectors.toList());
System.out.println("配置項 List: " + configList);
// 轉(zhuǎn)換為數(shù)組
String[] configArray = configString.split(",");
String[] trimmedArray = Arrays.stream(configArray)
.map(String::trim)
.toArray(String[]::new);
System.out.println("配置項數(shù)組: " + Arrays.toString(trimmedArray));
// 如果需要對數(shù)組進行進一步處理
String[] upperCaseArray = configList.stream()
.map(String::toUpperCase)
.toArray(String[]::new);
System.out.println("大寫配置項數(shù)組: " + Arrays.toString(upperCaseArray));
}
}
六、常見問題與解決方案
6.1 Arrays.asList 返回的 List 不可變
這是最常見的問題之一。
問題:
import java.util.*;
public class AsListImmutableProblem {
public static void main(String[] args) {
String[] array = {"apple", "banana"};
List<String> list = Arrays.asList(array);
// 嘗試添加元素
try {
list.add("cherry"); // 拋出 UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("錯誤: " + e.getMessage());
}
}
}
解決方案:
import java.util.*;
public class AsListImmutableSolution {
public static void main(String[] args) {
String[] array = {"apple", "banana"};
List<String> list = Arrays.asList(array);
// 方案 1: 創(chuàng)建新的 ArrayList
List<String> mutableList = new ArrayList<>(list);
mutableList.add("cherry");
System.out.println("新的可變 List: " + mutableList);
// 方案 2: 使用 Stream
List<String> streamList = Arrays.stream(array).collect(Collectors.toList());
streamList.add("cherry");
System.out.println("Stream 創(chuàng)建的 List: " + streamList);
}
}
6.2 基本類型數(shù)組轉(zhuǎn)換為 List 的陷阱
使用 Arrays.asList() 時,基本類型數(shù)組會被當(dāng)作一個對象處理。
import java.util.*;
public class PrimitiveArrayTrap {
public static void main(String[] args) {
// 錯誤方式:int[] 會被當(dāng)作單個對象
int[] intArray = {1, 2, 3};
List<int[]> wrongList = Arrays.asList(intArray); // List<int[]>
System.out.println("錯誤方式的結(jié)果: " + wrongList); // 輸出: [[I@...]
System.out.println("長度: " + wrongList.size()); // 輸出: 1
// 正確方式:先裝箱再轉(zhuǎn)換
List<Integer> correctList = Arrays.stream(intArray).boxed().collect(Collectors.toList());
System.out.println("正確方式的結(jié)果: " + correctList); // 輸出: [1, 2, 3]
}
}
6.3 Stream 性能與內(nèi)存
對于大量數(shù)據(jù)的轉(zhuǎn)換,Stream API 會創(chuàng)建中間對象,可能帶來性能和內(nèi)存開銷。
import java.util.*;
import java.util.stream.Collectors;
public class StreamPerformanceConsideration {
public static void main(String[] args) {
int size = 1000000;
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < size; i++) {
largeList.add(i);
}
// 對于大型列表,直接轉(zhuǎn)換可能更快
long start = System.nanoTime();
List<Integer> convertedList1 = new ArrayList<>(largeList);
long time1 = System.nanoTime() - start;
// Stream 轉(zhuǎn)換
start = System.nanoTime();
List<Integer> convertedList2 = largeList.stream().collect(Collectors.toList());
long time2 = System.nanoTime() - start;
System.out.println("直接構(gòu)造時間: " + time1 / 1_000_000 + " ms");
System.out.println("Stream 時間: " + time2 / 1_000_000 + " ms");
// 但 Stream 在鏈?zhǔn)讲僮鲿r更有優(yōu)勢
start = System.nanoTime();
List<Integer> filteredList = largeList.stream()
.filter(x -> x % 2 == 0)
.map(x -> x * 2)
.collect(Collectors.toList());
long time3 = System.nanoTime() - start;
System.out.println("Stream 鏈?zhǔn)讲僮鲿r間: " + time3 / 1_000_000 + " ms");
}
}
七、可視化:List 與數(shù)組轉(zhuǎn)換流程
為了更直觀地理解 List 與數(shù)組之間的轉(zhuǎn)換關(guān)系,下面是一個使用 Mermaid 繪制的流程圖,展示了主要的轉(zhuǎn)換路徑和特點。
graph TD
A[List] --> B{轉(zhuǎn)換為數(shù)組?}
A --> C{轉(zhuǎn)換為 List?}
B -->|toArray()| D[Array]
B -->|Stream+toArray()| D
C -->|Arrays.asList()| E[Fixed-size List (Arrays.ArrayList)]
C -->|Stream+Collectors.toList()| F[Mutable ArrayList]
C -->|new ArrayList<>(...)| F
subgraph "Arrays.asList()"
E --> G[Fixed size]
E --> H[Backed by original array]
E --> I[No add/remove operations]
end
subgraph "Collectors.toList()"
F --> J[Mutable]
F --> K[Standard ArrayList]
F --> L[Full List functionality]
end
subgraph "toArray()"
D --> M[Creates new array]
D --> N[Can specify type]
end
style E fill:#ffcccc
style F fill:#ccffcc
style D fill:#ccccff
八、最佳實踐與總結(jié)
8.1 選擇合適的轉(zhuǎn)換方式
根據(jù)具體需求選擇最適合的轉(zhuǎn)換方式:
- 簡單復(fù)制:如果只是想復(fù)制一個
List或者從數(shù)組創(chuàng)建一個可變的List,直接使用new ArrayList<>(source)或new ArrayList<>(Arrays.asList(array))。 - 需要鏈?zhǔn)讲僮?/strong>:如果需要在轉(zhuǎn)換過程中進行過濾、映射等操作,使用
Stream和Collectors.toList()。 - API 兼容性:如果需要將
List傳遞給一個期望數(shù)組的 API,使用list.toArray(new T[0])。 - 性能敏感場景:對于大量數(shù)據(jù)的簡單轉(zhuǎn)換,避免不必要的 Stream 開銷,直接使用構(gòu)造函數(shù)或
toArray()。
8.2 注意事項
- 類型安全:使用泛型確保類型安全。
- 不可變性:注意
Arrays.asList()返回的List是不可變的,如果需要修改,需要創(chuàng)建副本。 - 基本類型:處理基本類型數(shù)組時,注意
Arrays.asList()會將其視為單個對象,需要使用Stream的boxed()方法。 - 內(nèi)存使用:頻繁的轉(zhuǎn)換操作會增加內(nèi)存開銷,特別是在大數(shù)據(jù)集上。
8.3 代碼示例:綜合應(yīng)用
import java.util.*;
import java.util.stream.Collectors;
public class ComprehensiveExample {
public static void main(String[] args) {
// 1. 從外部獲取數(shù)組(例如,來自數(shù)據(jù)庫查詢結(jié)果)
String[] externalData = {"user1", "user2", "user3", "user4"};
// 2. 轉(zhuǎn)換為 List 以便進行復(fù)雜的處理(例如,過濾、排序)
List<String> userList = Arrays.stream(externalData)
.filter(name -> !name.startsWith("user3")) // 過濾掉 user3
.sorted() // 排序
.collect(Collectors.toList());
System.out.println("處理后的用戶列表: " + userList);
// 3. 生成報告或發(fā)送給 API(需要數(shù)組)
String[] reportArray = userList.toArray(new String[0]);
System.out.println("報告數(shù)組: " + Arrays.toString(reportArray));
// 4. 保存到另一個數(shù)據(jù)結(jié)構(gòu)(例如,Set)
Set<String> userSet = new HashSet<>(userList);
System.out.println("用戶 Set: " + userSet);
// 5. 從 Set 轉(zhuǎn)換回 List(如果需要)
List<String> fromSet = new ArrayList<>(userSet);
System.out.println("從 Set 轉(zhuǎn)換的 List: " + fromSet);
// 6. 處理基本類型數(shù)組
int[] scores = {85, 92, 78, 96, 88};
List<Integer> scoreList = Arrays.stream(scores)
.boxed() // 裝箱
.collect(Collectors.toList());
System.out.println("分?jǐn)?shù)列表: " + scoreList);
// 7. 計算平均分
double averageScore = scoreList.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
System.out.println("平均分: " + averageScore);
// 8. 將平均分轉(zhuǎn)換為數(shù)組(例如,用于圖表繪制)
double[] avgArray = new double[]{averageScore};
System.out.println("平均分?jǐn)?shù)組: " + Arrays.toString(avgArray));
}
}
九、性能與內(nèi)存考量
在處理大規(guī)模數(shù)據(jù)時,選擇合適的轉(zhuǎn)換方法對性能和內(nèi)存使用至關(guān)重要。
9.1 內(nèi)存使用分析
Arrays.asList():不創(chuàng)建新數(shù)組,僅創(chuàng)建一個視圖,內(nèi)存開銷最小。new ArrayList<>(Arrays.asList(array)):創(chuàng)建一個ArrayList和一個Arrays.ArrayList視圖,內(nèi)存開銷略高。new ArrayList<>(sourceList):創(chuàng)建一個新的ArrayList,內(nèi)存開銷較高。Stream + Collectors.toList():創(chuàng)建Stream對象和新的ArrayList,內(nèi)存開銷較大。
9.2 性能基準(zhǔn)測試
import java.util.*;
import java.util.stream.Collectors;
public class PerformanceBenchmark {
public static void main(String[] args) {
// 準(zhǔn)備測試數(shù)據(jù)
int size = 1000000;
Integer[] testData = new Integer[size];
for (int i = 0; i < size; i++) {
testData[i] = i;
}
// 測試方法 1: new ArrayList<>(Arrays.asList(array))
long start = System.nanoTime();
List<Integer> list1 = new ArrayList<>(Arrays.asList(testData));
long time1 = System.nanoTime() - start;
// 測試方法 2: new ArrayList<>(Arrays.asList(array)) (直接從數(shù)組)
start = System.nanoTime();
List<Integer> list2 = new ArrayList<>(Arrays.asList(testData));
long time2 = System.nanoTime() - start;
// 測試方法 3: Stream + Collectors.toList()
start = System.nanoTime();
List<Integer> list3 = Arrays.stream(testData).collect(Collectors.toList());
long time3 = System.nanoTime() - start;
// 測試方法 4: 手動循環(huán)
start = System.nanoTime();
List<Integer> list4 = new ArrayList<>();
for (Integer i : testData) {
list4.add(i);
}
long time4 = System.nanoTime() - start;
// 測試方法 5: Arrays.asList(array).stream().collect(Collectors.toList())
start = System.nanoTime();
List<Integer> list5 = Arrays.asList(testData).stream().collect(Collectors.toList());
long time5 = System.nanoTime() - start;
System.out.println("方法 1 (new ArrayList<>(Arrays.asList)): " + time1 / 1_000_000 + " ms");
System.out.println("方法 2 (new ArrayList<>(Arrays.asList) - same): " + time2 / 1_000_000 + " ms");
System.out.println("方法 3 (Stream + Collectors): " + time3 / 1_000_000 + " ms");
System.out.println("方法 4 (手動循環(huán)): " + time4 / 1_000_000 + " ms");
System.out.println("方法 5 (asList + Stream): " + time5 / 1_000_000 + " ms");
// 驗證結(jié)果一致性
System.out.println("所有結(jié)果大小相同: " + (list1.size() == list2.size() && list2.size() == list3.size() && list3.size() == list4.size() && list4.size() == list5.size()));
}
}
性能結(jié)論:
- 對于簡單復(fù)制,
new ArrayList<>(Arrays.asList(array))通常是最快的。 - 對于需要鏈?zhǔn)讲僮鞯膱鼍埃?code>Stream 提供了更好的可讀性和功能。
manual loop通常是最慢的,但有時在特定條件下可能有優(yōu)勢(例如,循環(huán)體內(nèi)有復(fù)雜的邏輯)。
十、總結(jié)
List 與數(shù)組之間的轉(zhuǎn)換是 Java 開發(fā)中的常見任務(wù)。本文詳細(xì)介紹了兩種核心方法:Arrays.asList() 和 Collectors.toList()。
Arrays.asList():快速、輕量級地將數(shù)組包裝為List,但返回的List是固定大小的,且底層共享數(shù)組。適用于只需要查看數(shù)據(jù)、不修改大小的場景,或者作為臨時視圖。Collectors.toList():通過StreamAPI 將數(shù)據(jù)收集到一個新的ArrayList中,返回一個完全可變的、標(biāo)準(zhǔn)的List。適用于需要后續(xù)修改、過濾、排序等操作的場景。
選擇哪種方式取決于具體的業(yè)務(wù)需求、性能要求和代碼的可讀性。記住關(guān)鍵點:
- 明確轉(zhuǎn)換目的:是僅僅為了遍歷?還是需要修改?
- 注意
Arrays.asList()的限制:它返回的List不支持add、remove等操作。 - 處理基本類型數(shù)組:需要使用
Stream和boxed()。 - 考慮性能:對于大量數(shù)據(jù),簡單復(fù)制比復(fù)雜 Stream 操作更高效。
- 利用 Stream 的強大功能:在需要鏈?zhǔn)教幚頃r,Stream API 提供了優(yōu)雅的解決方案。
掌握這些轉(zhuǎn)換技巧,可以讓你在處理數(shù)據(jù)時更加得心應(yīng)手,構(gòu)建出更健壯、高效的 Java 應(yīng)用程序。
以上就是Java實現(xiàn)List與數(shù)組互相轉(zhuǎn)換的兩種方法的詳細(xì)內(nèi)容,更多關(guān)于Java List與數(shù)組互轉(zhuǎn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
RestTemplate接口調(diào)用神器常見用法匯總
這篇文章主要介紹了RestTemplate接口調(diào)用神器常見用法匯總,通過案例代碼詳細(xì)介紹RestTemplate接口調(diào)用神器常見用法,需要的朋友可以參考下2022-07-07
集合框架(Collections Framework)詳解及代碼示例
這篇文章主要介紹了集合框架(Collections Framework)詳解及代碼示例,文章涉及集合數(shù)組的區(qū)別,collection接口,iterator迭代器,list接口及其用法,LinkedHashSet集合等有關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以了解下。2017-11-11
java Socket無法完全接收返回內(nèi)容的解決方案
這篇文章主要介紹了java Socket無法完全接收返回內(nèi)容的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
解決異常FileNotFoundException:class path resource找不到資源文件的問題
今天小編就為大家分享一篇關(guān)于解決異常FileNotFoundException:class path resource找不到資源文件的問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
elasticsearch索引index之Mapping實現(xiàn)關(guān)系結(jié)構(gòu)示例
這篇文章主要介紹了elasticsearch索引index之Mapping實現(xiàn)關(guān)系結(jié)構(gòu)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04
Spring動態(tài)多數(shù)據(jù)源配置實例Demo
本篇文章主要介紹了Spring動態(tài)多數(shù)據(jù)源配置實例Demo,具有一定的參考價值,有興趣的可以了解一下。2017-01-01
SpringBoot @Validated注解實現(xiàn)參數(shù)分組校驗的方法實例
這篇文章主要給大家介紹了關(guān)于SpringBoot @Validated注解實現(xiàn)參數(shù)分組校驗的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

