假设你创造在一个接口应用有以下定义方法:
public String[] getParameters();
那么你该当细心反思。数组不但单老式,而且我们有公允的因由避免流露它们。在这篇文章中,我将试图总结在Java API中应用数组的缺陷。首先从最出乎意料的一个例子Start。
你可能认为应用数组是最快速的,因为数组是大大大多数collection完成的底层数据结构。应用一个纯数组如何会比应用一个包含数组的Tools功用更低?
其它翻译版本 (2) 加载中让我们先从这个看起来很熟悉的普遍的习惯用法Start:
public String[] getNames() { return namesList.toArray( new String[ namesList.size() ] ); }
这个方法从一个用来在其内部保管数据的可变凑集处创建了一个数据. 它颠末供应一个的确大小的数组来检验测验优化数组的创建. 幽默的是,这一“优化”使得其比下面的更轻易的版本速度还要慢(请看图表中绿色VS橘色条):
public String[] getNames() { return namesList.toArray( new String[ 0 ] ); }
不过,假设方法前去的是一个List, 创建防御式的副本又越发的快了 (红条):
public List<String> getNames() { return new ArrayList( namesList ); }
不合的地方在于一个ArrayList将它的数据项放在一个Object[]数组中,并且应用的是无类型的toArray方法,其比有类型的方法要快十分多(蓝条). 这是类型安全的,因为无类型的数组时封装在由编译器检查的泛型类型ArrayList<T>中的.
这个图标展示了一个在Java 7上n=5的参考标准. 不过,更多的数据项也许是此外一个VM情况系啊,这幅图片实在不会修改太多. CPU的开支可能实在不会太激烈,可是会有增加. 机遇有一个数组的应用者该当将其转换到一个凑集中去,以便利用它做任何任务, 然后将结果转换回一个数组,来送进此外一个接口的方法中,诸如此类做法.
是用一个轻易的ArrayList,而不是一个数组来提升功用,无需再动太多的手脚. ArrayList 为封装的数组增加了32字节的恒定开支. 例如,一个有十个Tools的数组需求104字节,一个ArrayList 136字节.
应用 凑集,你甚至可能决定前去内部列表的一个不成修改的版本:
public List<String> getNames() { return Collections.unmodifiableList( namesList ); }
此操作会在结实的市价运转,因此他比任何上述其它的方法都要快十分多(黄条). 其一致个防御式的拷贝不合。一个不成修改的凑集将会在你的内部数据变卦时跟着变卦。假设变卦爆发了,客户端会在迭代数据项时运转到一个ConcurrentModificationException中. 可以认为它是一个糟糕的设计,接供词给了一个在运转时抛出一个UnsupportedOperationException. 不过,起码关于内部的应用,这个方法关于一个防御式的拷贝而言,会是一个高功用的选择 - 一些不成能应用数组完成的Tools.
其它翻译版本 (1) 加载中Java 是一门面向Tools的言语。面向Tools的中间观念就是供应一些方法来访问和操作它们的数据,而不是间接对数据域履行操作. 这些方法创建一个接口来描画你可以在Tools上面做的任务.
由于java已经对功用做了设计,原生类型和数组已经被融合进了类型系统傍边. Tools可以使用数组来在内容高效地存储数据. 可是,即使颠末数组来呈现一个可变凑集的元素,它们也不会供应任何方法来访问和操作这些元素. 现实上,除间接访问的交流元素之外,在数组上你没有多少其它任务可以做. 数组甚至连toString 和 equals 都没有一个故意义的完成, 而凑集却有:
String[] array = { "foo", "bar" }; List<String> list = Arrays.asList( array ); System.out.println( list ); // -> [foo, bar] System.out.println( array ); // -> [Ljava.lang.String;@6f548414 list.equals( Arrays.asList( "foo", "bar" ) ) // -> true array.equals( new String[] { "foo", "bar" } ) // -> false
不合于数组,凑集的 API 供应了非常多有效的方法来访问元素. 用户可以检查包含的元素,提取子列表也许打算交集. 凑集可以向数据层添加特定的特点, 诸如线程安全,同时将完成事理保持在内部可见.
颠末应用一个数据,你定义了数据被保管在内存中的哪个地方. 颠末应用一个凑集,你定义了用户可以在数据上做的操作.
假设你依托于编译器检查的类型安全,警觉Tools数组. 下面的代码会在运转时奔溃,可是编译器找不出问题地址:
Number[] numbers = new Integer[10]; numbers[0] = Long.valueOf( 0 ); // throws ArrayStoreException
启事是数组是“协变式”的, 比如,假设 T 是S 的一个子类型, 那么 T[] 就会是 S[] 的一个子类型. Joshua Bloch 在其著作 Effective Java 涵盖了一切的实践, 每一个Java开发者必读.
归因于这个举动,流露数组类型的接口容许前去声明数组类型的一个子类型, 招致了一个奇特的运转时异常.
Bloch 同时也阐明说,数组与泛型类型不兼容. 因为数组会在运转时逼迫恳求有类型信息,而泛型则会在编译时被检查,泛型类型不能被放到数组中.
通俗而言,数组和泛型不能很好的融合。假设你创造自己在融合它们而失掉了一个编译时错误也许警告,那你的第一反应该当是用list去交流数组.
- Joshua Bloch, Effective Java (第二版), 第29条
数组底层的言语构造、它们会被用在完成中,可是它们不应该想其它的类流露. 在一个接口方法中应用数组违犯了面向Tools的绳尺,它会招致违和的API,并且它也可能给类型安全和功用构成短板.
本文中的一切译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译Task依照 CC 协议,假设我们的Task有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务