为什么Java中没有SortedList?

时间:2012-01-04 10:35:54

标签: java sorting collections

在Java中,有SortedSetSortedMap接口。两者都属于Java的标准集合框架,并提供了一种访问元素的排序方式。

但是,根据我的理解,Java中没有SortedList。您可以使用java.util.Collections.sort()对列表进行排序。

知道为什么它的设计是这样的吗?

12 个答案:

答案 0 :(得分:615)

列表迭代器首先保证您按列表的内部顺序获取列表的元素(又名。插入顺序)。更具体地说,它是按照您插入元素的顺序或您操作列表的顺序。排序可以看作是对数据结构的操纵,有几种方法可以对列表进行排序。

我会按照有用性的顺序订购方式,我个人认为:

1。请考虑使用SetBag集合

注意:我将此选项置于顶部,因为这是您通常想要做的事情。

有序集会在插入时自动对集合进行排序,这意味着它会在您向集合中添加元素时进行排序。这也意味着您无需手动对其进行排序。

此外,如果您确定不需要担心(或有)重复元素,那么您可以使用TreeSet<T>代替。它实现了SortedSetNavigableSet接口,并且可以按照您可能期望的列表工作:

TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding

for (String s : set) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

如果您不想要自然排序,可以使用带Comparator<T>的构造函数参数。

