Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析

问题引出

Iterable接口下的forEach()方法可以用来遍历集合内的元素,示例如下:

import java.util.*;
public class Test {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(10);
        list.add(6);
        list.add(11);
        list.add(1);
        list.add(20);
        System.out.println("使用List的foreach方法遍历List");
        list.forEach(System.out::println);
    }
}

运行结果:Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第1张图片

Arrays工具类下的asList()方法可以将数组转换成List集合,尝试使用Arrays.asList(数组).foreach()来遍历数组。

1.被转换的数组是基本数据类型的数组(以int[]为例)

import java.util.*;
public class Test {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        Arrays.asList(array).forEach(System.out::println);
    }
}

运行结果:Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第2张图片

2.被转换的数组是引用类型的数组(以String[]为例)

import java.util.*;
public class Test {
    public static void main(String[] args) {
        String[] array = {"aaa","bbb","ccc","ddd","eee"};
        Arrays.asList(array).forEach(System.out::println);
    }
}

 运行结果:Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第3张图片

 总结上述运行结果发现:基本数据类型的数组通过Arrays.asList()转换为List集合后遍历返回的是一个数组的地址值。引用数据类型通过这种方式转换为List集合后才能够遍历其中的元素。

补充:而且基本数据类型数组的结果就是原数组的地址

Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第4张图片

 

问题分析

查看Arrays.asList()的源码:Arrays.asList()方法会创建一个Arrays的内部类ArrayList并返回(返回的集合类型是java.util.Arrays.ArrayList,并不是我们常用的java.util.ArrayList,但是这不重要),可以看到这个方法将传入的参数解析成为数组并赋值给内部类ArrayList的成员变量a[]。

Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第5张图片

Arrays.asList()方法的参数是一个泛型的可变长参数T...a。

关于可变长参数

可变长参数的形参用来表示可以传入任意个指定类型的实参(0个、1个、任意个,也可以传入一个数组)Java编译器会根据实参数量创建一个指定类型的数组,如指定可变长参数为String...类型,数组类型为String[],指定可变长参数为int...类型,数组类型为int[]。当不如参数时,Java编译器会创建一个空数组,而不是传入参数null。

可变长参数示例:

public class Test {
    public static void variableLengthParameterTest(String... strs) {
        if(strs.length == 0){
            System.out.println("没有输入参数");
        }else{
            for(String str : strs){
                System.out.println(str);
            }
        }
    }
    public static void main(String[] args) {
        String strs[] = {"aaa","bbb","ccc","ddd","eee"};
        System.out.println("不输入参数");
        variableLengthParameterTest();
        System.out.println("输入一个参数");
        variableLengthParameterTest("aaa");
        System.out.println("输入三个参数");
        variableLengthParameterTest("aaa","bbb","ccc");
        System.out.println("输入一个数组");
        variableLengthParameterTest(strs);
    }
}

运行结果 :Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第6张图片

 关于泛型

泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。​​​​​​​​

泛型有一个重要的机制——类型擦除。Java编译器在执行泛型擦除之后类型会被改变为Object类型,所以泛型只能使用引用数据类型!!!!!

验证泛型机制是引用数据类型:

import java.lang.reflect.Method;
class inner{
    public T test(T param){
        return param;
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException{
        Class clazz = inner.class;
        Method[] methods = clazz.getMethods();
        for(Method method : methods){
            System.out.println(method.getReturnType() + "\t" + method.getName());
        }
    }
}

运行结果Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第7张图片

可以看到代码中指定的返回类型是泛型T,使用反射机制获取到的返回类型是java.lang.Object。

结论

使用Arrays.asList()方法是所需要的参数是一个引用类型的可变长参数,当传入一个引用类型的数组(如new Stirng[]{"aaa","bbb","ccc"})时由于数组内的元素都是引用类型的所以Java编译器会创建一个new Stirng[]{"aaa","bbb","ccc"}数组并传入;当传入一个基本数据类型的数组(如new int[]{1,2,3})时由于其内部元素不是引用类型,而其本身是一个引用类型,Java编译器会创建一个int[][]数组(二维数组,可以看作一个内部元素都是一维数组的一维数组),并将我们传入的数组{1,2,3}存入这个二维数组,返回的ArrayList内部成员数组a={{1,2,3}}。所以在使用Arrays.asList(new int{1,2,3}).forEach()遍历时会打印出传入数组的地址。

补充

 关于内部类Arrays.ArrayList的forEach()方法:先判断成员变量数组a是否为空,如果为空则抛出空指针异常NullPointerException,否则遍历数组a并执行传入的方法。Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第8张图片

Arrays.asList(new int[]{}).forEach()无法实现遍历问题分析_第9张图片

 

 

你可能感兴趣的:(java)