关于Arrays类中asList(T... a)泛型参数辨析

前提

我们需要知道两点

(1)T指的是泛型类型,它只能是引用类型,何为引用类型?在java中除了基本数据类型(如byte、short、int、long、float、double、boolean、char)之外的所有类型都是引用类型。引用类型包括类(class)、接口(interface)、数组(array)等

(2)我们asList返回值是一个List<T>类型,也就是说我们传递什么T最后返回的List中元素类型就是什么T类型,比如我们传递一个Integer 那么List中元素就是Integer,我们传递一个int \[\] 那么我们list中元素就是int\[\],有的同志就要问了,为什么传递过去的事int \[\] 泛型T不会识别为int ,刚才我们说了泛型只能是引用类型,但是我们int是基本类型,不会识别为引用类型,所以我们参数接收的时候直接就将int \[\] 识别成了引用类型,所以我们返回的结果List<int\[\]>就是这种类型的。

实验

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

int\[\] a = {2,3,4,5};

Integer\[\] b = {2,3,4,5};

List listA = Arrays.asList(a);

List listA1 = Arrays.asList(2,3,4,5);

List listB = Arrays.asList(b);

System.out.println(listA.size());

System.out.println(listA1.size());

System.out.println(listB.size());

System.out.println("ListA元素类型:"+listA.get(0).getClass());

System.out.println("ListA元素:"+Arrays.toString((int\[\]) listA.get(0)));

System.out.println("ListA1元素类型:"+listA1.get(0).getClass());

System.out.println("ListB元素类型:"+listB.get(0).getClass());

}

运行结果

我们可以看到我们传递过去的int \[\] array数组,接受参数确实将int \[\] 识别为了T,也就是说我们List中元素是int\[\] ,所以我们的元素个数是1。

对于我们传递过去的Arrays.asList(2,3,4,5);这个可变参数我们没有指定类型,但是java会自动装箱操作,将这个几个参数自动封装为Integer类型。

补充

上面都是一维数组,那么如果是二维数组又是什么情况呢?

我们以Arrays中的toArray方法为例

public Object\[\] toArray() {

return Arrays.copyOf(elementData, size);

}

@SuppressWarnings("unchecked")

public <T> T\[\] toArray(T\[\] a) {

if (a.length < size)

// 新建一个运行时类型的数组,但是ArrayList数组的内容

return (T\[\]) Arrays.copyOf(elementData, size, a.getClass());

//调用System提供的arraycopy()方法实现数组之间的复制

System.arraycopy(elementData, 0, a, 0, size);

if (a.length > size)

asize = null;

return a;

class Solution {

public int\[\]\[\] reconstructQueue(int\[\]\[\] people) {

Arrays.sort(people,(a,b)->{

if(a0==b0) return a1-b1;

return b0-a0;

});

LinkedList<int\[\]> que = new LinkedList<>();

for (int\[\] p : people) {

que.add(p1,p);

}

return que.toArray(new intpeople.length\[\]);

}

}

实际上我们传递过去的二维数组int\[\]\[\] ,我们参数中泛型T识别的是int\[\] ,然后传递到源码中的a实际上就是我们int\[\] 类型的一维数组,这个可以这样理解:int32 实际上是 int3 {int2, int2, int2}。

然后返回值直接就是T\[\] 就是int3\[\]。

下面这个例子更加好理解一些。

List<int\[\]> res = new ArrayList<>();

res.add(new int\[\]{100});

int\[\]\[\] a = res.toArray(new int0\[\]);

System.out.println(a00);

//成功运行并输出

100

其他问题

2.asList()方法返回对象使用add()方法抛出异常

先进行如下测试:

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

List<String> pets = Arrays.asList("tiger","mice");

pets.add("lion");

}

结果如下:

我们这里可以看到他抛出了一个异常,难道add方法不是List的基本用法吗?我们继续扒一下源码。

原来原来Arrays的asList方法使用的ArrayList类是一个内部定义的类,而不是java.util.ArrayList类。其部分源码如下:

private static class ArrayList<E> extends AbstractList<E>

implements RandomAccess, java.io.Serializable

{

@java.io.Serial

private static final long serialVersionUID = -2764017481108945198L;

@SuppressWarnings("serial") // Conditionally serializable

private final E\[\] a;

ArrayList(E\[\] array) {

a = Objects.requireNonNull(array);

}

....

}

我们可以看到这是一个静态内部类,存储数组元素的a变量是final类型的,由此判断,这个静态内部类是不能做任何内部元素的添加删除操作的!就跟String类一样,String对象存储字符数组的变量也是有final修饰符的。因为一旦增加数组元素,这个数组容量已经定好的容器就无法装载增加的元素了。

内部类里面并没有add,remove方法,我们可以看下这个类继承的AbstractList类里面对这些方法的实现:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

........

public void add(int index, E element) {

throw new UnsupportedOperationException();

}

public E remove(int index) {

throw new UnsupportedOperationException();

}

}

找到异常的来源了,我们使用asList得到的对象add、remove方法直接就是抛出异常。那么我们如果要想对asList得到的对象使用add、remove方法,该怎么办呢?

List<String> pets = new ArrayList<String>(Arrays.asList("a", "b", "c"));

可以将传入的参数转换为一个ArrayList对象返回。

3.通过Arrays.asList方法将数组转成集合后,使用set方法修改元素,为什么原来的数组的值也会改变?

测试:

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

String\[\] strings={"A","B","C"};

List<String> stringList=Arrays.asList(strings);

stringList.set(0,"G");

System.out.println(stringList);

System.out.println(strings0);

}

结果:

​我们可以看到集合的值和原数组的第一个值都被修改了,为什么原数组也会被修改呢?

原因是静态内部类ArrayList的成员变量a使用了final,用于存储集合的数组引用始终被强制指向原有数组。

所以原数组也会被修改。

​那么怎么在不修改原数组的基础上修改集合呢?我们同样可以采取将传入的参数转换为一个ArrayList对象返回的方式。

参考链接:https://blog.csdn.net/qq_51634677/article/details/131223655

相关推荐
郑洁文6 分钟前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
螺丝钉code1 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
z落落2 小时前
C# 四种特殊类:抽象类、密封类、静态类、部分类
开发语言·c#
摇滚侠2 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea
VidDown2 小时前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
折哥的程序人生 · 物流技术专研2 小时前
Java 23 种设计模式:从踩坑到精通 | 原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?
java·设计模式·架构·原型模式·单一职责原则
装不满的克莱因瓶3 小时前
基于 OpenResty 扩展开发实现动态服务注册与发现能力
java·开发语言·架构·openresty
程序员小羊!3 小时前
06Java 异常机制与常用类
java
weixin_523185323 小时前
Java基础知识总结(四):引用数据类型与参数传递机制
java·开发语言·python
Nayxxu3 小时前
Claude API 生产稳定性设计:超时、降级、备用模型和告警怎么做
开发语言·php