前言
数组是Java三大引用数据类型之一,也是最简单的复合数据类型。
在实际开发中,我们总会用到Collection接口来提高编码效率,能够灵活的操作数组与实现集合接口的类间的转换是Java程序员的基本功之一。本文仅涉及Java8及之前的内容。
Collections Framework
Java集合框架(JCF)包含集合接口、一系列实现以及其他内容,其中 Collection接口 分为两组。
最基本的接口java.util.Collection
具有以下后代:
- java.util.Set
- java.util.SortedSet
- java.util.NavigableSet
- java.util.Queue
- java.util.concurrent.BlockingQueue
- java.util.concurrent.TransferQueue
- java.util.Deque
- java.util.concurrent.BlockingDeque
其他 Collection接口 基于java.util.Map
而不是真正的集合。但是,这些接口包含 collection-view(集合视图) 操作,这使它们能够作为集合进行操作。Map
有以下后代:
- java.util.SortedMap
- java.util.NavigableMap
- java.util.concurrent.ConcurrentMap
- java.util.concurrent.ConcurrentNavigableMap
实现集合接口的类通常具有 < Implementation-style > < Interface >
形式的名称。下表总结了通用实现:
Interface | Hash Table | Resizable Array | Balanced Tree | Linked List | Hash Table + Linked List |
---|---|---|---|---|---|
Set | HashSet | TreeSet | LinkedHashSet | ||
List | ArrayList | LinkedList | |||
Deque | ArrayDeque | LinkedList | |||
Map | HashMap | TreeMap | LinkedHashMap |
java.util.Arrays
Arrays类包含用于操作数组(例如排序和搜索)的各种方法,还包含一个允许将数组视为列表的静态工厂。
这个类实际上是JCF的一部分。
数组转List
这里介绍四种方法,也是最常见的一些方法。
方法一:Arrays.asList()
该方法返回由指定数组支持的固定大小列表。(对返回列表的更改会直接写入到数组。)
此方法充当基于数组和基于集合的 API 之间的桥梁,并与
Collection.toArray()
结合使用。返回的列表是可序列化的,并实现随机存取。
在使用这个方法时需要注意两个要点。首先,该方法仅支持引用类型数组(如Integer、Double、String…)转换到列表。其次,转换到列表后,不能对其进行增删,只能查改。
这是因为Arrays.asList()
返回值是 java.util.Arrays
类中一个私有静态内部类 java.util.Arrays.ArrayList
,它并非 java.util.ArrayList
类。java.util.Arrays.ArrayList
类具有 set(),get(),contains()
等方法,但是不具有添加add()
或删除remove()
方法。
下面我们来写一点代码进行实践,首先让我们初始化两个数组,一个基本数据类型数组unboxed
和一个包装类数组boxed
:
1 | // author: SilenceZheng66 |
下面我们先来尝试操作基本数据类型数组转换为列表:
1 | // author: SilenceZheng66 |
此时会报错:java: 不兼容的类型: int[]无法转换为int
。这是因为asList
方法将int[] unboxed
的类型看为int[]
,则只接收到了一个元素unboxed
,并没有像我们希望的将数组中的元素视为最小组成。于是我们可以修改为:
1 | // author: SilenceZheng66 |
此时会运行成功,输出[1, 1, 1, 1, 1]
,虽然解决了运行时错误,但这不是我们希望的结果。下面我们来尝试使用包装类数组boxed
作为参数:
1 | // author: SilenceZheng66 |
此时程序会逐个输出数组内的元素,并且数组内元素已经被增加1,说明自动拆箱工作正常,并且对于列表的修改直接写入到了原数组,得到了我们想要的结果。
同时,在Java8中,我们可以通过Lambda表达式与函数式接口的配合实现更简单的“把列表元素自增1”的操作:list.replaceAll(integer -> integer + 1);
方法二:java.util.ArrayList
这种方法是对于Arrays.asList()
的延伸,相当于将该方法产生的返回值由java.util.Arrays.ArrayList
转为java.util.ArrayList
。新产生的ArrayList与原数组无关,是一块新的内存空间。
1 | // author: SilenceZheng66 |
程序输出1 2
,说明list
与原数组boxed
无关。
方法三:Collections.addAll()
Collections类是JCF的成员,包含对集合进行操作或返回集合的静态方法。
该方法与方法二类似,但性能最高。首先根据数组的长度创建一个长度相同的列表,然后通过Collections.addAll()
方法,将数组中的元素转为二进制,然后添加到列表中。
1 | // author: SilenceZheng66 |
注意该方法无法操作基本类型数组。
方法四:Stream
Java8加入的特性,通过流式API操作集合。通过该方法可以直接对基本类型数组进行操作。
1 | // author: SilenceZheng66 |
List转数组
除了循环赋值(效率低)外,提供两种方法作为参考。
方法一:List.toArray()
List接口下的toArray()
方法存在重载,即 Object[] toArray();
和 <T> T[] toArray(T[] a);
。 当我们使用无参形式时,返回的值为Object数组,那么我们后面需要手动进行类型转换:
1 | // author: SilenceZheng66 |
以上写法会报错:java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
。说明Object数组不能简单的转化为其他类数组,于是我们应该逐个转换:
1 | // author: SilenceZheng66 |
逐个转换当然也可以应用自动装箱和拆箱,于是可以与基本类型数组做转换。
另一种方式就是采用含参的toArray()
,于是只能转换到类数组。
1 | // author: SilenceZheng66 |
以上三行写法效果是一样的。
方法二:Stream
使用流API做数组转换,支持转到类数组和基本类型数组:
1 | // author: SilenceZheng66 |
其他Collection
最常用的转换应该就是数组与列表(List)间,但数组与集合间的转换不止于此。
以Set为例,我们可以这样构造一个HashSet:
1 | // author: SilenceZheng66 |
输出结果为[1, 2]
,这也是对类数组去重的一种方式。
参考文献
[1] https://blog.csdn.net/qq_37132495/article/details/121304797
后记
首发于 silencezheng.top,转载请注明出处。