美文网首页
列表优先于数组

列表优先于数组

作者: 没走过的二丁目 | 来源:发表于2018-07-26 15:55 被阅读0次

数组于泛型相比较,有两个重要的不同点。首先,数组是协变的(covariant)。这个词听起来有点吓人,其实是表示如果Sub为Super的子类型,那么数字sub[]类型,就是super[]类型的子类型。相反泛型是不可变的(invariant):对于任意两个不同的类型type1和type2,list<type1>和既不是list<type2>的子类型,也不是他的超类型,你可能认为泛型是有有缺陷的,但是实际上数组才是有缺陷的 ,举个例子

  //编译通过,但是会抛出异常
        Object[] array = new Long[2];
        array[0] = " dd";

        //编译不通过
        ArrayList<Long> num = new ArrayList<Long>();
        num.add("dd");

上面两种方法无论哪个都无法将String放进long容器,但使用数组你在运行时候才能发现错误,而使用列表在编译的时候就知道。
    数组于泛型之间的第二大区别在于,数组是具体化的(reified)因此数组会在运行时才知道并检查他们的元素类型约束。如上所示将String放到long中会得到一个ArraystoreException异常,相比之下,泛型则是通过擦除来实现的,因此泛型只在编译的时候强化类型信息,在运行的时候擦除元素类型信息
    由于上述这些根本的区别,因此数组和泛型不能很好的混合使用。例如,创建泛型,参数化类型或者类型参数的数组是非法的

new list<String>[]
new list<E>[]
new E[]

这些都会在编译的时候报错:generic array creation(泛型数组创建错误)
那么为什么创建泛型数组是非法的?
因为它不是类型安全的,要是他合法,编译器在其他正确的程序中发生的转换就会在运行时候失败,并抛出一个ClassCastExeception异常,这就违背了泛型系统提供的基本特征。 举个栗子

        List<String>[] stringLists=new List<String>[1];//创建一个泛型数组,假设合法
        List<Integer> intList= Arrays.asList(42);//创建并初始化一个包含单个元素的List<Integer>
        Object[] objects=stringLists;//将List<String>数组保存到一个Object数组变量中,这是合法的,因为数组和协变的。
        objects[0]=intList;//将List<Integer>保存到Object数组里唯一的元素中,这是可以的,因为泛型是通过擦除实现的。
        String s=stringLists[0].get(0);//我们从这个数组里唯一的列表中获取唯一的元素,编译器会自动地获取到元素转换成String,但它是一个Integer,因此,我们在运行时得到一个ClassCastException。
        //为了防止这种情况(创建泛型数组),第一行就产生了一个编译时错误。

当你得到泛型数组创建错误时候,最好的办法就是优先使用集合类型List<E>,而不是数组类型E[],举个复杂栗子
假设有一个(collections.synchronizedList返回的那种)同步列表和一个函数,假设要编写一个方法reduce,并使用apply函数来处理这个列表,列表元素类型为整数,并且函数是用来做两个整数的求和运算,reduce就会返回列表中所有元素的总和,如果函数是返回两个数的乘积,那么reduce就返回整个列表的乘积,代码如下

 static Object reduce(List list,Function f,Object initVal){
        synchronized (list){
            Object result = initVal;
            for(Object o:list){
                result = f.apply(result,o);
            }
            return result;
        }

    }
    interface Function{
        Object apply(Object arg1,Object arg2);
    }

第67条中告诉我们:不要从同步区域中调用外来方法,因此在持有锁的时候修改reduce方法来复制列表中的内容,要这么做一般用List的toArray方法(它在内部锁定列表)

 static Object reduce(List list,Function f,Object initVal){
        Object [] snaphot = list.toArray();//lock list
            Object result = initVal;
            for(Object o:snaphot){
                result = f.apply(result,o);
            }
            return result;

    }

如果视试图用泛型来完成这一点,就会遇到之前讨论的麻烦,以下是Function的泛型版

 interface Function<T>{
        T apply(T arg1,T arg2);
    }

那么我们来天真的修改reduce方法

static <E> E reduce(List<E> list,Function<E> f,E initVal){
        E [] snaphot = list.toArray();//error required E[]  found Object[]
            E result = initVal;
            for(E o:snaphot){
                result = f.apply(result,o);
            }
            return result;
    }

由于上述编译错误,你可能会说,没什么大不了的,将类型强转就行

 E [] snaphot = (E[]) list.toArray();

这样错误是消除了 但是却出现了一条警告


image.png

编译器告诉我们,它无法在运行时候检查转换的安全性,因为它在运行时候还不知道E是什么,这段程序虽然是可以运行的但是,通过微小的改动,就可以让它在没有包含显示转换的行上抛出ClassCastExeception

那么应该怎么办??? 就是用列表代替数组 代码如下:

static <E> E reduce(List<E> list, Function<E> f, E initVal) {
        List<E> snaphot;
        synchronized (list) {
            snaphot = new ArrayList<>(list);
        }
        E result = initVal;
        for (E o : snaphot) {
            result = f.apply(result, o);
        }
        return result;
    }

这个版本的代码虽然冗长,但是可以确定在运行时候不会得到任何的转换异常。

数组和泛型有着非常不同的类型规则。数组是协变且可以具体化的;泛型是不可变的且可以被擦除的。因此,数组提供了运行时的类型安全,但是没有编译时的类型安全,反之,对于泛型也一样。一般来说,数组和泛型不能很好地混合使用。如果你发现自己将它们混合起来使用,并且得到了编译时错误或者警告,你的第一反应就应该是用列表代替数组。

相关文章

  • 列表优先于数组

    数组于泛型相比较,有两个重要的不同点。首先,数组是协变的(covariant)。这个词听起来有点吓人,其实是表示如...

  • 第25条:列表优先于数组

    数组与泛型相比,有两个重要的不同点。 1.数组是协变的。其实只是表示如果Sub为Super的子类型,那么数组类型S...

  • Perl-2-列表与数组

    一、简介 列表:标量的有序集合 数组:储存列表的变量区别:列表指的是数据,数组指的是变量,列表的值不一定要放在数组...

  • perl-three(2018-05-26)

    第三章 列表和数组 标量代表单数,那么列表和数组就代表复数。 列表是标量的有序集合(指的是数据),数组是存储列表的...

  • 列表和数组

    Perl里的列表和数组用于表示复数。列表是指有序集合,数组是存储列表的变量。数组和列表里每个元素都是独立互不相关的...

  • 第3章 列表与数组

    列表(list):标量的有序集合。--数据数组(array):储存列表的变量。--变量“标量-标量变量;列表-数组...

  • R的数组和列表基本操作:创建与访问

    数组 创建数组array() array(向量名,维度说明,dimnames = list(维名称列表)) 列表 ...

  • Perl 列表与数组(一)

    1. 理解含义 列表(list):标量的有序集合。 数组(array):存储列表的变量。 2. 列表与数组的异同 ...

  • 数组/列表

  • 2018年9月29日.NET笔试面试题

    数组列表和数组有什么区别? 数组即Array类,数组列表即ArrayList类,两者非常相似,不过Array类在S...

网友评论

      本文标题:列表优先于数组

      本文链接:https://www.haomeiwen.com/subject/gnlpmftx.html