这两者有什么区别,哪种更好用,为什么?
如果有这样的事情,正确的答案是第一种形式。请看下面我的答案。
列表是接口,而arraylist是该接口的实现。
第二个更好,因为这意味着您可以在以后为列表的另一个实现更改ArrayList,而无需更改应用程序的其余部分。您可能希望这样做是因为性能原因,或者因为您选择/将选择的列表实现行为的其他方面。
确切地。如果您发现以后需要确保使用ArrayList,那么可以更改声明。否则,您的代码的其余部分实际上不需要知道正在使用哪种列表,这样,如果性能或其他考虑因素允许,您可以稍后交换不同的实现(可能是LinkedList)。
@Bill Everyone总是在实现中提到LinkedList,您可能也会进行切换,但我认为如果提到collections.unmodifiableList()、collections.checkedList()或collections.synchronizedList(),情况会更糟。
@ilmtitan更不用说其他的集合,比如google集合中不变的集合。
+1切割并干燥。研究这个话题,我希望我早点发现这一点,因为这些词比上千篇关于这个话题的浮夸、重复的文章回答的问题更多。
两者都是自Java 1.5以来的缩写。
应该是:
1 2
List< String> list = new ArrayList< String> ( ) ;
// or whatever data type you are using in your list
请阅读Joshua Bloch的有效Java,特别是这两个项目:
23:不要在新代码中使用原始类型(这甚至可以在线获得)
52:通过他们的界面
顺便说一句,如果您使用guava,那么您有一个构造arraylist的工厂方法,这样您就不必重复类型参数:
1
List< String> list = Lists.newArraylist ( ) ;
我希望每个人都知道你现在应该使用列表的模板版本(例如,如果你不使用,Eclipse会唠叨你)。但是你关于番石榴的第二句话很有趣!
+ 1的"有效Java",这是一本非常好的书。
我同意,这是每一个Java开发者都应该阅读的唯一一本书。
我认为这是每一个Java开发者应该阅读的第一本书;
List 为接口,其中ArrayList 为类,该类实现List 接口。
我更喜欢第二种形式,这是更一般的形式,即如果您不使用特定于ArrayList 的方法,您可以将其类型声明为接口List 类型。使用第二种形式,可以更容易地将实现从ArrayList 更改为实现List 接口的其他类。
编辑:正如许多这样的用户所评论的,这两个表单可以是接受List 或ArrrayList 的任何方法的参数。但当我声明方法时,我更喜欢接口:
1
void showAll
( List < String
> sl
) ...
用途:
只有当我的方法使用特定于ArrayList 的方法时,就像ensureCapacity() 一样。
响应信息,我们应该使用类型化的EDCOX1,0,而不是仅仅EDCOX1,12,是非常好的(当然,如果我们不使用古爪哇)。
对我来说,这听起来是反复无常的。第一个形式的ArrayList aList 可以作为参数传递给采用List 或ArrayList 参数的任何方法。第二种形式的List aList 只能作为参数传递给采用List 参数的方法。虽然变量List aList 引用了ArrayList ,但是强类型会阻止引用变量aList 作为参数匹配到ArrayList 参数。
-1正好相反;您可以将ArrayList 传递给需要List 的方法。不能将List 传递给要求ArrayList 的方法。
@伊什塔,伯特,没错。但您忘了提到一个方法几乎不应该期望数组列表。在99%的情况下,任何列表和50%的集合都可以。
+我想迈克尔在这里有点误会。如果您创建了一个接口,如果内部没有使用arraylist特定的特性,那么最好使用一个列表。这样就不会对用户强制实施实现。arraylist是列表的可能实现之一。
@我评论是因为我希望Micha?尼古拉斯修正了他的答案,这样就不会被误解,这将是一个很好的答案!但在目前的状态下,这是错误的。
@罗尔特-我敢肯定@michal的想法是对的,但答案是错的。最初的概念是正确的("第二种形式……更为普遍),但其余部分显然不正确。答案和正确信息的地方也是如此。否决票和评论确保(接受!)回答错误的信息不只是表面价值。一旦答案被更正,可以删除落选(可能还有评论)。
@伯特F:如果他会说"…你可以把一个列表(或arraylist)传递给对列表参数进行操作的方法…",那就不会那么混乱了,对吧?我看得出现在不对劲。
从我九个赞成票的观点来看,这一切都非常令人沮丧:p
我能想象…再次提醒我为什么一个人会否决整个社区的哪一个答案是最好的?我不反对接受答案的可能性,但接受的答案不必排在最前面。
谢谢你的评论。你说得对,我想的更多的是声明方法,而不是变量。我希望我编辑的答案更好。
所有这些答案都是从某个地方读到的教条中背诵出来的。
变量声明和初始化语句Type x = new Constructor(); 绝对是实现细节的一部分。它不是公共API的一部分(除非它是public final ,但List 是可变的,所以这是不适当的)
作为一个实现细节,您试图用抽象来愚弄谁?最好保持类型尽可能具体。它是数组列表还是链接列表?它是否应该是线程安全的?选择对于您的实现很重要,您仔细地选择了特定的列表impl。然后你把它当作一个简单的东西来宣布,就好像它无关紧要,你不在乎?
把它声明为List 的唯一合法理由是我太懒了,不想打字。这也涵盖了这样一个论点:如果我需要移动到另一个列表impl,我就少了一个地方可以修改。
只有当变量范围很小时,这个原因才是合法的,并且您可以从一眼看到它的所有用法。否则,请保留最具体的类型,以便在使用变量的所有代码中显示其性能和语义特征。
我大多不同意这一点,但它不值得-3;因为它提出了有效的论点(即使我不同意结论)。
顺便说一句,我不同意的一点是,"选择对您的实现很重要,您仔细选择了具体的列表impl",如果是这样的话,使用具体的类型可能是合理的,但在我的经验中,情况往往不是这样。例如,当使用集合类时,我几乎总是只使用arraylist&hashset,并且只考虑这是否会导致问题(即实际上从不)。在这种情况下,使用list/set更好地表达了我的意图,即"我只想要一个列表"。
+我完全同意。如果范围很小,则使用特定类型。你的另一个答案是:stackoverflow.com/a/12805778/1350762
这是一个Java怪癖,由于有限的类型推断和学说OOP。对于局部变量,它主要是样式;如果您需要子类型的某些特定特性,请使用子类型(下面的示例),否则也可以使用。
Java风格是使用超类型,甚至在实现的主体内执行接口,以及与可见类型的一致性(有效Java第二版:项目52:通过它们的接口引用对象)。在具有更多类型推断的语言中,例如c+/c/y//go等,不需要显式地声明类型,并且局部变量将具有特定类型。
对于可见类型(公共或受保护:字段、参数或方法的返回值),您几乎总是希望使用最通用的类型:接口或抽象类,以提供更好的灵活性(有效Java:项目40:仔细设计设计签名)。但是,对于不可见的类型(private或package private成员或局部变量),可以使用(它只是样式),有时还需要使用更具体的类型,包括具体的类。
查看有效的Java标准指南;个人想法如下。
使用更通用的类型(即使不可见)的原因是为了减少噪音:您声明只需要更通用的类型。在不可见的成员(如私有方法)上使用常规类型也可以在更改类型时减少用户流失。但是,这不适用于局部变量,因为它只改变了一行:ConcreteFoo foo = new ConcreteFoo(); 到OtherConcreteFoo foo = new OtherConcreteFoo(); 。
您确实需要子类型的情况包括:
您只需要子类型上存在的成员,例如:
实现的一些特性,如ensureCapacity for ArrayList
(在测试代码中常见)假类的一些成员,如(假设)FakeFileSystem#createFakeFile 。
您依赖子类型的行为,尤其是在父类型方法的重写中,例如:
具有更一般的参数类型,
更具体的返回类型,或
引发更具体或更少的异常类型。
作为最后一个例子,我应该关闭一个StringReader吗?:stringreader.html close重写reader.html close,不抛出IOException ,因此使用StringReader 而不是Reader 作为局部变量意味着您不需要处理实际不会发生的异常,并显著减少了样板。
在大多数情况下,我更喜欢第二种,因为它表示您没有在arraylist API中使用任何特定的内容,如果以后需要,您可以替换任何其他类型的列表,而不必更改除第一行以外的任何代码。
我知道在用接口声明变量时至少有一种情况不起作用。当你想使用反射。
我在一些代码上做了一个bug修复,在那里我声明了一个变量为Map ,并给它分配了一个HashMap 的实例。在通过反射访问的方法调用中,此变量用作参数。问题是反射试图找到一个带有HashMap 签名而不是声明的Map 签名的方法。由于没有使用HashMap 作为参数的方法,我无法通过反射找到方法。
1 2 3 4 5
Map
< String , Object
> map
= new HashMap
< String , Object
> ( ) ;
public void test
( Map
< String , Object
> m
) { ...
} ;
Method m
= this .
getClass ( ) .
getMethod ( "test" ,
new Class
<?> [ ] { map.
getClass ( ) } ) ;
找不到使用接口的方法。如果您制作另一个使用HashMap 的测试版本,那么它将工作——但是现在您必须用一个具体的类来声明变量,而不是更灵活的接口……
错误是方法查找代码中的错误。复杂的查找不需要精确的参数类型匹配,相反,它模拟编译器的算法来找到合适的方法。
这里的实际bug是您正在使用map.getClass(),它将返回hashmap.class来查找方法,而您应该使用map.class。
@约翰似乎是个小打字错误,但不止如此。在这种特殊情况下使用反射的全部目的是我不知道要调用哪个方法。我通过了参数的列表。如果我知道我想将该方法与map<>参数一起使用,我就不会使用reflection:。
如果您使用的是Java 1.4或更早的版本,那么我将使用第二个。最好尽可能地用一般性的方式声明字段,以防以后需要将其转换为其他内容,如向量等。
从1.5开始,我会选择以下内容
1
List< String> x = new ArrayList< String> ( ) ;
它给你一点类型安全性。列表"x"可以添加一个字符串对象,就这样。当您从列表"x"中得到一个项目时,您可以依靠一个字符串将要返回的事实。这也有助于删除不必要的强制转换,当您6个月后返回并尝试记住代码的作用时,这种强制转换会使代码难以读取。如果您试图添加任何其他对象类型,编译器/IDE将通过显示错误来帮助您记住应该进入列表"x"的类型。
如果要向列表中添加多个对象类型,则可以添加注释以抑制编译错误。
这样式不好。不要压制警告,使用List 。示例:List y = new ArrayList(); y.add(1); y.add("abc"); y.add(true); 。
(当然,我在示例中使用的自动拳击也很糟糕)
如果您使用的是1.4或更早版本,那么首先要做的就是升级您的JVM。
我不想引发一场火焰战,我只是想给出离开Java 1.4引用的理由。我相信至少还有一个贫穷的开发人员别无选择,只能在维护旧产品时使用比最新工具少的工具。不幸的是,我们中的一些人没有追索权,而是继续在大型遗留项目中使用旧的JVM,因为业务只允许对自定义代码进行更新,而不允许对支持库进行更新。此外,重新测试可能导致使用旧的JVM的整个代码库的成本也是合理的。