为什么Java中没有SortedList ?

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

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

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

610206 次浏览

因为所有列表都已经按照条目的添加顺序(FIFO顺序)“排序”了,所以您可以使用另一种顺序“使用”它们,包括元素的自然顺序,使用java.util.Collections.sort()

编辑:

列表作为数据结构的基础是有趣的是插入项的顺序。

集合没有这个信息。

如果您想通过添加时间来排序,请使用List。如果您想按其他标准排序,请使用SortedSet

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

我将按照我个人看到的有用性的顺序来排序:

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(也称为Bags),这是一个允许重复元素的Set,而不是它们的第三方实现。最值得注意的是番石榴库有一个TreeMultiset,它的工作原理很像TreeSet

2. 使用Collections.sort()对列表进行排序

如上所述,对Lists进行排序是对数据结构的操作。因此,当你需要“一个真相来源”以各种方式排序时,手动排序是可行的。

您可以使用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,它对于locale敏感的字符串排序非常有用。这里有一个例子:

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在评论中链接了一个相关的问题,也回答了这个问题。

在排序集合你很可能不想操纵中,内部数据结构,这就是为什么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"

4. 编写自己的SortedList

你不应该这么做。

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

  1. 它打破了List<E>接口的约定,因为add方法应该确保元素驻留在用户指定的索引中。
  2. 为什么要重新发明轮子?你应该使用树集或多集,而不是在上面的第一点指出。

然而,如果你想把它作为一个练习,这里有一个代码示例让你开始,它使用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的默认实现将抛出UnsupportedOperationExceptions。

因为List的概念与自动排序集合的概念是不兼容的。List的意义在于,在调用list.add(7, elem)之后,调用list.get(7)将返回elem。在自动排序的列表中,元素可以位于任意位置。

可以这样想:List接口有add(int index, E element)set(int index, E element)这样的方法。约定是,一旦你在X位置添加了一个元素,你就会在那里找到它,除非你在它之前添加或删除元素。

如果任何列表实现都以某种顺序存储元素,而不是基于索引,那么上述列表方法就没有意义了。

另一点是插入操作的时间复杂度。 对于列表插入,期望复杂度为O(1)。 但是排序后的列表并不能保证这一点 最重要的一点是列表对它们的元素没有任何假设。 例如,你可以列出没有实现equalscompare的东西
List API中的第一行表示它是一个有序集合(也称为序列)。如果您对列表进行排序,则无法维护该顺序,因此Java中没有TreeList。< br > 正如API所说,Java List的灵感来自Sequence,可以看到Sequence属性http://en.wikipedia.org/wiki/Sequence_(数学)

这并不意味着您不能对列表进行排序,但是Java严格遵守了他的定义,并且默认情况下不提供列表的排序版本。

https://github.com/geniot/indexed-tree-map

考虑使用indexed-tree-map。它是一个增强的JDK的TreeSet,它提供了通过索引访问元素的功能,并且无需迭代或隐藏的底层列表来备份树,就可以找到元素的索引。该算法基于每次有变化时更新已更改节点的权重。

JavaFX # EYZ0

虽然花了一段时间,Java 8确实有一个排序的List。 # EYZ0 < / p >

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

更新:注意,在Java 11中,JavaFX工具包已经移到JDK之外,现在是一个独立的库。JavaFX 11可以作为可下载的SDK或从MavenCentral获得。看到# EYZ0

对于任何新手来说,在2015年4月,Android现在在支持库中有一个SortedList类,专门设计用于RecyclerView。以下是关于它的博客

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

Data structures schema


树数据结构SortedSetSortedMap接口使用红黑树实现算法分别实现了TreeSetTreeMap。因此,它确保没有重复的项(或键在Map的情况下)。

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

如果你正在寻找一种方法来排序元素,但也能够以一种有效的方式通过索引访问它们,你可以做以下事情:

  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);
}
}

(在这个答案中可以看到更完整的实现)

我认为以上都不能回答这个问题,原因如下:

  1. 因为同样的功能可以通过使用其他集合来实现,如TreeSet, collections, PriorityQueue..等(但这是另一种选择,它也会施加它们的约束,即Set将删除重复的元素。简单地说,即使它没有施加任何约束,它也不能回答为什么java社区没有创建SortedList的问题)
  2. 因为List元素没有实现compare/equals方法(这适用于Set &地图也在一般物品不实现可比接口,但当我们需要这些物品的排序顺序&想要使用TreeSet/TreeMap,项目应该实现Comparable接口)
  3. 因为List使用索引&由于排序它不会工作(这可以通过引入中间接口/抽象类轻松解决)

但没有人说出它背后的确切原因。因为我相信这些问题最好由java社区自己来回答,因为它只有一个&具体的答案,但让我尽我所能来回答这个问题,

我们知道排序是一个昂贵的操作,List和amp之间有一个基本的区别;Set/Map, List可以有重复,但Set/Map不能。 这就是我们为Set/Map提供TreeSet/TreeMap形式的默认实现的核心原因。在内部,这是一个红黑树,每个操作(插入/删除/搜索)都具有O (log N)的复杂性,其中由于重复列表无法适合这个数据存储结构

现在问题来了,我们也可以为List选择一个默认的排序方法,比如归并排序,它被Collections.sort(list)方法使用,复杂度为O(nlogn)。社区并没有故意这样做,因为我们确实有多种选择的排序算法为非不同的元素,如快速排序,ShellSort, RadixSort…等等。未来还会有更多。有时,同一种排序算法根据要排序的数据执行不同的操作。因此,他们想保留这个选项,让我们自己选择。Set/Map不是这样,因为O (log N)是最好的排序复杂度。

我们有Collections.sort(arr)方法,它可以帮助数组列表arr排序。以desc方式排序,我们可以使用Collections.sort(arr, Collections.reverseOrder())