当前位置: 首页 > 知识库问答 >
问题:

Java Streams:如何进行有效的“区分和排序”?

喻嘉泽
2023-03-14

假设我有一个

天真的做法是只做以下几件事:

Stream.of(...)
    .sorted()
    .distinct()

或者,也许反过来:

Stream.of(...)
    .distinct()
    .sorted()

由于JDK的源代码并不能真正访问这两种方法的实现,我只是想知道可能的内存消耗和性能影响。

或者将我自己的过滤器编写为以下内容会更有效吗?

Stream.of(...)
    .sorted()
    .filter(noAdjacentDuplicatesFilter())

public static Predicate<Object> noAdjacentDuplicatesFilter() {
    final Object[] previousValue = {new Object()};

    return value -> {
        final boolean takeValue = !Objects.equals(previousValue[0], value);
        previousValue[0] = value;
        return takeValue;
    };
}

共有2个答案

诸福
2023-03-14

免责声明:我知道性能测试很难,尤其是在需要热身的JVM和没有其他进程运行的受控环境中。

如果我测试它,我会得到这些结果,因此您的实现似乎有利于并行执行。(在i7上运行4核超线程)。

所以“.distinct().sorted()”似乎要慢一些。正如霍尔格所预测/解释的那样

Round 1 (Warm up?)
3938
2449
5747
Round 2
2834
2620
3984
Round 3 Parallel
831
4343
6346
Round 4 Parallel
825
3309
6339

使用代码:

package test.test;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SortDistinctTest {

    public static void main(String[] args) {
        IntStream range = IntStream.range(0, 6_000_000);
        List<Integer> collect = range.boxed().collect(Collectors.toList());
        Collections.shuffle(collect);

        long start = System.currentTimeMillis();

        System.out.println("Round 1 (Warm up?)");
        collect.stream().sorted().filter(noAdjacentDuplicatesFilter()).collect(Collectors.counting());
        long fst = System.currentTimeMillis();
        System.out.println(fst - start);

        collect.stream().sorted().distinct().collect(Collectors.counting());
        long snd = System.currentTimeMillis();
        System.out.println(snd - fst);

        collect.stream().distinct().sorted().collect(Collectors.counting());
        long end = System.currentTimeMillis();
        System.out.println(end - snd);

        System.out.println("Round 2");
        collect.stream().sorted().filter(noAdjacentDuplicatesFilter()).collect(Collectors.counting());
        fst = System.currentTimeMillis();
        System.out.println(fst - end);

        collect.stream().sorted().distinct().collect(Collectors.counting());
        snd = System.currentTimeMillis();
        System.out.println(snd - fst);

        collect.stream().distinct().sorted().collect(Collectors.counting());
        end = System.currentTimeMillis();
        System.out.println(end - snd);

        System.out.println("Round 3 Parallel");
        collect.stream().parallel().sorted().filter(noAdjacentDuplicatesFilter()).collect(Collectors.counting());
        fst = System.currentTimeMillis();
        System.out.println(fst - end);

        collect.stream().parallel().sorted().distinct().collect(Collectors.counting());
        snd = System.currentTimeMillis();
        System.out.println(snd - fst);

        collect.stream().parallel().distinct().sorted().collect(Collectors.counting());
        end = System.currentTimeMillis();
        System.out.println(end - snd);

        System.out.println("Round 4 Parallel");
        collect.stream().parallel().sorted().filter(noAdjacentDuplicatesFilter()).collect(Collectors.counting());
        fst = System.currentTimeMillis();
        System.out.println(fst - end);

        collect.stream().parallel().sorted().distinct().collect(Collectors.counting());
        snd = System.currentTimeMillis();
        System.out.println(snd - fst);

        collect.stream().parallel().distinct().sorted().collect(Collectors.counting());
        end = System.currentTimeMillis();
        System.out.println(end - snd);

    }

    public static Predicate<Object> noAdjacentDuplicatesFilter() {
        final Object[] previousValue = { new Object() };

        return value -> {
            final boolean takeValue = !Objects.equals(previousValue[0], value);
            previousValue[0] = value;
            return takeValue;
        };

    }

}
饶承宣
2023-03-14

当您在sorted()之后链接一个明显()操作时,实现将利用数据的排序性质并避免构建内部HashSet,这可以由以下程序演示

