【问题标题】:Sorting a list of collections performance tips对集合性能提示列表进行排序
【发布时间】:2019-06-18 17:13:05
【问题描述】:

我有一个包含对的集合列表,我应该按照集合对键的字母顺序对列表进行排序,我目前的解决方案是通过覆盖 add 方法来保持列表排序,如下面的代码。

注意:列表集合对键始终相同

(汽车,1)(汽车,1)

(熊,1)

所以我只需要获取集合的第一对键即可对其进行排序

List<Collection<Pair<String, Integer>>> shufflingResult;

public void init() {
    shufflingResult = new ArrayList<>() {
        public boolean add(Collection<Pair<String, Integer>> c) {
            super.add(c);
            Collections.sort(shufflingResult, new Comparator<Collection<Pair<String, Integer>>>() {
                @Override
                public int compare(Collection<Pair<String, Integer>> pairs, Collection<Pair<String, Integer>> t1) {
                    return pairs.iterator().next().getKey().compareTo(t1.iterator().next().toString());
                }
            });
            return true;
        }
    };
}

这是我正在寻找的最佳性能方式吗?

【问题讨论】:

  • 应该在代码审查堆栈上

标签: java list performance collections


【解决方案1】:

性能是一件棘手的事情。最好的排序算法很大程度上取决于数据的数量和类型,以及随机程度。一些算法在部分排序的数据时是最好的,而另一些算法对于真正随机的数据是最好的。

一般来说,在您确定工作代码的性能不足之前,请担心优化问题。先搞定事情,然后确定瓶颈在哪里。它可能不是排序,而是其他的东西。

Java 提供了良好的通用排序算法。您正在使用 Collections.sort() 之一。 Java 中没有 SortedList,but javafx.base contains a SortedList 包装了提供的 List 并根据实例化时提供的 Comparator 进行排序。这将防止您必须覆盖 List 实现的基本行为。

虽然您的代码看起来可行,但这里有一些建议:

  1. pairs.iterator().next().getKey() 如果pairs为空,将抛出NPE。
  2. pairs.iterator().next().getKey() 如果对为空,将抛出 NoSuchElementException。
  3. pairs.iterator().next().getKey() 如果第一个 Pair 的空键为空,则会抛出 NPE。
  4. 所有这些对于 t1 也是如此。
  5. 您将pairs.iterator().next().getKey() 与t1.iterator().next().toString() 进行比较。一个是 Pair 的 String 表示,另一个是 Pair 中的 Key。这个对吗?

虽然您的代码可能会确保这些情况永远不会发生,但稍后有人可能会对其进行修改,从而导致令人不快的意外。您可能希望将验证添加到您的 add 方法以确保不会发生这些情况。当参数无效时抛出 IllegalArgumentException 通常是一种好习惯。

另一个想法:由于您的 Collection 内容始终相同,并且如果没有两个 Collection 具有相同类型的 Pairs,您应该能够使用 SortedMap>> 而不是一个列表。如果您通过 Key 进行比较,这种 Map 将为您整理好东西。你将put Collection 使用第一对的键作为映射/入口键。地图的keySet()values()entrySet() 都将返回按排序顺序迭代的集合。

【讨论】:

  • Java 将按照它的排序方式进行排序。这与相关操作的性能无关。使用 SortedList 是一个很好的提及,并且可能是一个不错的选择。但更不用说每次都进行排序很糟糕。
  • Arrays.sort()(这是 Collections.sort() 调用的)的默认搜索算法是 DualPivotQuickSort。在许多情况下,此版本的快速排序不会像快速排序那样退化,而且通常性能更高。快速排序和二分搜索都具有 O(n log(n)) 性能。 Java 排序很好。列表有多大(集合大小不相关)?如果它们很小,则 InsertionSort 将优于 MergeSort、QuickSort 等,尤其是在部分/大部分排序的集合上,如果您对每个插入进行排序,您将拥有这些集合。
  • 二分搜索是 log(n) 而不是 nLog(n) 因此为什么如果它已经排序会更好。
  • Generally speaking, worry about optimization until you've determined that working code is not sufficiently performant. 你为什么要担心到它变得可怕的地步。看来这是最值得关注的时候了。
  • AFAIK DualPivotQuickSort 仅用于基元。对于对象,Java 保证了稳定的排序,这排除了 QuickSort 。相反,使用了 TimSort,它可以很好地处理大部分已经排序的序列。这在 OP 的情况下很有用,因为他们的 add 的复杂性是 O(n) 而不是 O(n * log(n))
【解决方案2】:

如果集合已经排序并且您想要做的就是添加。进行二进制搜索,然后每次要插入时都使用list.add(index,element); 排序是不好的。你应该做一次,然后保持良好的插入顺序。

添加一些代码来显示 bsearch。因为集合只会返回匹配项。只需提供清单。新对象。以及按您想要的方式对列表进行排序的比较器。如果添加许多项目,其中 N> 列表的当前大小可能更好地添加所有然后排序。

private static void add(List<ThingObject> l, ThingObject t, Comparator<ThingObject> c) {
    if (l != null) {
        if (l.size() == 0) {
            l.add(t);
        } else {
            int index = bSearch(l, t, c);
            l.add(index, t);
        }
    }
}

private static int bSearch(List<ThingObject> l, ThingObject t, Comparator<ThingObject> c) {
    boolean notFound = true;
    int high = l.size() - 1;
    int low = 0;
    int look = (low + high) / 2;
    while (notFound) {
        if (c.compare(l.get(look), t) > 0) {
            // it's to the left of look
            if (look == 0 || c.compare(l.get(look - 1), t) < 0) {
                //is it adjacent?
                notFound = false;
            } else {
                //look again.
                high = look - 1;
                look = (low + high) / 2;
            }
        } else if (c.compare(l.get(look), t) < 0) {
            // it's to the right of look
            if (look == l.size() - 1 || c.compare(l.get(look + 1), t) > 0) {
                //is it adjacent?
                look = look + 1;
                notFound = false;
            } else {
                //look again.
                low = look + 1;
                look = (low + high) / 2;
            }
        } else {
            notFound = false;
        }

    }
    return look;
}

【讨论】:

    猜你喜欢
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多