Java 8提供
input()
e.g。 Stream.of(item).collect(Collectors.toSomeCollection())
。避免相对昂贵的Stream.of("abc").collect(Collectors.toSet())
操作员在幕前比
new
e.g。 new SomeCollection(Arrays.asList(item))
?
我确定使用new HashSet<>(Arrays.asList("abc"))
初始化不同集合的成本不同(哈希集在准备使用之前需要哈希表,数组列表是已分配的数组,而链表不需要这样)。
我试图弄清楚流相关类是否在内部避免new
,但OpenJDK代码很难理解。我认为如果我将new
视为已经初始化的管道和使用可重用函数的收集器,我们就可以。
可能是另一种方式吗?
答案 0 :(得分:8)
让我们衡量(使用jmh)并找出哪一个更快&#39;:
@BenchmarkMode({ Mode.AverageTime })
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@Fork(1)
public class MyBenchmark {
private static final int ITERATIONS = 10_000_000;
@Benchmark
public void baseLine(Blackhole bh) {
for (int i = 0; i < ITERATIONS; i++) {
Set<String> s = new HashSet<>();
s.add("A");
bh.consume(s);
}
}
@Benchmark
public void asList(Blackhole bh) {
for (int i = 0; i < ITERATIONS; i++) {
Set<?> s = new HashSet<>(Arrays.asList("A"));
bh.consume(s);
}
}
@Benchmark
public void collectStream(Blackhole bh) {
for (int i = 0; i < ITERATIONS; i++) {
Set<?> s = Stream.of("A").collect(Collectors.toSet());
bh.consume(s);
}
}
@Benchmark
public void setOf(Blackhole bh) {
for (int i = 0; i < ITERATIONS; i++) {
Set<?> s = Set.of("A");
bh.consume(s);
}
}
@Benchmark
public void singletonCollection(Blackhole bh) {
for (int i = 0; i < ITERATIONS; i++) {
Set<?> s = Collections.singleton("A");
bh.consume(s);
}
}
}
结果是:
# JMH version: 1.19
# VM version: JDK 9, VM 9+181
Benchmark Mode Cnt Score Error Units
MyBenchmark.baseLine avgt 10 0.301 ± 0.002 s/op
MyBenchmark.asList avgt 10 0.350 ± 0.012 s/op
MyBenchmark.collectStream avgt 10 0.517 ± 0.009 s/op
MyBenchmark.setOf avgt 10 0.057 ± 0.001 s/op
MyBenchmark.singletonCollection avgt 10 0.057 ± 0.001 s/op
所以在你提到的2中,使用asList
似乎更快。如果您只有一个元素,则可以使用Collections.singleton*
(由@AndyTurner建议)。 Java 9接口工厂方法适用于任意数量的元素,但也针对单个元素进行了优化。
答案 1 :(得分:2)
理论上,Stream.of(item).collect(Collectors.toSomeCollection())
可以返回更有效的集合,因为故意未指定确切的集合类型。
在实践中,有一些阻碍这种情况的障碍。例如。收集器必须provide a supplier容器才能积累元素,而不提供有关流特征的任何提示,即使它们事先已知,例如Stream.of(item)
示例,已知其具有单个元素。收集器可以为中间类型提供供应商,该类型在最后转换为不同的结果类型,这可以增加内存效率,但需要额外的CPU时间。
这会导致例如Arrays.asList(stream.toArray())
可能比stream.collect(Collectors.toList())
效率更高,因为如果可预测,toArray()
操作可以访问流 size 。但是对于通过Stream.of(item[,items])
创建的没有中间操作的流,显然不能比Arrays.asList(item[,items])
更有效率,List
在没有流机制的情况下首先创建Collections.singletoList(item)
。
但是对于单个项目,List.of(item)
或Java 9的Set
甚至更高效,因为它们根本不需要处理数组。
创建HashSet
时,差异甚至更大,正如Jorn Vernee’s answer中的实际数字所示。
要理解它们,我们必须意识到在每个“baseline”,“asList”和“collectStream”变体中,都会创建一个HashSet
。即使在当前的Java 9中,HashMap
仍然作为HashSet
的包装器实现,这会减少代码大小但不会减小堆大小,当然也不会增加效率。
因此,在所有这三种变体中,单个元素集实例由HashMap
实例,Map.Entry
实例,数组(条目)实例和HashSet
实现实例组成。这个重量在所有三种变体中都有。
在“asList”变体的Arrays.asList
构造过程中,会创建一个varargs参数的数组,Stream.of
会在其周围创建一个包装器。
相比之下,“collectStream”变体可以依赖于优化的单个参数Spliterator
变体,但它仍然会创建一个包含项目的流构建器(充当Stream
)和a HashSet
实施实例在其上运行。
同样,“asList”和“collectStream”都将在内部处理条件,因为无论其形式如何,都会对源进行迭代,以将所有元素添加到Collections.singleton
,而在“基线”中“变体,只有一个元素添加的知识已被直接编码。
相比之下,Set.of
和Set
都创建了一个Set
实现实例,只包含一个引用。这是最有效的形式,并且不需要运行时评估来决定必须使用此表单。
在此示例中,即使散列也必须被视为负面性能方面。虽然散列在大量元素的情况下具有很好的扩展性,但对于单个元素的情况来说,这是一个过时的操作,而不是专门的单元素ArrayList<Data> items=new ArrayList<>();
SpinnerDialog spinnerDialog;
ImageView imageView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drawer_menu);
// Searching With Spinner[START]
initItems();
spinnerDialog =new SpinnerDialog(DrawerMenu.this,items,"Select Item");
spinnerDialog.bindOnSpinerListener(new OnSpinerItemClick() {
@Override
public void onClick(Data item, int position) {
Toast.makeText(DrawerMenu.this,"Selected: "+item.getId()+" "+item.getTitle(),Toast.LENGTH_SHORT).show();
}
});
imageView=(ImageView)findViewById(R.id.search_Quali);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
spinnerDialog.showSpinerDialog();
}
});
}
private void initItems() {
for(int i=0;i<100;i++)
{
items.add(new Data(101+i+"","Item"+(i+1)));
}
}
class Data {
String Id,title;
Data(String id,String title)
{
this.title=title;
this.Id=id;
}
public String getId() {
return Id;
}
public String getTitle() {
return title;
}
// Searching With Spinner[END]
}
实现。
答案 2 :(得分:1)
List
,Set
或Map
的收藏家最终会创建List
,Set
或Map
个实例。
例如,这里是Collectors.toSet()
正文:
public static <T>
Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
(left, right) -> { left.addAll(right); return left; },
CH_UNORDERED_ID);
}
所以你永远不会忘记new
运算符声明。
Stream.of()
允许操纵Stream
我认为这是使用它的唯一正当理由。