java array map_Java中Array、List、Set、Map

本文详细介绍了Java中Arrays.asList方法在转换基本类型数组时的陷阱,由于基本类型数组不能作为泛型参数,导致转换后的列表只有一个元素。此外,asList返回的列表是不可变的,不支持添加、删除等操作。开发者需要注意这些细节以避免逻辑错误,特别是在处理列表长度和修改操作时。
摘要由CSDN通过智能技术生成

建议65:避开基本类型数组转换列表陷阱

我们在开发中经常会使用Arrays和Collections这两个工具类和列表之间转换,非常方便,但也有时候会出现一些奇怪的问题,来看如下代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public class Client65 {

2 public static void main(String[] args) {

3 int data [] = {1,2,3,4,5};

4 List list= Arrays.asList(data);

5 System.out.println("列表中的元素数量是:"+list.size());

6 }

7 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

也许你会说,这很简单,list变量的元素数量当然是5了。但是运行后打印出来的列表数量为1。

事实上data确实是一个有5个元素的int类型数组,只是通过asList转换成列表后就只有一个元素了,这是为什么呢?其他4个元素到什么地方去了呢?

我们仔细看一下Arrays.asList的方法说明:输入一个变长参数,返回一个固定长度的列表。注意这里是一个变长参数,看源码:

public static List asList(T... a) {

return new ArrayList<>(a);

}

asList方法输入的是一个泛型变长参数,我们知道基本类型是不能泛型化的,也就是说8个基本类型不能作为泛型参数,要想作为泛型参数就必须使用其所对应的包装类型,那前面的例子传递了一个int类型的数组,为何程序没有报编译错误呢?

在Java中,数组是一个对象,它是可以泛型化的,也就是说我们的例子是把一个int类型的数组作为了T的类型,所以在转换后在List中就只有一个类型为int数组的元素了,我们打印出来看看,代码如下:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public class Client65 {

2 public static void main(String[] args) {

3 int data [] = {1,2,3,4,5};

4 List list= Arrays.asList(data);

5 System.out.println("元素类型是:"+list.get(0).getClass());

6 System.out.println("前后是否相等:"+data.equals(list.get(0)));

7 }

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

输出的结果是: 元素类型是:class [I   前后是否相等:true

很明显,放在列表中的元素时一个int数组,可能有人要问了,为什么"元素类型:"后的class是"[I"?我们并没有指明是数组(Array)类型呀!这是因为JVM不可能输出Array类型,因为Array是属于java.lang.reflect包的,它是通过反射访问数组元素的工具类。在Java中任何一个一维数组的类型都是 " [I " ,究其原因就是Java并没有定义数组这一个类,它是在编译器编译的时候生成的,是一个特殊的类,在JDK的帮助中也没有任何数组类的信息。

弄清楚了问题,修改也很easy,直接使用包装类即可,部分代码如下:

Integer data [] = {1,2,3,4,5};

把int替换为Integer即可让输出元素数量为5.需要说明的是,不仅仅是int类型的数组有这个问题,其它7个基本类型的数组也存在相似的问题,这就需要大家注意了,在把基本类型数组转换为列表时,要特别小心asList方法的陷阱,避免出现程序逻辑混乱的情况。

注意:原始类型数组不能作为asList的输入参数,否则会引起程序逻辑混乱。

建议66:asList方法产生的List的对象不可更改

上一个建议指出了asList方法在转换基本类型数组时存在的问题,接着我们看一下asList方法返回的列表有何特殊的地方,代码如下:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public class Client66 {

2 public static void main(String[] args) {

3 // 五天工作制

4 Week days[] = { Week.Mon, Week.Tue, Week.Wed, Week.Thu, Week.Fri };

5 // 转换为列表

6 List list = Arrays.asList(days);

7 // 增加周六为工作日

8 list.add(Week.Sat);

9 /* do something */

10 }

11 }

12 enum Week {

13 Sun, Mon, Tue, Wed, Thu, Fri, Sat

14 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

很简单的程序呀,默认声明的工作日(workDays)是从周一到周五,偶尔周六也会算作工作日加入到工作日列表中,不过,这段程序执行时会不会有什么问题呢?编译没有任何问题,但是一运行,却出现了如下结果:

e46d9f3af359fb82af103b47a4f7bb68.png

UnsupportedOperationException,不支持的操作,居然不支持list的add方法,这是什么原因呢?我们看看asList方法的源代码:

public static List asList(T... a) {

return new ArrayList<>(a);

}

直接new了一个ArrayList对象返回,难道ArrayList不支持add方法,不可能呀!可能,问题就出现在这个ArrayList类上,此ArrayList非java.util.ArrayList,而是Arrays工具类的一个内部类,其构造函数如下所示:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 private static class ArrayList extends AbstractList

2 implements RandomAccess, java.io.Serializable

3 {

4 private static final long serialVersionUID = -2764017481108945198L;

5 private final E[] a;

6

7 ArrayList(E[] array) {

8 if (array==null)

9 throw new NullPointerException();

10 a = array;

11 }

12 /*其它方法略*/

13 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

这个ArrayList是一个静态私有内部类,除了Arrays能访问外,其它类都不能访问,仔细看这个类,它没有提供add方法,那肯定是父类AbstractList提供了,来看代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public boolean add(E e) {

2 add(size(), e);

3 return true;

4 }

5

6 public void add(int index, E element) {

7 throw new UnsupportedOperationException();

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

父类确实提供了,但没有提供具体的实现,所以每个子类都需要自己覆写add方法,而Arrays的内部类ArrayList没有覆写,因此add一个元素就报错了。

我们深入地看看这个ArrayList静态内部类,它仅仅实现了5个方法:

size:元素数量

get:获得制定元素

set:重置某一元素值

contains:是否包含某元素

toArray:转化为数组,实现了数组的浅拷贝

把这几个方法的源代码展示一下:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 //元素数量

2 public int size() {

3 return a.length;

4 }

5

6 public Object[] toArray() {

7 return a.clone();

8 }

9 //转化为数组,实现了数组的浅拷贝

10 public T[] toArray(T[] a) {

11 int size = size();

12 if (a.length < size)

13 return Arrays.copyOf(this.a, size,

14 (Class extends T[]>) a.getClass());

15 System.arraycopy(this.a, 0, a, 0, size);

16 if (a.length > size)

17 a[size] = null;

18 return a;

19 }

20 //获得指定元素

21 public E get(int index) {

22 return a[index];

23 }

24 //重置某一元素

25 public E set(int index, E element) {

26 E oldValue = a[index];

27 a[index] = element;

28 return oldValue;

29 }

30

31 public int indexOf(Object o) {

32 if (o==null) {

33 for (int i=0; i

34 if (a[i]==null)

35 return i;

36 } else {

37 for (int i=0; i

38 if (o.equals(a[i]))

39 return i;

40 }

41 return -1;

42 }

43 //是否包含元素

44 public boolean contains(Object o) {

45 return indexOf(o) != -1;

46 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

对于我们经常使用list.add和list.remove方法它都没有实现,也就是说asList返回的是一个长度不可变的列表,数组是多长,转换成的列表也就是多长,换句话说此处的列表只是数组的一个外壳,不再保持列表的动态变长的特性,这才是我们关注的重点。有些开发人员喜欢这样定义个初始化列表:

List names= Arrays.asList("张三","李四","王五");

一句话完成了列表的定义和初始化,看似很便捷,却隐藏着重大隐患---列表长度无法修改。想想看,如果这样一个List传递到一个允许添加的add操作的方法中,那将会产生何种结果,如果有这种习惯的javaer,请慎之戒之,除非非常自信该List只用于只读操作。

针对ArrayList,使用高级for循环实质是通过Iterator来循环效率比基本for循环遍历元素稍差,在数据较多要用到性能时就不能为了简单用高级for循环。

频繁插入和删除元素时用LinkList,修改元素操作多时用ArrayList。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值