- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
哪种方式处理不同且已排序的集合最有效?
Set<MyObj> ret = new TreeSet<>();
for (Foo foo : foos)
ret.add(new MyObj(foo));
List<MyObj> ret = foos.stream().map(MyObj::new)
.distinct().sorted()
.collect(Collectors.toList());
Set<MyObj> ret = foos.stream().map(MyObj::new)
.collect(Collectors.toCollection(TreeSet::new));
第一种方式似乎最不优雅但易于阅读。第二种方式让我担心 distinct
和 sorted
会处理流两次。最后一种方式感觉还可以,但是流中的 TreeSet 开销是多少?
有什么线索吗?谢谢。
最佳答案
从 Stream API 源代码来看,我的初步猜测是:对于许多项目,简单流 (2) 应该是最快的,明显优于 TreeSet 版本 (1),然后 TreeSet 流 (3) 应该稍微跟上在后面。对于短数据集,(1) 可能比 (2) 好,后者又比 (3) 好,因为 Stream 创建会增加一些开销。 distinct-sorted 流的工作原理大致如下:
Set<MyObj> set = new HashSet<>();
List<MyObj> result = new ArrayList<>();
for (Foo foo : foos) {
MyObj myObj = new MyObj(foo);
if(set.add(myObj))
result.add(myObj);
}
result.sort(null);
return result;
让我们将此实现添加为 (4)。它使用 HashSet
检查结果是否不同,将它们添加到中间容器中,然后对其进行排序。这应该比维护 TreeSet
更快,因为我们不需要在每次插入后保持顺序(TreeSet
需要这样做,可能会重新平衡树)。实际的 Stream 实现效率会稍低一些,因为它不能就地对结果列表进行排序。相反,它会创建中间容器,对其进行排序,然后使用一系列 list.add
调用将结果转储到最终列表中。
结果可能取决于初始 foos
集合中的元素数量以及不同元素的数量。我称之为多样性:多样性 = 1 表示大致每个元素都不同; diversity = 0.5 表示每个元素大约重复两次。此外,结果可能在很大程度上取决于初始元素顺序:当输入数据被预排序或接近预排序时,排序算法可能会快一个数量级。
所以让我们按以下方式参数化我们的测试:
foos
中的元素数量):10、1000、100000我假设 Foo
只包含一个 int
字段。当然,结果可能在很大程度上取决于 Foo
类的 compareTo
、equals
和 hashCode
实现,因为版本 (2 ) 和 (4) 使用 equals
和 hashCode
而版本 (1) 和 (3) 使用 compareTo
。我们将简单地做到这一点:
@Override
public int hashCode() {
return x;
}
@Override
public boolean equals(Object o) {
return this == o || (o != null && getClass() == o.getClass() && x == ((Foo) o).x);
}
@Override
public int compareTo(Foo o) {
return Integer.compare(x, o.x);
}
可以通过以下方式生成预排序元素:
foos = IntStream.range(0, size)
.mapToObj(x -> new Foo((int)(x*diversity)))
.collect(Collectors.toList());
可以通过以下方式生成随机元素:
foos = new Random().ints(size, 0, (int) (size * diversity))
.mapToObj(Foo::new)
.collect(Collectors.toList());
使用JMH 1.13和JDK 1.8.0_101,VM 25.101-b13 64bit进行测量
预排序(所有时间均以 μs 为单位):
diversity size (1) (2) (3) (4)
1 10 0.2 0.5 0.3 0.2
1 1000 48.0 36.0 53.0 24.2
1 100000 14165.7 4759.0 15177.3 3341.6
0.5 10 0.2 0.3 0.2 0.2
0.5 1000 36.9 23.1 41.6 20.1
0.5 100000 11442.1 2819.2 12508.7 2661.3
0.2 10 0.1 0.3 0.2 0.2
0.2 1000 32.0 13.0 29.8 16.7
0.2 100000 8491.6 1969.5 8971.9 1951.7
未预分类:
diversity size (1) (2) (3) (4)
1 10 0.2 0.4 0.2 0.3
1 1000 72.8 77.4 73.6 72.7
1 100000 21599.9 16427.1 22807.8 16322.2
0.5 10 0.2 0.3 0.2 0.2
0.5 1000 64.8 46.9 69.4 45.5
0.5 100000 20335.2 11190.3 20658.6 10806.7
0.2 10 0.1 0.3 0.2 0.2
0.2 1000 48.0 19.6 56.7 22.2
0.2 100000 16713.0 5533.4 16885.0 5930.6
我最初的猜测大体上是正确的。对于预排序数据,当我们有 100,000 个元素时,(2) 和 (4) 会好几倍。当我们有很多重复项时,差异会变得更大,因为它们不会增加排序时间,而且重复插入到 HashSet
比重复插入到 TreeSet
更有效。对于随机数据,与 TimSort 算法(Java 用于对列表和数组进行排序)相比,TreeSet
性能对输入数据顺序的依赖性要小得多。对于小型数据集,简单的 TreeSet
速度很快,但使用 (4) 版本也可能具有竞争力。
基准测试的源代码和原始结果可用 here .
关于java - TreeSet 与 Java 8 Streams 性能对比,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42243012/
我正在尝试实现具有以下签名的方法: public static Pair, Stream> flatten(Iterator, Stream>> iterator); 该方法的目标是将每种流类型展平
我有两个流从两个不同的 api 获取。 Stream get monthOutStream => monthOutController.stream; Stream get resultOutStre
Stream.of(int[])返回 Stream ,而 Stream.of(String[])返回 Stream . 为什么这两种方法的行为不同?两者都应该返回 Stream和 Stream或 St
我正在使用 rxdart在 dart 中处理流的包。我被困在处理一个特殊的问题上。 请看一下这个虚拟代码: final userId = BehaviorSubject(); Stream getSt
我到处都找遍了,还是没弄明白。我知道你可以用流建立两个关联: 用于支持数据存储的包装器意味着作为消费者和供应商之间的抽象层 数据随着时间的推移变得可用,而不是一次全部 SIMD 代表单指令,多数据;在
考虑下面的代码: List l=new ArrayList<>(); l.add(23);l.add(45);l.add(90); Stream str=l.stream
我有一个大型主干/requirejs 应用程序,我想迁移到 webpack,最新的“webpack”:“^4.27.1”,但我遇到了一个我无法解决的错误。 我一直在阅读 https://webpack
我正在使用 xmpp 开发聊天应用程序,根据我们的要求,我们有三台服务器 Apache Tomcat 7、ejabbered 2.1.11 和 mysql 5.5, to run xmppbot on
我知道如何使用 Java 库,并且我可以编写一些循环来执行我需要的操作,但问题更多,为什么 scala.collection.JavaConverters 中没有任何内容或scala.collecti
我正在尝试创建一个单一的衬里,它应该计算一个非常长的文本文件中的唯一单词。独特的词例如:márya fëdorovna scarlet-liveried,...所以基本上都是非英语词。 我的问题是我的
如果我有以下情况: StreamWriter MySW = null; try { Stream MyStream = new FileStream("asdf.txt"); MySW =
有人可以帮我将以下语句转换为 Java8: 我有一个像这样的 HashMap : private Map, List>> someMap; 我想在java8中转换以下逻辑: private Strin
有人可以帮我将以下语句转换为 Java8: 我有一个像这样的 HashMap : private Map, List>> someMap; 我想在java8中转换以下逻辑: private Strin
考虑两种测试方法parallel()和sequential(): @Test public void parallel() throws Exception { System.ou
我是 NodeJS 的新手,我基本上想做的是通过 HTTP 将 .pdf 上传到我的服务器。我正在使用 POST rquest 来处理 Content-Type multipart/form-data
哪个更好:MemoryStream.WriteTo(Stream destinationStream) 或 Stream.CopyTo(Stream destinationStream)?? 我正在谈
给定一个 Stream,我想创建一个新的 Stream,其中的元素在它们之间有时间延迟。 我尝试使用 tokio_core::reactor::Timeout 和 Stream 的 and_then
我是 Kafka Streams 和 Spring Cloud Stream 的新手,但在将集成相关代码移动到属性文件方面已经阅读了有关它的好东西,因此开发人员可以主要专注于事物的业务逻辑方面。 这里
源代码看起来非常相似:pump , pipe .为什么我要使用一个而不是另一个?一个只是另一个的更好版本吗? 最佳答案 Stream.pipe 现在显然是自 0.3.x 以来的首选方法,因此尽可能尝试
我正在寻找是否有更好的方法来解决我不得不使用这些签名的困境(注意:由于 Spock 测试,T[][] 是必需的,我提供 T[][] 作为数据提供商) 我的方法签名是: public T[][] cr
我是一名优秀的程序员,十分优秀!