Java“双括号初始化”的效率?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/924285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Efficiency of Java "Double Brace Initialization"?
提问by Jim Ferrans
In Hidden Features of Javathe top answer mentions Double Brace Initialization, with a veryenticing syntax:
在Hidden Features of Java 中,最佳答案提到了Double Brace Initialization,其语法非常诱人:
Set<String> flavors = new HashSet<String>() {{
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}};
This idiom creates an anonymous inner class with just an instance initializer in it, which "can use any [...] methods in the containing scope".
这个习惯用法创建了一个匿名内部类,其中只有一个实例初始值设定项,它“可以使用包含范围内的任何 [...] 方法”。
Main question: Is this as inefficientas it sounds? Should its use be limited to one-off initializations? (And of course showing off!)
主要问题:这是否像听起来那么低效?它的使用是否应该仅限于一次性初始化?(当然还有炫耀!)
Second question: The new HashSet must be the "this" used in the instance initializer ... can anyone shed light on the mechanism?
第二个问题:新的 HashSet 必须是实例初始值设定项中使用的“this”……谁能解释一下这个机制?
Third question: Is this idiom too obscureto use in production code?
第三个问题:这个习语是否太晦涩而无法在生产代码中使用?
Summary:Very, very nice answers, thanks everyone. On question (3), people felt the syntax should be clear (though I'd recommend an occasional comment, especially if your code will pass on to developers who may not be familiar with it).
总结:非常非常好的答案,谢谢大家。关于问题 (3),人们认为语法应该很清楚(尽管我建议偶尔发表评论,特别是如果您的代码将传递给可能不熟悉它的开发人员时)。
On question (1), the generated code should run quickly. The extra .class files do cause jar file clutter, and slow program startup slightly (thanks to @coobird for measuring that). @Thilo pointed out that garbage collection can be affected, and the memory cost for the extra loaded classes may be a factor in some cases.
在问题 (1) 上,生成的代码应该运行得很快。额外的 .class 文件确实会导致 jar 文件混乱,并且会稍微减慢程序启动速度(感谢 @coobird 对此进行了测量)。@Thilo 指出垃圾收集可能会受到影响,在某些情况下,额外加载的类的内存成本可能是一个因素。
Question (2) turned out to be most interesting to me. If I understand the answers, what's happening in DBI is that the anonymous inner class extends the class of the object being constructed by the new operator, and hence has a "this" value referencing the instance being constructed. Very neat.
问题(2)对我来说是最有趣的。如果我理解答案,那么 DBI 中发生的事情是匿名内部类扩展了由 new 运算符构造的对象的类,因此具有引用正在构造的实例的“this”值。非常整洁。
Overall, DBI strikes me as something of an intellectual curiousity. Coobird and others point out you can achieve the same effect with Arrays.asList, varargs methods, Google Collections, and the proposed Java 7 Collection literals. Newer JVM languages like Scala, JRuby, and Groovy also offer concise notations for list construction, and interoperate well with Java. Given that DBI clutters up the classpath, slows down class loading a bit, and makes the code a tad more obscure, I'd probably shy away from it. However, I plan to spring this on a friend who's just gotten his SCJP and loves good natured jousts about Java semantics! ;-) Thanks everyone!
总的来说,DBI 给我留下了一种知识上的好奇心。Coobird 和其他人指出,您可以使用 Arrays.asList、varargs 方法、Google Collections 和提议的 Java 7 Collection 文字实现相同的效果。Scala、JRuby 和 Groovy 等较新的 JVM 语言也为列表构造提供了简洁的符号,并且可以与 Java 很好地互操作。鉴于 DBI 弄乱了类路径,稍微减慢了类加载速度,并使代码更加晦涩难懂,我可能会回避它。但是,我计划将这个介绍给一个刚刚获得 SCJP 并且喜欢关于 Java 语义的善意较量的朋友!;-) 谢谢大家!
7/2017: Baeldung has a good summaryof double brace initialization and considers it an anti-pattern.
7/2017:Baeldung对双括号初始化做了很好的总结,并将其视为一种反模式。
12/2017: @Basil Bourque notes that in the new Java 9 you can say:
12/2017:@Basil Bourque 指出,在新的 Java 9 中,您可以说:
Set<String> flavors = Set.of("vanilla", "strawberry", "chocolate", "butter pecan");
That's for sure the way to go. If you're stuck with an earlier version, take a look at Google Collections' ImmutableSet.
那肯定是要走的路。如果您坚持使用早期版本,请查看Google Collections 的 ImmutableSet。
采纳答案by coobird
Here's the problem when I get too carried away with anonymous inner classes:
当我对匿名内部类过于痴迷时,问题就出现了:
2009/05/27 16:35 1,602 DemoApp2.class
2009/05/27 16:35 1,976 DemoApp2.class
2009/05/27 16:35 1,919 DemoApp2.class
2009/05/27 16:35 2,404 DemoApp2.class
2009/05/27 16:35 1,197 DemoApp2.class
/* snip */
2009/05/27 16:35 1,953 DemoApp2.class
2009/05/27 16:35 1,910 DemoApp2.class
2009/05/27 16:35 2,007 DemoApp2.class
2009/05/27 16:35 926 DemoApp2.class
2009/05/27 16:35 4,104 DemoApp2.class
2009/05/27 16:35 2,849 DemoApp2.class
2009/05/27 16:35 926 DemoApp2.class
2009/05/27 16:35 4,234 DemoApp2.class
2009/05/27 16:35 2,849 DemoApp2.class
/* snip */
2009/05/27 16:35 614 DemoApp2.class
2009/05/27 16:35 2,344 DemoApp2.class
2009/05/27 16:35 1,551 DemoApp2.class
2009/05/27 16:35 1,604 DemoApp2.class
2009/05/27 16:35 1,809 DemoApp2.class
2009/05/27 16:35 2,022 DemoApp2.class
These are all classes which were generated when I was making a simple application, and used copious amounts of anonymous inner classes -- each class will be compiled into a separate class
file.
这些都是我在制作一个简单的应用程序时生成的所有类,并使用了大量匿名内部类——每个类都将被编译成一个单独的class
文件。
The "double brace initialization", as already mentioned, is an anonymous inner class with an instance initialization block, which means that a new class is created for each "initialization", all for the purpose of usually making a single object.
正如已经提到的,“双括号初始化”是一个带有实例初始化块的匿名内部类,这意味着为每个“初始化”创建一个新类,通常都是为了创建单个对象。
Considering that the Java Virtual Machine will need to read all those classes when using them, that can lead to some time in the bytecode verficationprocess and such. Not to mention the increase in the needed disk space in order to store all those class
files.
考虑到 Java 虚拟机在使用它们时需要读取所有这些类,这可能会导致字节码验证过程等一些时间。更不用说增加存储所有这些class
文件所需的磁盘空间了。
It seems as if there is a bit of overhead when utilizing double-brace initialization, so it's probably not such a good idea to go too overboard with it. But as Eddie has noted in the comments, it's not possible to be absolutely sure of the impact.
使用双大括号初始化时似乎有一些开销,因此过度使用它可能不是一个好主意。但正如埃迪在评论中指出的那样,不可能绝对确定影响。
Just for reference, double brace initialization is the following:
仅供参考,双括号初始化如下:
List<String> list = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
It looks like a "hidden" feature of Java, but it is just a rewrite of:
它看起来像是 Java 的一个“隐藏”特性,但它只是对以下内容的重写:
List<String> list = new ArrayList<String>() {
// Instance initialization block
{
add("Hello");
add("World!");
}
};
So it's basically a instance initialization blockthat is part of an anonymous inner class.
Joshua Bloch's Collection Literals proposalfor Project Coinwas along the lines of:
Joshua Bloch对Project Coin的Collection Literals 提案大致如下:
List<Integer> intList = [1, 2, 3, 4];
Set<String> strSet = {"Apple", "Banana", "Cactus"};
Map<String, Integer> truthMap = { "answer" : 42 };
Sadly, it didn't make its wayinto neither Java 7 nor 8 and was shelved indefinitely.
遗憾的是,它既没有进入 Java 7 也没有进入 Java 8 并且被无限期搁置。
Experiment
实验
Here's the simple experiment I've tested -- make 1000 ArrayList
s with the elements "Hello"
and "World!"
added to them via the add
method, using the two methods:
这是我测试过的简单实验——ArrayList
用元素制作 1000秒"Hello"
并"World!"
通过add
方法添加到它们中,使用两种方法:
Method 1: Double Brace Initialization
方法一:双括号初始化
List<String> l = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
Method 2: Instantiate an ArrayList
and add
方法 2:实例化一个ArrayList
andadd
List<String> l = new ArrayList<String>();
l.add("Hello");
l.add("World!");
I created a simple program to write out a Java source file to perform 1000 initializations using the two methods:
我创建了一个简单的程序来写出一个 Java 源文件来使用两种方法执行 1000 次初始化:
Test 1:
测试 1:
class Test1 {
public static void main(String[] s) {
long st = System.currentTimeMillis();
List<String> l0 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
List<String> l1 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
/* snip */
List<String> l999 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};
System.out.println(System.currentTimeMillis() - st);
}
}
Test 2:
测试 2:
class Test2 {
public static void main(String[] s) {
long st = System.currentTimeMillis();
List<String> l0 = new ArrayList<String>();
l0.add("Hello");
l0.add("World!");
List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World!");
/* snip */
List<String> l999 = new ArrayList<String>();
l999.add("Hello");
l999.add("World!");
System.out.println(System.currentTimeMillis() - st);
}
}
Please note, that the elapsed time to initialize the 1000 ArrayList
s and the 1000 anonymous inner classes extending ArrayList
is checked using the System.currentTimeMillis
, so the timer does not have a very high resolution. On my Windows system, the resolution is around 15-16 milliseconds.
请注意,使用 来检查初始化 1000ArrayList
秒和 1000 个匿名内部类扩展ArrayList
所用的时间System.currentTimeMillis
,因此计时器的分辨率不是很高。在我的 Windows 系统上,分辨率约为 15-16 毫秒。
The results for 10 runs of the two tests were the following:
两次测试运行 10 次的结果如下:
Test1 Times (ms) Test2 Times (ms)
---------------- ----------------
187 0
203 0
203 0
188 0
188 0
187 0
203 0
188 0
188 0
203 0
As can be seen, the double brace initialization has a noticeable execution time of around 190 ms.
可以看出,双括号初始化有一个明显的执行时间大约 190 毫秒。
Meanwhile, the ArrayList
initialization execution time came out to be 0 ms. Of course, the timer resolution should be taken into account, but it is likely to be under 15 ms.
同时,ArrayList
初始化执行时间为 0 ms。当然要考虑到定时器的分辨率,但很可能在15ms以下。
So, there seems to be a noticeable difference in the execution time of the two methods. It does appear that there is indeed some overhead in the two initialization methods.
因此,这两种方法的执行时间似乎存在明显差异。看起来这两种初始化方法确实存在一些开销。
And yes, there were 1000 .class
files generated by compiling the Test1
double brace initialization test program.
是的,.class
通过编译Test1
双括号初始化测试程序生成了1000 个文件。
回答by Eddie
Taking the following test class:
参加以下测试课程:
public class Test {
public void test() {
Set<String> flavors = new HashSet<String>() {{
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}};
}
}
and then decompiling the class file, I see:
然后反编译类文件,我看到:
public class Test {
public void test() {
java.util.Set flavors = new HashSet() {
final Test thispublic class Sample1 {
private static final String someVar;
static {
String temp = null;
..... // block of code setting temp
someVar = temp;
}
}
;
{
thispublic class Sample2 {
private final String someVar;
// This is an instance initializer
{
String temp = null;
..... // block of code setting temp
someVar = temp;
}
}
= Test.this;
super();
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}
};
}
}
This doesn't look terribly inefficient to me. If I were worried about performance for something like this, I'd profile it. And your question #2 is answered by the above code: You're inside an implicit constructor (and instance initializer) for your inner class, so "this
" refers to this inner class.
这在我看来并不是非常低效。如果我担心这样的性能,我会分析它。上面的代码回答了您的问题 #2:您在内部类的隐式构造函数(和实例初始值设定项)中,因此“ this
”指的是这个内部类。
Yes, this syntax is obscure, but a comment can clarify obscure syntax usage. To clarify the syntax, most people are familiar with a static initializer block (JLS 8.7 Static Initializers):
是的,这个语法很晦涩,但是注释可以澄清晦涩的语法用法。为了澄清语法,大多数人都熟悉静态初始化程序块(JLS 8.7 静态初始化程序):
public void test() {
Set<String> flavors = new HashSet<String>() {
{
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}
};
}
You can also use a similar syntax (without the word "static
") for constructor usage (JLS 8.6 Instance Initializers), although I have never seen this used in production code. This is much less commonly known.
您还可以使用类似的语法(不带“ static
”一词)用于构造函数的使用(JLS 8.6 Instance Initializers),尽管我从未见过在生产代码中使用过这种语法。这是鲜为人知的。
public void test() {
Set<String> flavors = new MyHashSet();
}
class MyHashSet extends HashSet<String>() {
public MyHashSet() {
add("vanilla");
add("strawberry");
add("chocolate");
add("butter pecan");
}
}
If you don't have a default constructor, then the block of code between {
and }
is turned into a constructor by the compiler. With this in mind, unravel the double brace code:
如果您没有默认构造函数,则{
和之间的代码块}
会被编译器转换为构造函数。考虑到这一点,解开双括号代码:
List<String> aList = Arrays.asList("vanilla", "strawberry", "chocolate");
The block of code between the inner-most braces is turned into a constructor by the compiler. The outer-most braces delimit the anonymous inner class. To take this the final step of making everything non-anonymous:
最里面的大括号之间的代码块被编译器转换为构造函数。最外面的大括号界定匿名内部类。采取这最后一步,使一切都非匿名:
public static Set<T> setOf(T ... elements) {
return new HashSet<T>(Arrays.asList(elements));
}
For initialization purposes, I'd say there is no overhead whatsoever (or so small that it can be neglected). However, every use of flavors
will go not against HashSet
but instead against MyHashSet
. There is probably a small (and quite possibly negligible) overhead to this. But again, before I worried about it, I would profile it.
出于初始化目的,我想说没有任何开销(或者小到可以忽略)。但是,每次使用 offlavors
都不会反对HashSet
,而是反对MyHashSet
。这可能有很小的(很可能可以忽略不计)开销。但同样,在我担心它之前,我会分析它。
Again, to your question #2, the above code is the logical and explicit equivalent of double brace initialization, and it makes it obvious where "this
" refers: To the inner class that extends HashSet
.
再次,对于您的问题 #2,上面的代码是双括号初始化的逻辑和显式等价物,它使“ this
” 指的是:指向扩展的内部类HashSet
。
If you have questions about the details of instance initializers, check out the details in the JLSdocumentation.
如果您对实例初始化程序的详细信息有疑问,请查看JLS文档中的详细信息。
回答by Eddie
This will call
add()
for each member. If you can find a more efficient way to put items into a hash set, then use that. Note that the inner class will likely generate garbage, if you're sensitive about that.It seems to me as if the context is the object returned by
new
, which is theHashSet
.If you need to ask... More likely: will the people who come after you know this or not? Is it easy to understand and explain? If you can answer "yes" to both, feel free to use it.
这将要求
add()
每个成员。如果您能找到一种更有效的方法将项目放入哈希集中,请使用它。请注意,如果您对此敏感,内部类可能会生成垃圾。在我看来,上下文好像是 返回的对象
new
,也就是HashSet
.如果你需要问……更有可能:你后面来的人会知道吗?容易理解和解释吗?如果您可以对两者都回答“是”,请随意使用它。
回答by Paul Morie
Efficiency aside, I rarely find myself wishing for declarative collection creation outside of unit tests. I do believe that the double brace syntax is very readable.
除了效率之外,我很少发现自己希望在单元测试之外创建声明式集合。我确实相信双括号语法非常易读。
Another way to achieve the declarative construction of lists specifically is to use Arrays.asList(T ...)
like so:
实现列表声明式构造的另一种方法是Arrays.asList(T ...)
像这样使用:
static public Set<T> setOf(T ... elements) {
Set set=new HashSet<T>(elements.size());
for(T elm: elements) { set.add(elm); }
return set;
}
The limitation of this approach is of course that you cannot control the specific type of list to be generated.
这种方法的局限性当然是您无法控制要生成的特定类型列表。
回答by Nat
To create sets you can use a varargs factory method instead of double-brace initialisation:
要创建集合,您可以使用可变参数工厂方法而不是双括号初始化:
public class Test {
public void add(Object o) {
}
public Set<String> makeSet() {
return new HashSet<String>() {
{
add("hello"); // HashSet
Test.this.add("hello"); // outer instance
}
};
}
}
The Google Collections library has lots of convenience methods like this, as well as loads of other useful functionality.
Google Collections 库有很多这样的便捷方法,以及大量其他有用的功能。
As for the idiom's obscurity, I encounter it and use it in production code all the time. I'd be more concerned about programmers who get confused by the idiom being allowed to write production code.
至于成语的晦涩难懂,我一直在生产代码中遇到并使用它。我更关心那些被允许编写生产代码的习惯用法弄糊涂的程序员。
回答by Lawrence Dol
I second Nat's answer, except I would use a loop instead of creating and immediately tossing the implicit List from asList(elements):
我第二个 Nat 的答案,除了我会使用循环而不是创建并立即从 asList(elements) 中抛出隐式列表:
public Set<String> makeSet() {
return new HashSet<String>() {
{
add("hello"); // not HashSet anymore ...
}
@Override
boolean add(String s){
}
};
}
回答by Neil Coffey
There's generally nothing particularly inefficient about it. It doesn't generally matter to the JVM that you've made a subclass and added a constructor to it-- that's a normal, everyday thing to do in an object-oriented language. I can think of quite contrived cases where you could cause an inefficiency by doing this (e.g. you have a repeatedly-called method that ends up taking a mixture of different classes because of this subclass, whereas ordinary the class passed in would be totally predictable-- in the latter case, the JIT compiler could make optimisations that are not feasible in the first). But really, I think the cases where it'll matter are very contrived.
通常没有什么特别低效的。对于 JVM 来说,创建子类并向其添加构造函数通常无关紧要——这是在面向对象的语言中很正常的日常事情。我可以想到一些非常人为的情况,您可能会通过这样做导致效率低下(例如,您有一个重复调用的方法,由于这个子类,该方法最终混合了不同的类,而普通的类传入将是完全可以预测的- - 在后一种情况下,JIT 编译器可以进行在第一种情况下不可行的优化)。但实际上,我认为这很重要的情况非常人为。
I'd see the issue more from the point of view of whether you want to "clutter things up" with lots of anonymous classes. As a rough guide, consider using the idiom no more than you'd use, say, anonymous classes for event handlers.
我会更多地从您是否想用大量匿名类“弄乱”的角度来看待这个问题。作为一个粗略的指南,请考虑使用这个习语,而不是像使用事件处理程序的匿名类一样。
In (2), you're inside the constructor of an object, so "this" refers to the object you're constructing. That's no different to any other constructor.
在 (2) 中,您在对象的构造函数中,因此“this”指的是您正在构造的对象。这与任何其他构造函数没有什么不同。
As for (3), that really depends on who's maintaining your code, I guess. If you don't know this in advance, then a benchmark that I would suggest using is "do you see this in the source code to the JDK?" (in this case, I don't recall seeing many anonymous initialisers, and certainly not in cases where that's the onlycontent of the anonymous class). In most moderately sized projects, I'd argue you're really going to need your programmers to understand the JDK source at some point or other, so any syntax or idiom used there is "fair game". Beyond that, I'd say, train people on that syntax if you have control of who's maintaining the code, else comment or avoid.
至于(3),我猜这真的取决于谁在维护你的代码。如果您事先不知道这一点,那么我建议使用的基准测试是“您在 JDK 的源代码中看到了吗?” (在这种情况下,我不记得看到过很多匿名初始化程序,当然在匿名类的唯一内容的情况下也不会)。在大多数中等规模的项目中,我认为您确实需要您的程序员在某些时候了解 JDK 源代码,因此在那里使用的任何语法或习语都是“公平游戏”。除此之外,我想说,如果您可以控制谁维护代码,则可以对人们进行语法培训,否则评论或避免。
回答by Thilo
One property of this approach that has not been pointed out so far is that because you create inner classes, the whole containing class is captured in its scope. This means that as long as your Set is alive, it will retain a pointer to the containing instance (this$0
) and keep that from being garbage-collected, which could be an issue.
迄今为止尚未指出的这种方法的一个特性是,因为您创建了内部类,所以整个包含类都被捕获在其范围内。这意味着只要您的 Set 还活着,它就会保留一个指向包含实例 ( this$0
)的指针并防止它被垃圾收集,这可能是一个问题。
This, and the fact that a new class gets created in the first place even though a regular HashSet would work just fine (or even better), makes me not want to use this construct (even though I really long for the syntactic sugar).
这一点,以及尽管常规 HashSet 可以正常工作(甚至更好),但首先创建了一个新类的事实使我不想使用这个构造(即使我真的很渴望语法糖)。
Second question: The new HashSet must be the "this" used in the instance initializer ... can anyone shed light on the mechanism? I'd have naively expected "this" to refer to the object initializing "flavors".
第二个问题:新的 HashSet 必须是实例初始值设定项中使用的“this”……谁能解释一下这个机制?我天真地期望“this”指代初始化“flavors”的对象。
This is just how inner classes work. They get their own this
, but they also have pointers to the parent instance, so that you can call methods on the containing object as well. In case of a naming conflict, the inner class (in your case HashSet) takes precedence, but you can prefix "this" with a classname to get the outer method as well.
这就是内部类的工作方式。它们有自己的this
,但它们也有指向父实例的指针,因此您也可以调用包含对象的方法。在命名冲突的情况下,内部类(在您的情况下为 HashSet)优先,但您也可以在“this”前加上类名以获取外部方法。
package literal;
public class collection {
public static <T> List<T> List(T...elems){
return Arrays.asList( elems );
}
}
To be clear on the anonymous subclass being created, you could define methods in there as well. For example override HashSet.add()
要明确创建的匿名子类,您也可以在其中定义方法。例如覆盖HashSet.add()
import static literal.collection.List;
import static system.io.*;
public class CollectionDemo {
public void demoList(){
List<String> slist = List( "a", "b", "c" );
List<Integer> iList = List( 1, 2, 3 );
for( String elem : List( "a", "java", "list" ) )
System.out.println( elem );
}
}
回答by Jim Ferrans
Mario Gleichman describeshow to use Java 1.5 generic functions to simulate Scala List literals, though sadly you wind up with immutableLists.
Mario Gleichman描述了如何使用 Java 1.5 泛型函数来模拟 Scala 列表文字,尽管遗憾的是您最终得到了不可变列表。
He defines this class:
他定义了这个类:
Set<String> getFlavors(){
return Collections.unmodifiableSet(flavors)
}
and uses it thusly:
并因此使用它:
##代码##Google Collections, now part of Guavasupports a similar idea for list construction. In this interview, Jared Levy says:
Google Collections,现在是Guava 的一部分,支持类似的列表构建想法。在这次采访中,Jared Levy 说:
[...] the most heavily-used features, which appear in almost every Java class I write, are static methods that reduce the number of repetitive keystrokes in your Java code. It's so convenient being able to enter commands like the following:
Map<OneClassWithALongName, AnotherClassWithALongName> = Maps.newHashMap();
List<String> animals = Lists.immutableList("cat", "dog", "horse");
[...] 使用最频繁的特性,几乎出现在我编写的每个 Java 类中,都是静态方法,可以减少 Java 代码中重复击键的次数。能够输入如下命令非常方便:
Map<OneClassWithALongName, AnotherClassWithALongName> = Maps.newHashMap();
List<String> animals = Lists.immutableList("cat", "dog", "horse");
7/10/2014: If only it could be as simple as Python's:
2014 年 7 月 10 日:如果它可以像 Python 一样简单就好了:
animals = ['cat', 'dog', 'horse']
animals = ['cat', 'dog', 'horse']
2/21/2020: In Java 11 you can now say:
2/21/2020:在 Java 11 中,您现在可以说:
animals = List.of(“cat”, “dog”, “horse”)
animals = List.of(“cat”, “dog”, “horse”)
回答by bestsss
leak prone
容易泄漏
I've decided to chime in. The performance impact includes: disk operation + unzip (for jar), class verification, perm-gen space (for Sun's Hotspot JVM). However, worst of all: it's leak prone. You can't simply return.
我决定插一句。性能影响包括:磁盘操作+解压缩(用于 jar)、类验证、永久空间(用于 Sun 的 Hotspot JVM)。然而,最糟糕的是:它容易泄漏。你不能简单地返回。
##代码##So if the set escapes to any other part loaded by a different classloader and a reference is kept there, the entire tree of classes+classloader will be leaked. To avoid that, a copy to HashMap is necessary, new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}})
. Not so cute any more.
I don't use the idiom, myself, instead it is like new LinkedHashSet(Arrays.asList("xxx","YYY"));
因此,如果该集合转义到由不同类加载器加载的任何其他部分,并且在那里保留了一个引用,则整个类+类加载器树将被泄漏。为了避免这种情况,副本的HashMap是必要的,new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}})
。不再那么可爱了。我自己不使用这个成语,而是像 new LinkedHashSet(Arrays.asList("xxx","YYY"));