public class DistinctAndSort {
    static int COMPARE, EQUALS, HASHCODE;
    static class Tracker implements Comparable<Tracker> {
        static int SERIAL;
        int id;
        Tracker() {
            id=SERIAL++/2;
        }
        public int compareTo(Tracker o) {
            COMPARE++;
            return Integer.compare(id, o.id);
        }
        public int hashCode() {
            HASHCODE++;
            return id;
        }
        public boolean equals(Object obj) {
            EQUALS++;
            return super.equals(obj);
        }
    }
    public static void main(String[] args) {
        System.out.println("adjacent sorted() and distinct()");
        Stream.generate(Tracker::new).limit(100)
              .sorted().distinct()
              .forEachOrdered(o -> {});
        System.out.printf("compareTo: %d, EQUALS: %d, HASHCODE: %d%n",
                          COMPARE, EQUALS, HASHCODE);
        COMPARE=EQUALS=HASHCODE=0;
        System.out.println("now with intermediate operation");
        Stream.generate(Tracker::new).limit(100)
            .sorted().map(x -> x).distinct()
            .forEachOrdered(o -> {});
        System.out.printf("compareTo: %d, EQUALS: %d, HASHCODE: %d%n",
                          COMPARE, EQUALS, HASHCODE);
    }
}

哪个会打印

adjacent sorted() and distinct()
compareTo: 99, EQUALS: 99, HASHCODE: 0
now with intermediate operation
compareTo: 99, EQUALS: 100, HASHCODE: 200

中间操作,就像map(x-

不能保证会发生这种优化,但是,可以合理地假设Stream实现的开发人员不会删除该优化,甚至不会尝试添加更多优化,因此滚动您自己的实现将阻止您的代码受益从未来的优化。

此外,您所创建的是一个“有状态谓词”,强烈反对使用它,当然,当与并行流一起使用时,它会中断。

如果您不相信流API能够足够有效地执行此操作,那么最好在没有流API的情况下实现此特定操作。

 类似资料:
  • 我最近开始学习PySpark的大数据分析。我有以下问题,并试图找到一个更好的方法来实现这一点。我会带你解决下面的问题。 给定下面的pyspark数据框: 我想按列进行分组——Col1、Col2、Col3,并在每个组中按日期时间降序排序 然后,从每个排序的组中选出最上面的一行(即DateTime中最新的一行) 最后,透视Col3值并使用“值” 我该如何以一种有效的方式,以较小的步骤实现这一目标?提前

  • 我试图查询具有分区键和排序键的表(但是分区键和排序键是1:1,我只想使用分区键[仅返回一项]进行查询)。 这是我尝试过的代码,但没有成功(testId是分区键名,1234567890是字符串形式的分区键值);你们都知道我可以只使用分区键进行查询的方法吗?记住,由于分区键和排序键是1:1,所以只会返回一个项?提前非常感谢您。[这是我的第一篇堆栈溢出帖子-很抱歉,如果我用词不当,我很乐意回答关于我的措

  • 问题内容: 考虑数据框 使用按列定义的组,我想按预期进行排序。但是,我想将这些行保留在原处。 问题答案: 您可以用来取回新的所需索引顺序,然后用于重新排列DataFrame: 如果需要,可以将上面的两行合并为一条长行。 结果输出: 请注意,如果您不关心维护旧索引,则可以直接从中重新分配: 产生:

  • 我想在RESTful API中支持分页。 我的API方法应该通过返回产品的JSON列表http://localhost/products/v1/getproductsbycategory,可能有数千种产品,我想翻阅它们,因此我的请求应该如下所示:

  • 问题内容: 假设我们在集合中有一些项目,并且我们想使用某些比较器对它们进行排序,并期望结果在列表中: 一种方法是对列表中的项目进行排序,例如: Anothe方法正在使用排序流: 我想知道哪种方法更有效?排序流是否有任何优势(例如在多核上进行Faste排序)? 在运行时复杂性方面/最快方面是高效的。 我不相信自己要实现一个完美的基准,学习并不能真正启发我。 问题答案: 可以肯定地说,两种形式的排序都

  • 我正在使用Elasticsearch ch6.8,我在响应中得到了一个文档列表。一些文档具有相同的分数,但它们在响应列表中以相同的顺序出现。我想知道算法ES使用什么来对具有相同分数的文档进行排序?