在Java中,有 SortedSet
和 SortedMap
两个接口。它们都属于Java集合框架,提供了一种排序访问元素的方式。
然而,在我的理解中,Java中没有SortedList
。可以使用java.util.Collections.sort()
对列表进行排序。
为什么Java没有设计SortedList
呢?您有什么想法吗?
在Java中,有 SortedSet
和 SortedMap
两个接口。它们都属于Java集合框架,提供了一种排序访问元素的方式。
然而,在我的理解中,Java中没有SortedList
。可以使用java.util.Collections.sort()
对列表进行排序。
为什么Java没有设计SortedList
呢?您有什么想法吗?
列表迭代器首先保证您以列表的内部顺序(即插入顺序)获取列表的元素。更具体地说,它是按照您插入元素或操作列表的方式排序的。排序可以看作是对数据结构的一种操作,有多种方法可以对列表进行排序。
我会按我个人认为的有用程度的顺序列出这些方法:
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(也称为Bags),它是允许重复元素的Set
,并且有第三方实现。最著名的是来自Guava libraries的一个TreeMultiset
,它的工作原理很像TreeSet
。
Collections.sort()
对列表进行排序如上所述,List
的排序是一种数据结构的操作。因此,在需要“单一真相源”以各种方式排序的情况下,手动排序是正确的选择。
您可以使用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 在评论中链接了一个相关问题,也回答了这个问题。
在已排序集合中,您最有可能不想操纵内部数据结构,这就是为什么 PriorityQueue 没有实现 List 接口的原因(因为这会直接访问其元素)。
PriorityQueue
迭代器的注意事项PriorityQueue
类实现了 Iterable<E>
和 Collection<E>
接口,因此可以像通常一样迭代。但是,迭代器无法保证按排序顺序返回元素。相反(正如 Alderath 在评论中指出的那样),您需要使用 poll()
函数直到队列为空。
请注意,可以通过接受任何集合的构造函数将列表转换为优先队列:
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
异常。
Integer maxVal = prioQueue.peek(prioQueue.size() - 1);
。其次,如果你想把优先队列简单地用作排序列表,代码中看到“PriorityQueue”会比看到“SortedList”不太直观,如果这样的数据结构存在的话。 - AlderathCollections.sort()
方法甚至允许您使用 Comparator
对象定义用于排序的比较函数。 - Mike 'Pomax' Kamermans由于列表的概念与自动排序集合的概念不兼容。列表的重点在于,在调用list.add(7, elem)
之后,调用list.get(7)
将返回elem
。对于自动排序列表,元素可能会出现在任意位置。
list.add(index, element)
方法,但对于排序集合来说是没有意义的,但根据文档,它是可选的。 - matt-pielat由于所有列表已经按照添加项目的顺序(FIFO顺序)“排序”,您可以使用 java.util.Collections.sort()
以另一种顺序“重新排序”它们,包括元素的自然顺序。
编辑:
作为数据结构的列表基于插入项的顺序很有趣。
集合不具备该信息。
如果您想按添加时间排序,请使用List
。 如果要按其他条件排序,请使用 SortedSet
。
List
具有默认的FIFO排序(即添加项目的顺序),但是读到这句话时,我想到的是元素的自然排序,这是一种有点不同的东西。我建议进行编辑。 - usr-local-ΕΨΗΕΛΩΝ集合(Set)和映射(Map)是非线性数据结构。列表(List)是线性数据结构。
树形数据结构SortedSet
和SortedMap
接口使用红黑树实现算法分别实现了TreeSet
和TreeMap
。因此,它确保没有重复的项目(或在Map
的情况下是键)。
List
已经维护了一个有序的集合和基于索引的数据结构,而树不是基于索引的数据结构。Tree
不能包含重复的元素。List
中,我们可以有重复的元素,因此没有TreeList
(即没有SortedList
)。java.util.Collections.sort()
。它根据其元素的自然顺序将指定的列表排序为升序。SortedList
虽然花费了一些时间,但Java 8确实拥有一个排序的List
。
http://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html
正如您在javadocs中所看到的,它是JavaFX集合的一部分,旨在为ObservableList提供排序视图。
更新:请注意,随着Java 11的发布,JavaFX工具包已经移出JDK并成为一个单独的库。 JavaFX 11可作为可下载SDK或从MavenCentral获取。请参见https://openjfx.io
对于任何新手来说,从2015年4月开始,Android现在在支持库中有一个SortedList类,专门设计用于与RecyclerView
一起使用。这是关于它的博客文章。
另一个需要考虑的问题是插入操作的时间复杂度。 对于列表插入,人们期望其复杂度为O(1)。 但是对于排序列表,这不能得到保证。
最重要的一点是列表对其元素没有任何假设。
例如,您可以将不实现equals
或compare
的东西制作成列表。
List
接口不保证其方法的任何性能规范。 - Matt QuigleyArrayList
提供 O(1)
的 遍历时间,一旦到达目标位置,提供 O(1)
的访问时间。而链表则提供 O(n)
的 遍历时间,同样一旦到达目标位置,提供 O(1)
的访问时间。排序结构通常提供 O(log(n))
的 遍历时间,一旦到达目标位置,提供 O(1)
的访问速度。除了遍历之外,其他操作可能具有不同的复杂度。例如,对于它们各自的实现,插入的复杂度分别为 O(n)
、O(1)
和 O(log(n))
。 - Simply Beautiful ArtList
接口有像add(int index, E element)
、set(int index, E element)
这样的方法。它们的约定是,一旦你在位置X添加了一个元素,除非你在它之前或之后添加或删除元素,否则你将在那里找到它。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);
}
}
这并不意味着你不能对列表进行排序,但 Java 严格遵循其定义,并不默认提供已排序的列表版本。
int diff = this.score - that.score;``return (diff == 0) ? 1 : diff;
。由于这是一种不好的做法,建议将其作为匿名构造函数参数提供,而不是实现Comparable接口。 - earcam