Java 9集合的便利工厂方法作为集合文字的替代方法
考虑这种方法(只是为了说明):
boolean isSmallNumber(String s) { return (n in ["one", "two", "three", "four"]); }
当然,这不是Java ,但它可能是你最喜欢的替代语言,支持集合文字,如Groovy或Kotlin 。 表达式是简洁的,就像字符串文字一样,编译器可以将集合文字放在某个静态存储区(甚至可能是"intern()"
)。
现在输入Java 9 :
boolean isSmallNumber(String s) { return Set.of("one", "two", "three", "four").contains(s); }
这也是简洁的,但不幸的是,每次调用它时,都会在堆上分配一个新的Set,然后立即使其可用于垃圾回收。
当然,您可以定义一个收集常量:
private static final Set<String> SMALL_NUMBERS = Set.of(...);
但是这个定义可能与一个大类中的方法定义有一千多的距离,并且你可能无法为它考虑一个好的描述性名称,而字面可能更清楚(在这个假设的情况下)。
所以,如果我在方法内部使用Set.of(...)
, JIT编译器会在每次调用方法时优化创建新对象吗?
我制作了一个简单的JMH基准:
@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) public class Temp { private Object value; @Setup public void setUp() { value = 50; } @Benchmark public boolean list1() { return List.of("one").contains(value); } @Benchmark public boolean list2() { return List.of("one", "two").contains(value); } @Benchmark public boolean list3() { return List.of("one", "two", "three").contains(value); } @Benchmark public boolean list4() { return List.of("one", "two", "three", "four").contains(value); } @Benchmark public boolean set1() { return Set.of("one").contains(value); } @Benchmark public boolean set2() { return Set.of("one", "two").contains(value); } @Benchmark public boolean set3() { return Set.of("one", "two", "three").contains(value); } @Benchmark public boolean set4() { return Set.of("one", "two", "three", "four").contains(value); } }
使用-prof gc
运行基准测试后,我可以得出如下结论:JIT优化了list1
, list2
, set1
, set2
,而不是list3
, list4
, set3
, set4
[1]
这似乎是完全合理的,因为对于N >= 3
listN
/ setN
创建比N <= 2
更复杂的List
/ Set
实现。
List
2个元素的实现:
static final class List2<E> extends AbstractImmutableList<E> { private final E e0; private final E e1; ... }
List
3个或更多元素的实现:
static final class ListN<E> extends AbstractImmutableList<E> { private final E[] elements; ... }
ListN
包含另一个间接级别(一个数组),这显然使得逃逸分析更加困难。
JMH输出(稍微改变以适应页面):
Benchmark Mode Cnt Score Error Units list1 avgt 5 3,075 ? 1,165 ns/op list1:·gc.alloc.rate avgt 5 0,131 ? 1,117 MB/sec list1:·gc.alloc.rate.norm avgt 5 ? 10?? B/op list1:·gc.count avgt 5 ? 0 counts list2 avgt 5 3,161 ? 0,543 ns/op list2:·gc.alloc.rate avgt 5 0,494 ? 3,065 MB/sec list2:·gc.alloc.rate.norm avgt 5 0,001 ? 0,003 B/op list2:·gc.count avgt 5 ? 0 counts list3 avgt 5 33,094 ? 4,402 ns/op list3:·gc.alloc.rate avgt 5 6316,970 ? 750,240 MB/sec list3:·gc.alloc.rate.norm avgt 5 64,016 ? 0,089 B/op list3:·gc.count avgt 5 169,000 counts list3:·gc.time avgt 5 154,000 ms list4 avgt 5 32,718 ? 3,657 ns/op list4:·gc.alloc.rate avgt 5 6403,487 ? 729,235 MB/sec list4:·gc.alloc.rate.norm avgt 5 64,004 ? 0,017 B/op list4:·gc.count avgt 5 165,000 counts list4:·gc.time avgt 5 146,000 ms set1 avgt 5 3,218 ? 0,822 ns/op set1:·gc.alloc.rate avgt 5 0,237 ? 1,973 MB/sec set1:·gc.alloc.rate.norm avgt 5 ? 10?? B/op set1:·gc.count avgt 5 ? 0 counts set2 avgt 5 7,087 ? 2,029 ns/op set2:·gc.alloc.rate avgt 5 0,647 ? 4,755 MB/sec set2:·gc.alloc.rate.norm avgt 5 0,001 ? 0,010 B/op set2:·gc.count avgt 5 ? 0 counts set3 avgt 5 88,460 ? 16,834 ns/op set3:·gc.alloc.rate avgt 5 3565,506 ? 687,900 MB/sec set3:·gc.alloc.rate.norm avgt 5 96,000 ? 0,001 B/op set3:·gc.count avgt 5 143,000 counts set3:·gc.time avgt 5 108,000 ms set4 avgt 5 118,652 ? 41,035 ns/op set4:·gc.alloc.rate avgt 5 2887,359 ? 920,180 MB/sec set4:·gc.alloc.rate.norm avgt 5 104,000 ? 0,001 B/op set4:·gc.count avgt 5 136,000 counts set4:·gc.time avgt 5 94,000 ms
Java HotSpot(TM)64位服务器虚拟机(构建9 + 181,混合模式)