Java實(shí)現(xiàn)List與數(shù)組互相轉(zhuǎn)換的兩種方法
在 Java 編程中,List 和數(shù)組(Array)是兩種常用的數(shù)據(jù)結(jié)構(gòu)。List 提供了動(dòng)態(tài)大小、豐富的操作方法,而數(shù)組則以其高效的隨機(jī)訪問(wèn)和內(nèi)存效率著稱(chēng)。在實(shí)際開(kāi)發(fā)中,我們經(jīng)常需要在這兩者之間進(jìn)行轉(zhuǎn)換。本文將深入探討 List 與數(shù)組之間的相互轉(zhuǎn)換,重點(diǎn)介紹 Arrays.asList 和 Collectors.toList 這兩種常用且重要的方法,并分析它們的特點(diǎn)、適用場(chǎng)景及注意事項(xiàng)。
一、引言
在 Java 應(yīng)用程序開(kāi)發(fā)中,數(shù)據(jù)的表示和處理是核心環(huán)節(jié)。List 接口(如 ArrayList、LinkedList)和數(shù)組(Array)是 Java 中最基礎(chǔ)、最常用的數(shù)據(jù)容器。List 提供了諸如動(dòng)態(tài)擴(kuò)容、豐富的遍歷和修改方法(如 add, remove, get)等特性,使其非常適合處理動(dòng)態(tài)變化的數(shù)據(jù)集合。而數(shù)組則以其緊湊的內(nèi)存布局和快速的索引訪問(wèn)能力,在需要高性能、固定大小數(shù)據(jù)處理的場(chǎng)景下表現(xiàn)出色。
然而,在實(shí)際項(xiàng)目中,我們常常面臨需要在 List 和數(shù)組之間進(jìn)行轉(zhuǎn)換的需求。例如:
- API 接口調(diào)用:某些舊版 API 或第三方庫(kù)可能期望接收數(shù)組作為參數(shù),而我們內(nèi)部的數(shù)據(jù)結(jié)構(gòu)可能是
List。 - 性能優(yōu)化:在特定算法中,數(shù)組的隨機(jī)訪問(wèn)性能優(yōu)于
List。 - 數(shù)據(jù)序列化/反序列化:某些序列化框架或協(xié)議可能基于數(shù)組。
- 兼容性處理:處理遺留代碼或與不支持
Collection接口的庫(kù)交互。
因此,熟練掌握 List 與數(shù)組之間的轉(zhuǎn)換方法至關(guān)重要。本文將聚焦于兩個(gè)核心方法:Arrays.asList 和 Collectors.toList,解析它們的工作原理、優(yōu)缺點(diǎn)以及最佳實(shí)踐。
1.1 為什么需要 List 與數(shù)組互轉(zhuǎn)?
- 接口適配:API 設(shè)計(jì)可能要求不同的輸入格式。
- 性能考量:數(shù)組在某些場(chǎng)景下提供更好的性能。
- 數(shù)據(jù)共享:在不同組件或模塊間傳遞數(shù)據(jù)時(shí),可能需要轉(zhuǎn)換格式。
- 歷史代碼兼容:與舊代碼或遺留系統(tǒng)的交互。
1.2 核心概念:Arrays.asList 與 Collectors.toList
我們將重點(diǎn)關(guān)注以下兩個(gè)方法:
Arrays.asList(...):這是一個(gè)靜態(tài)方法,它將可變參數(shù)列表(通常是數(shù)組)包裝成一個(gè)固定大小的List。這個(gè)List是Arrays.ArrayList的實(shí)例,它并非java.util.ArrayList,而是Arrays類(lèi)內(nèi)部的一個(gè)私有類(lèi)。Collectors.toList():這是 Java 8 Stream API 中的一個(gè)收集器(Collector),用于將流(Stream)中的元素收集到一個(gè)java.util.ArrayList中。
二、Arrays.asList 的詳解
2.1 基本用法
Arrays.asList 是 java.util.Arrays 類(lèi)中的一個(gè)靜態(tài)方法,它可以將一個(gè)或多個(gè)對(duì)象作為參數(shù),包裝成一個(gè)固定大小的 List。這個(gè) List 的底層實(shí)際上是傳入的數(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ù)組會(huì)影響 List
array[0] = "orange";
System.out.println("修改數(shù)組后 List: " + listFromArr); // 輸出: [orange, banana, cherry]
}
}
2.2 Arrays.asList 的內(nèi)部機(jī)制與特點(diǎn)
Arrays.asList 返回的是 Arrays.ArrayList,這是一個(gè) Arrays 類(lèi)內(nèi)部的私有靜態(tài)類(lèi),它繼承自 AbstractList,但與我們常見(jiàn)的 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 類(lèi)型: " + 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
// 嘗試添加元素會(huì)拋出 UnsupportedOperationException
try {
list.add("date"); // 這會(huì)拋出異常
} catch (UnsupportedOperationException e) {
System.err.println("嘗試添加元素失敗: " + e.getMessage());
}
// 嘗試刪除元素會(huì)拋出 UnsupportedOperationException
try {
list.remove(0); // 這會(huì)拋出異常
} catch (UnsupportedOperationException e) {
System.err.println("嘗試刪除元素失敗: " + e.getMessage());
}
// 但是可以修改列表中的元素(因?yàn)榈讓邮峭粋€(gè)數(shù)組)
list.set(0, "grape");
System.out.println("修改 List 元素后: " + list); // 輸出: [grape, banana, cherry]
System.out.println("原始數(shù)組也被修改: " + Arrays.toString(array)); // 輸出: [grape, banana, cherry]
// 嘗試替換整個(gè) List 會(huì)拋出 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是一個(gè)固定大小的視圖。嘗試調(diào)用add()、remove()等會(huì)修改List大小的方法會(huì)導(dǎo)致UnsupportedOperationException。 - 共享引用:
List和原始數(shù)組共享底層數(shù)據(jù)。修改數(shù)組元素會(huì)影響List,反之亦然。 - 非通用 ArrayList:返回的
List類(lèi)型是Arrays.ArrayList,而不是java.util.ArrayList。
2.3 Arrays.asList 的局限性
- 不可變性:無(wú)法通過(guò)
add、remove等方法動(dòng)態(tài)調(diào)整大小。 - 不適用于所有場(chǎng)景:如果需要一個(gè)真正的
ArrayList來(lái)進(jìn)行后續(xù)操作,Arrays.asList返回的對(duì)象就不夠用了。
import java.util.*;
public class ArraysAsListLimitations {
public static void main(String[] args) {
String[] array = {"apple", "banana"};
List<String> list = Arrays.asList(array);
// 1. 無(wú)法添加元素
try {
list.add("cherry"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("無(wú)法添加元素: " + e.getMessage());
}
// 2. 無(wú)法移除元素
try {
list.remove("apple"); // UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.err.println("無(wú)法移除元素: " + e.getMessage());
}
// 3. 無(wú)法設(shè)置大小
try {
list.setSize(5); // 編譯錯(cuò)誤,List 沒(méi)有 setSize 方法
} catch (Exception e) {
System.err.println("List 沒(méi)有 setSize 方法");
}
// 解決方案:創(chuàng)建一個(gè)新的 ArrayList
List<String> arrayList = new ArrayList<>(list);
arrayList.add("cherry");
System.out.println("新 ArrayList: " + arrayList); // 輸出: [apple, banana, cherry]
// 4. 無(wú)法使用需要?jiǎng)討B(tài)大小的方法
// 例如,使用 Collections.sort(list) 通常是安全的(因?yàn)橹皇桥判蛟?,不改變大?。?
Collections.sort(arrayList);
System.out.println("排序后的 ArrayList: " + arrayList); // 輸出: [apple, banana, cherry]
}
}
技巧:如果你需要一個(gè)可以動(dòng)態(tài)修改的 List,可以從 Arrays.asList 返回的 List 創(chuàng)建一個(gè)新的 ArrayList。
2.4 Arrays.asList 與泛型
Arrays.asList 支持泛型,可以處理任何類(lèi)型的數(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);
// 基本類(lèi)型數(shù)組(需要裝箱)
int[] intArray = {10, 20, 30};
// int[] 不能直接傳給 Arrays.asList,需要手動(dòng)包裝
List<Integer> intList = Arrays.stream(intArray).boxed().collect(Collectors.toList());
System.out.println("int[] 轉(zhuǎn) List: " + intList);
// 自定義對(duì)象數(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 中的一個(gè)收集器,用于將流中的元素收集到一個(gè) 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ù)雜對(duì)象轉(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)部機(jī)制與特點(diǎn)
Collectors.toList() 返回的是一個(gè) Collector,它會(huì)將流中的元素收集到一個(gè) java.util.ArrayList 中。這個(gè) ArrayList 是一個(gè)標(biāo)準(zhǔn)的、可變的 List 實(shí)現(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 類(lèi)型: " + originalList.getClass().getName()); // 輸出: java.util.Arrays$ArrayList
System.out.println("新 List 類(lèi)型: " + 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]
// 可以進(jìn)行各種 List 操作
newList.remove("a");
Collections.sort(newList);
System.out.println("排序后的 List: " + newList); // 輸出: [b, c, d]
}
}
? 優(yōu)點(diǎn):
- 可變性:返回的是標(biāo)準(zhǔn)的
java.util.ArrayList,可以自由地進(jìn)行add、remove、set等操作。 - 靈活性:可以方便地與其他
Stream操作結(jié)合,如filter,map,sorted等。 - 通用性:返回的是標(biāo)準(zhǔn)的
List接口實(shí)現(xiàn),可以用于任何需要List的地方。
3.3 使用 Collectors.toList() 的注意事項(xiàng)
- 性能開(kāi)銷(xiāo):
Stream的創(chuàng)建和處理會(huì)有一定的性能開(kāi)銷(xiāo),對(duì)于簡(jiǎn)單轉(zhuǎn)換,直接使用new ArrayList<>(sourceList)可能更快。 - 內(nèi)存占用:
Collectors.toList()會(huì)創(chuàng)建一個(gè)新的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() 后再?gòu)?fù)制(注意:這種方式是創(chuàng)建副本)
List<String> list3 = new ArrayList<>(Arrays.asList(sourceList.toArray()));
// 方式 4: 手動(dòng)循環(huán)(對(duì)于大型列表,可能更慢)
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);
// 驗(yàn)證是否為新對(duì)象
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)的全面對(duì)比
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. 無(wú)參 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]); // 傳入長(zhǎng)度為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. 基本類(lèi)型數(shù)組轉(zhuǎn)換 (需要手動(dòng)處理)
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ù)組會(huì)影響 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);
// 可以進(jìn)行鏈?zhǔn)讲僮?
List<String> filteredList = Arrays.stream(array)
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
System.out.println("過(guò)濾后: " + filteredList); // 輸出: [dog, bird]
// 轉(zhuǎn)換為不同類(lèi)型的 List
List<Integer> lengths = Arrays.stream(array)
.map(String::length)
.collect(Collectors.toList());
System.out.println("長(zhǎng)度列表: " + lengths); // 輸出: [3, 3, 4]
// 基本類(lèi)型數(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 性能對(duì)比與選擇建議
讓我們通過(guò)一個(gè)簡(jiǎn)單的性能測(cè)試來(lái)比較不同方法的效率。
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;
}
// 測(cè)試 1: Arrays.asList + new ArrayList
long start = System.nanoTime();
List<Integer> list1 = new ArrayList<>(Arrays.asList(array));
long time1 = System.nanoTime() - start;
// 測(cè)試 2: Stream + Collectors.toList
start = System.nanoTime();
List<Integer> list2 = Arrays.stream(array).collect(Collectors.toList());
long time2 = System.nanoTime() - start;
// 測(cè)試 3: 直接構(gòu)造 ArrayList (最高效)
start = System.nanoTime();
List<Integer> list3 = new ArrayList<>(Arrays.asList(array));
long time3 = System.nanoTime() - start;
// 測(cè)試 4: 手動(dòng)循環(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");
// 驗(yàn)證結(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é)論:
- 簡(jiǎn)單轉(zhuǎn)換:直接使用
new ArrayList<>(Arrays.asList(array))或new ArrayList<>(sourceList)通常是最快的。 - 復(fù)雜操作:如果需要在轉(zhuǎn)換過(guò)程中進(jìn)行過(guò)濾、映射等操作,Stream API 的
Collectors.toList()更具優(yōu)勢(shì)和可讀性。 - 內(nèi)存考慮:
Arrays.asList()返回的是視圖,不會(huì)創(chuàng)建新數(shù)組;而Collectors.toList()和new ArrayList()會(huì)創(chuàng)建新的ArrayList。
五、實(shí)際應(yīng)用場(chǎng)景
5.1 API 接口兼容
假設(shè)你需要調(diào)用一個(gè)老 API,它期望接收一個(gè) 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ù)處理場(chǎng)景中,經(jīng)常需要將數(shù)據(jù)從一種形式轉(zhuǎn)換為另一種形式。
import java.util.*;
import java.util.stream.Collectors;
public class DataProcessingExample {
public static void main(String[] args) {
// 假設(shè)有一個(gè)訂單列表
List<Order> orders = Arrays.asList(
new Order("A001", 100.0),
new Order("A002", 200.0),
new Order("A003", 150.0)
);
// 1. 獲取所有訂單號(hào)
List<String> orderNumbers = orders.stream()
.map(Order::getOrderNumber)
.collect(Collectors.toList());
System.out.println("訂單號(hào): " + orderNumbers);
// 2. 獲取所有訂單金額
List<Double> amounts = orders.stream()
.map(Order::getAmount)
.collect(Collectors.toList());
System.out.println("訂單金額: " + amounts);
// 3. 過(guò)濾金額大于 150 的訂單
List<Order> highValueOrders = orders.stream()
.filter(o -> o.getAmount() > 150)
.collect(Collectors.toList());
System.out.println("高價(jià)值訂單: " + highValueOrders);
// 4. 將訂單轉(zhuǎn)換為數(shù)組 (例如,傳遞給某個(gè)需要數(shù)組的函數(shù))
Order[] orderArray = orders.toArray(new Order[0]);
System.out.println("訂單數(shù)組長(zhǎng)度: " + orderArray.length);
// 5. 轉(zhuǎn)換為 Map (鍵為訂單號(hào),值為金額)
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 配置管理
在配置文件處理中,有時(shí)需要將配置項(xiàng)轉(zhuǎn)換為數(shù)組或列表。
import java.util.*;
import java.util.stream.Collectors;
public class ConfigManagementExample {
public static void main(String[] args) {
// 模擬配置項(xiàng)(例如,逗號(hào)分隔的字符串)
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("配置項(xiàng) List: " + configList);
// 轉(zhuǎn)換為數(shù)組
String[] configArray = configString.split(",");
String[] trimmedArray = Arrays.stream(configArray)
.map(String::trim)
.toArray(String[]::new);
System.out.println("配置項(xiàng)數(shù)組: " + Arrays.toString(trimmedArray));
// 如果需要對(duì)數(shù)組進(jìn)行進(jìn)一步處理
String[] upperCaseArray = configList.stream()
.map(String::toUpperCase)
.toArray(String[]::new);
System.out.println("大寫(xiě)配置項(xiàng)數(shù)組: " + Arrays.toString(upperCaseArray));
}
}
六、常見(jiàn)問(wèn)題與解決方案
6.1 Arrays.asList 返回的 List 不可變
這是最常見(jiàn)的問(wèn)題之一。
問(wèn)題:
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("錯(cuò)誤: " + 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 基本類(lèi)型數(shù)組轉(zhuǎn)換為 List 的陷阱
使用 Arrays.asList() 時(shí),基本類(lèi)型數(shù)組會(huì)被當(dāng)作一個(gè)對(duì)象處理。
import java.util.*;
public class PrimitiveArrayTrap {
public static void main(String[] args) {
// 錯(cuò)誤方式:int[] 會(huì)被當(dāng)作單個(gè)對(duì)象
int[] intArray = {1, 2, 3};
List<int[]> wrongList = Arrays.asList(intArray); // List<int[]>
System.out.println("錯(cuò)誤方式的結(jié)果: " + wrongList); // 輸出: [[I@...]
System.out.println("長(zhǎng)度: " + 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)存
對(duì)于大量數(shù)據(jù)的轉(zhuǎn)換,Stream API 會(huì)創(chuàng)建中間對(duì)象,可能帶來(lái)性能和內(nèi)存開(kāi)銷(xiāo)。
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);
}
// 對(duì)于大型列表,直接轉(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)造時(shí)間: " + time1 / 1_000_000 + " ms");
System.out.println("Stream 時(shí)間: " + time2 / 1_000_000 + " ms");
// 但 Stream 在鏈?zhǔn)讲僮鲿r(shí)更有優(yōu)勢(shì)
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(shí)間: " + time3 / 1_000_000 + " ms");
}
}
七、可視化:List 與數(shù)組轉(zhuǎn)換流程
為了更直觀地理解 List 與數(shù)組之間的轉(zhuǎn)換關(guān)系,下面是一個(gè)使用 Mermaid 繪制的流程圖,展示了主要的轉(zhuǎn)換路徑和特點(diǎ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
八、最佳實(shí)踐與總結(jié)
8.1 選擇合適的轉(zhuǎn)換方式
根據(jù)具體需求選擇最適合的轉(zhuǎn)換方式:
- 簡(jiǎn)單復(fù)制:如果只是想復(fù)制一個(gè)
List或者從數(shù)組創(chuàng)建一個(gè)可變的List,直接使用new ArrayList<>(source)或new ArrayList<>(Arrays.asList(array))。 - 需要鏈?zhǔn)讲僮?/strong>:如果需要在轉(zhuǎn)換過(guò)程中進(jìn)行過(guò)濾、映射等操作,使用
Stream和Collectors.toList()。 - API 兼容性:如果需要將
List傳遞給一個(gè)期望數(shù)組的 API,使用list.toArray(new T[0])。 - 性能敏感場(chǎng)景:對(duì)于大量數(shù)據(jù)的簡(jiǎn)單轉(zhuǎn)換,避免不必要的 Stream 開(kāi)銷(xiāo),直接使用構(gòu)造函數(shù)或
toArray()。
8.2 注意事項(xiàng)
- 類(lèi)型安全:使用泛型確保類(lèi)型安全。
- 不可變性:注意
Arrays.asList()返回的List是不可變的,如果需要修改,需要?jiǎng)?chuàng)建副本。 - 基本類(lèi)型:處理基本類(lèi)型數(shù)組時(shí),注意
Arrays.asList()會(huì)將其視為單個(gè)對(duì)象,需要使用Stream的boxed()方法。 - 內(nèi)存使用:頻繁的轉(zhuǎn)換操作會(huì)增加內(nèi)存開(kāi)銷(xiāo),特別是在大數(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ù)組(例如,來(lái)自數(shù)據(jù)庫(kù)查詢結(jié)果)
String[] externalData = {"user1", "user2", "user3", "user4"};
// 2. 轉(zhuǎn)換為 List 以便進(jìn)行復(fù)雜的處理(例如,過(guò)濾、排序)
List<String> userList = Arrays.stream(externalData)
.filter(name -> !name.startsWith("user3")) // 過(guò)濾掉 user3
.sorted() // 排序
.collect(Collectors.toList());
System.out.println("處理后的用戶列表: " + userList);
// 3. 生成報(bào)告或發(fā)送給 API(需要數(shù)組)
String[] reportArray = userList.toArray(new String[0]);
System.out.println("報(bào)告數(shù)組: " + Arrays.toString(reportArray));
// 4. 保存到另一個(gè)數(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. 處理基本類(lèi)型數(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. 計(jì)算平均分
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ù)時(shí),選擇合適的轉(zhuǎn)換方法對(duì)性能和內(nèi)存使用至關(guān)重要。
9.1 內(nèi)存使用分析
Arrays.asList():不創(chuàng)建新數(shù)組,僅創(chuàng)建一個(gè)視圖,內(nèi)存開(kāi)銷(xiāo)最小。new ArrayList<>(Arrays.asList(array)):創(chuàng)建一個(gè)ArrayList和一個(gè)Arrays.ArrayList視圖,內(nèi)存開(kāi)銷(xiāo)略高。new ArrayList<>(sourceList):創(chuàng)建一個(gè)新的ArrayList,內(nèi)存開(kāi)銷(xiāo)較高。Stream + Collectors.toList():創(chuàng)建Stream對(duì)象和新的ArrayList,內(nèi)存開(kāi)銷(xiāo)較大。
9.2 性能基準(zhǔn)測(cè)試
import java.util.*;
import java.util.stream.Collectors;
public class PerformanceBenchmark {
public static void main(String[] args) {
// 準(zhǔn)備測(cè)試數(shù)據(jù)
int size = 1000000;
Integer[] testData = new Integer[size];
for (int i = 0; i < size; i++) {
testData[i] = i;
}
// 測(cè)試方法 1: new ArrayList<>(Arrays.asList(array))
long start = System.nanoTime();
List<Integer> list1 = new ArrayList<>(Arrays.asList(testData));
long time1 = System.nanoTime() - start;
// 測(cè)試方法 2: new ArrayList<>(Arrays.asList(array)) (直接從數(shù)組)
start = System.nanoTime();
List<Integer> list2 = new ArrayList<>(Arrays.asList(testData));
long time2 = System.nanoTime() - start;
// 測(cè)試方法 3: Stream + Collectors.toList()
start = System.nanoTime();
List<Integer> list3 = Arrays.stream(testData).collect(Collectors.toList());
long time3 = System.nanoTime() - start;
// 測(cè)試方法 4: 手動(dòng)循環(huán)
start = System.nanoTime();
List<Integer> list4 = new ArrayList<>();
for (Integer i : testData) {
list4.add(i);
}
long time4 = System.nanoTime() - start;
// 測(cè)試方法 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 (手動(dòng)循環(huán)): " + time4 / 1_000_000 + " ms");
System.out.println("方法 5 (asList + Stream): " + time5 / 1_000_000 + " ms");
// 驗(yàn)證結(jié)果一致性
System.out.println("所有結(jié)果大小相同: " + (list1.size() == list2.size() && list2.size() == list3.size() && list3.size() == list4.size() && list4.size() == list5.size()));
}
}
性能結(jié)論:
- 對(duì)于簡(jiǎn)單復(fù)制,
new ArrayList<>(Arrays.asList(array))通常是最快的。 - 對(duì)于需要鏈?zhǔn)讲僮鞯膱?chǎng)景,
Stream提供了更好的可讀性和功能。 manual loop通常是最慢的,但有時(shí)在特定條件下可能有優(yōu)勢(shì)(例如,循環(huán)體內(nèi)有復(fù)雜的邏輯)。
十、總結(jié)
List 與數(shù)組之間的轉(zhuǎn)換是 Java 開(kāi)發(fā)中的常見(jiàn)任務(wù)。本文詳細(xì)介紹了兩種核心方法:Arrays.asList() 和 Collectors.toList()。
Arrays.asList():快速、輕量級(jí)地將數(shù)組包裝為List,但返回的List是固定大小的,且底層共享數(shù)組。適用于只需要查看數(shù)據(jù)、不修改大小的場(chǎng)景,或者作為臨時(shí)視圖。Collectors.toList():通過(guò)StreamAPI 將數(shù)據(jù)收集到一個(gè)新的ArrayList中,返回一個(gè)完全可變的、標(biāo)準(zhǔn)的List。適用于需要后續(xù)修改、過(guò)濾、排序等操作的場(chǎng)景。
選擇哪種方式取決于具體的業(yè)務(wù)需求、性能要求和代碼的可讀性。記住關(guān)鍵點(diǎn):
- 明確轉(zhuǎn)換目的:是僅僅為了遍歷?還是需要修改?
- 注意
Arrays.asList()的限制:它返回的List不支持add、remove等操作。 - 處理基本類(lèi)型數(shù)組:需要使用
Stream和boxed()。 - 考慮性能:對(duì)于大量數(shù)據(jù),簡(jiǎn)單復(fù)制比復(fù)雜 Stream 操作更高效。
- 利用 Stream 的強(qiáng)大功能:在需要鏈?zhǔn)教幚頃r(shí),Stream API 提供了優(yōu)雅的解決方案。
掌握這些轉(zhuǎn)換技巧,可以讓你在處理數(shù)據(jù)時(shí)更加得心應(yīng)手,構(gòu)建出更健壯、高效的 Java 應(yīng)用程序。
以上就是Java實(shí)現(xiàn)List與數(shù)組互相轉(zhuǎn)換的兩種方法的詳細(xì)內(nèi)容,更多關(guān)于Java List與數(shù)組互轉(zhuǎn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
RestTemplate接口調(diào)用神器常見(jiàn)用法匯總
這篇文章主要介紹了RestTemplate接口調(diào)用神器常見(jiàn)用法匯總,通過(guò)案例代碼詳細(xì)介紹RestTemplate接口調(diào)用神器常見(jiàn)用法,需要的朋友可以參考下2022-07-07
集合框架(Collections Framework)詳解及代碼示例
這篇文章主要介紹了集合框架(Collections Framework)詳解及代碼示例,文章涉及集合數(shù)組的區(qū)別,collection接口,iterator迭代器,list接口及其用法,LinkedHashSet集合等有關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
java實(shí)現(xiàn)肯德基收銀系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)肯德基收銀系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
java Socket無(wú)法完全接收返回內(nèi)容的解決方案
這篇文章主要介紹了java Socket無(wú)法完全接收返回內(nèi)容的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
解決異常FileNotFoundException:class path resource找不到資源文件的問(wèn)題
今天小編就為大家分享一篇關(guān)于解決異常FileNotFoundException:class path resource找不到資源文件的問(wèn)題,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
elasticsearch索引index之Mapping實(shí)現(xiàn)關(guān)系結(jié)構(gòu)示例
這篇文章主要介紹了elasticsearch索引index之Mapping實(shí)現(xiàn)關(guān)系結(jié)構(gòu)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Spring動(dòng)態(tài)多數(shù)據(jù)源配置實(shí)例Demo
本篇文章主要介紹了Spring動(dòng)態(tài)多數(shù)據(jù)源配置實(shí)例Demo,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
SpringBoot @Validated注解實(shí)現(xiàn)參數(shù)分組校驗(yàn)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于SpringBoot @Validated注解實(shí)現(xiàn)參數(shù)分組校驗(yàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

