在Java中,有SortedSet
和SortedMap
接口。两者都属于Java的标准集合框架,并提供了一种访问元素的排序方式。
但是,根据我的理解,Java中没有SortedList
。您可以使用java.util.Collections.sort()
对列表进行排序。
知道为什么它的设计是这样的吗?
答案 0 :(得分:615)
列表迭代器首先保证您按列表的内部顺序获取列表的元素(又名。插入顺序)。更具体地说,它是按照您插入元素的顺序或您操作列表的顺序。排序可以看作是对数据结构的操纵,有几种方法可以对列表进行排序。
我会按照有用性的顺序订购方式,我个人认为:
Set
或Bag
集合注意:我将此选项置于顶部,因为这是您通常想要做的事情。
有序集会在插入时自动对集合进行排序,这意味着它会在您向集合中添加元素时进行排序。这也意味着您无需手动对其进行排序。
此外,如果您确定不需要担心(或有)重复元素,那么您可以使用TreeSet<T>
代替。它实现了SortedSet
和NavigableSet
接口,并且可以按照您可能期望的列表工作:
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 libraries有TreeMultiset
,其效果与TreeSet
非常相似。
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);
java.util.PriorityQueue
虽然Java中没有排序列表但是有一个排序的队列可能对你来说也很合适。它是java.util.PriorityQueue
类。
Nico Haase在评论中将related question与constructor 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"
SortedList
类注意:您不应该这样做。
您可以编写自己的List类,每次添加新元素时都会对其进行排序。根据你的实现并且毫无意义,这可能会导致相当大的计算,除非你想把它作为一个练习,因为有两个主要原因:
List<E>
接口的合同,因为add
方法应该确保该元素将驻留在用户指定的索引中。但是,如果您想要将其作为练习,这里有一个代码示例来帮助您入门,它使用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是非线性数据结构。列表是线性数据结构。
树数据结构SortedSet
和SortedMap
接口分别使用已使用的Red-Black tree实现算法实现TreeSet
和TreeMap
。因此,它确保没有重复的项目(或Map
的情况下的密钥)。
List
已经维护了一个有序的集合和基于索引的数据结构,树不是基于索引的数据结构。Tree
不能包含重复项。List
我们可以有重复项,因此没有TreeList
(即没有SortedList
)。java.util.Collections.sort()
。它根据元素的自然顺序将指定列表按升序排序。答案 4 :(得分:9)
SortedList
虽然花了一段时间,但Java 8确实有一个排序List
。
http://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)的复杂性。 但是使用排序列表无法保证这一点。
最重要的一点是,列表不假设其元素。
例如,您可以列出未实现equals
或compare
。
答案 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)
如果您正在寻找一种对元素进行排序的方法,但又能够通过索引有效地访问它们,则可以执行以下操作:
ArrayList
)然后添加或删除元素,您可以使用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)
由于以下原因,我认为以上所有内容均无法回答该问题,
但是没有人说出它的确切原因,因为我相信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)是最好的排序复杂度。