或者,您可以使用 Multisets(也称为行李,即允许重复元素的Set,而不是是他们的第三方实现。最值得注意的是Guava librariesTreeMultiset,其效果与TreeSet非常相似。

2。使用Collections.sort()

对列表进行排序

如上所述,List s的排序是对数据结构的操纵。因此,对于需要“一个真实来源”的情况,这些情况将以各种方式排序,然后手动排序是可行的。

您可以使用java.util.Collections.sort()方法对列表进行排序。以下是如何使用的代码示例:

List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

Collections.sort(strings);
for (String s : strings) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

使用比较器

一个明显的好处是您可以在sort方法中使用Comparator。 Java还为Comparator提供了一些实现,例如Collator,这对于区域设置敏感的排序字符串很有用。这是一个例子:

Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing

Collections.sort(strings, usCollator);

在并发环境中排序

请注意,虽然在并发环境中使用sort方法并不友好,因为集合实例将被操作,您应该考虑使用不可变集合。这是Guava在Ordering课程中提供的内容,是一个简单的单行程序:

List<string> sorted = Ordering.natural().sortedCopy(strings);

3。使用java.util.PriorityQueue

换行列表

虽然Java中没有排序列表但是有一个排序的队列可能对你来说也很合适。它是java.util.PriorityQueue类。

Nico Haase在评论中将related questionconstructor that takes any collection联系起来。

在排序集合中,您很可能不想操纵内部数据结构,这就是PriorityQueue不实现List接口的原因(因为这样可以直接访问其元素) 。

警告PriorityQueue迭代器

PriorityQueue类实现Iterable<E>Collection<E>接口,因此可以像往常一样进行迭代。但是,不保证迭代器以排序顺序返回元素。相反(正如Alderath在评论中指出的那样)你需要poll()队列直到空。

请注意,您可以通过{{3}}:

将列表转换为优先级队列
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
    System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"

4。编写自己的SortedList

注意:您不应该这样做。

您可以编写自己的List类,每次添加新元素时都会对其进行排序。根据你的实现并且毫无意义,这可能会导致相当大的计算,除非你想把它作为一个练习,因为有两个主要原因:

  1. 它违反了List<E>接口的合同,因为add方法应该确保该元素将驻留在用户指定的索引中。
  2. 为什么重新发明轮子?您应该使用TreeSet或Multisets,如上面第一点所指出的那样。
  3. 但是,如果您想要将其作为练习,这里有一个代码示例来帮助您入门,它使用AbstractList抽象类:

    public class SortedList<E> extends AbstractList<E> {
    
        private ArrayList<E> internalList = new ArrayList<E>();
    
        // Note that add(E e) in AbstractList is calling this one
        @Override 
        public void add(int position, E e) {
            internalList.add(e);
            Collections.sort(internalList, null);
        }
    
        @Override
        public E get(int i) {
            return internalList.get(i);
        }
    
        @Override
        public int size() {
            return internalList.size();
        }
    
    }
    

    请注意,如果您没有覆盖所需的方法,那么AbstractList的默认实现将抛出UnsupportedOperationException

答案 1 :(得分:62)

因为List的概念与自动排序的集合的概念不兼容。列表的重点是,在致电list.add(7, elem)后,对list.get(7)的调用将返回elem。使用自动排序列表,元素可以最终处于任意位置。

答案 2 :(得分:23)

由于所有列表已经按添加项目的顺序“排序”(FIFO排序),因此您可以使用java.util.Collections.sort()“使用其他订单”(包括元素的自然排序)“求助”它们。

编辑:

列表,因为数据结构基于有趣的是插入项目的顺序。

集合没有该信息。

如果您想通过添加时间来订购,请使用List。如果您想按其他条件订购,请使用SortedSet

答案 3 :(得分:13)

Set和Map是非线性数据结构。列表是线性数据结构。

enter image description here

树数据结构SortedSetSortedMap接口分别使用已使用的Red-Black tree实现算法实现TreeSetTreeMap。因此,它确保没有重复的项目(或Map的情况下的密钥)。

  • List已经维护了一个有序的集合和基于索引的数据结构,树不是基于索引的数据结构。
  • 根据定义,
  • Tree不能包含重复项。
  • List我们可以有重复项,因此没有TreeList(即没有SortedList)。
  • 列表按插入顺序维护元素。因此,如果我们要对列表进行排序,我们必须使用java.util.Collections.sort()。它根据元素的自然顺序将指定列表按升序排序。

答案 4 :(得分:9)

JavaFX SortedList

虽然花了一段时间,但Java 8确实有一个排序Listhttp://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html

正如您在javadoc中所看到的,它是JavaFX集合的一部分,旨在提供ObservableList的排序视图。

更新:请注意,对于Java 11,JavaFX工具包已移出JDK,现在是一个单独的库。 JavaFX 11可作为可下载的SDK或MavenCentral提供。见https://openjfx.io

答案 5 :(得分:9)

对于任何新手,截至2015年4月,Android现在在支持库中有一个SortedList类,专门用于与RecyclerView一起使用。这是关于它的blog post

答案 6 :(得分:4)

另一点是插入操作的时间复杂性。 对于列表插入,人们期望O(1)的复杂性。 但是使用排序列表无法保证这一点。

最重要的一点是,列表不假设其元素。 例如,您可以列出未实现equalscompare

的内容

答案 7 :(得分:3)

可以这样想:List界面包含add(int index, E element)set(int index, E element)等方法。合同是,一旦你在X位置添加了一个元素,你就会在那里找到它,除非你在它之前添加或删除元素。

如果任何列表实现以某种顺序存储元素而不是基于索引,则上面的列表方法没有意义。

答案 8 :(得分:2)

List API中的第一行表示它是一个有序集合(也称为序列)。如果对列表进行排序,则无法维护顺序,因此Java中没有TreeList。
正如API所说,Java List受到了Sequence的启发,并且看到了序列属性http://en.wikipedia.org/wiki/Sequence_(mathematics

这并不意味着你不能对列表进行排序,而是Java严格按照他的定义排序,并且默认情况下不提供列表的排序版本。

答案 9 :(得分:1)

考虑使用indexed-tree-map。它是一个增强的JDK TreeSet,它通过索引提供对元素的访问,并查找没有迭代的元素索引或者备份树的隐藏底层列表。该算法基于每次发生变化时更新节点的权重。

答案 10 :(得分:0)

如果您正在寻找一种对元素进行排序的方法,但又能够通过索引有效地访问它们,则可以执行以下操作:

  1. 使用随机访问列表进行存储(例如ArrayList
  2. 确保始终对其进行排序

然后添加或删除元素,您可以使用Collections.binarySearch获取插入/删除索引。由于列表实现了随机访问,因此可以使用确定的索引来有效地修改列表。

示例:

/**
 * @deprecated
 *      Only for demonstration purposes. Implementation is incomplete and does not 
 *      handle invalid arguments.
 */
@Deprecated
public class SortingList<E extends Comparable<E>> {
    private ArrayList<E> delegate;

    public SortingList() {
        delegate = new ArrayList<>();
    }

    public void add(E e) {
        int insertionIndex = Collections.binarySearch(delegate, e);

        // < 0 if element is not in the list, see Collections.binarySearch
        if (insertionIndex < 0) {
            insertionIndex = -(insertionIndex + 1);
        }
        else {
            // Insertion index is index of existing element, to add new element 
            // behind it increase index
            insertionIndex++;
        }

        delegate.add(insertionIndex, e);
    }

    public void remove(E e) {
        int index = Collections.binarySearch(delegate, e);
        delegate.remove(index);
    }

    public E get(int index) {
        return delegate.get(index);
    }
}

答案 11 :(得分:0)

由于以下原因,我认为以上所有内容均无法回答该问题,

  1. 由于可以通过使用其他集合(例如TreeSet,Collections,PriorityQueue..etc()来实现相同的功能,但这是一种替代方法,它也将施加其约束,即Set将删除重复的元素。没有施加任何约束,也没有回答为什么Java社区未创建SortedList的问题
  2. 因为List元素未实现compare / equals方法(这对于Set&Map也适用,在一般情况下,项目不实现Comparable接口,但是当我们需要这些项目按排序顺序并要使用TreeSet时, / TreeMap,项目应实现Comparable接口
  3. 由于列表使用索引,并且由于排序而无法使用(引入中间接口/抽象类很容易处理

但是没有人说出它的确切原因,因为我相信Java社区本身可以最好地回答这类问题,因为它只有一个特定的答案,但是让我尽力回答以下问题,< / p>

我们知道排序是一项昂贵的操作,并且List与Set / Map之间存在一个基本区别,即List可以重复,而Set / Map不能重复。 这就是为什么我们以TreeSet / TreeMap的形式获得Set / Map默认实现的核心原因。在内部,这是一棵红黑树,每个操作(插入/删除/搜索)的复杂度为 O(log N),其中由于重复,List无法容纳此数据存储结构。

现在出现问题了,我们还可以为List选择默认的排序方法,例如 MergeSort ,它由Collections.sort(list)方法使用,复杂度为 O(N log N) 。社区没有故意这样做,因为我们确实有多种选择,可以对非独特元素(例如 QuickSort,ShellSort,RadixSort ...等)进行排序。将来会有更多。同样有时,相同的排序算法根据要排序的数据执行不同的操作。因此,他们希望将此选项保持打开状态,并留给我们选择。 Set / Map并非如此,因为 O(log N)是最好的排序复杂度。