Java中的排序集合
Posted
技术标签:
【中文标题】Java中的排序集合【英文标题】:Sorted collection in Java 【发布时间】:2010-09-29 19:09:24 【问题描述】:我是 Java 初学者。请建议可以/应该使用哪些集合来维护 Java 中的排序列表。我试过Map
和Set
,但它们不是我想要的。
【问题讨论】:
【参考方案1】:这来得太晚了,但是 JDK 中有一个类只是为了有一个排序列表。它被命名为“java.util.PriorityQueue
”(与其他Sorted*
接口有点乱)。它可以对Comparable<?>
s 或使用Comparator
进行排序。
与使用Collections.sort(...)
排序的List
的区别在于,这将始终保持偏序,具有O(log(n)) 插入性能,通过使用堆数据结构,而插入排序ArrayList
将是 O(n)(即,使用二分查找和移动)。
但是,与List
不同,PriorityQueue
不支持索引访问 (get(5)
),访问堆中项目的唯一方法是一次取出一个(因此名称为PriorityQueue
)。
【讨论】:
imo 这个答案值得更多的支持,因为它指出了唯一在 JDK 中具有该功能的 Collection 来自 Javadoc:“方法 iterator() 中提供的迭代器不能保证以任何特定顺序遍历 PriorityQueue 的元素。” @giraff:优先级队列就是这样,一种在保持优先级队列方面非常有效的数据结构。您可以从前面轮询并按排序顺序获取数据项。然而,堆内部并不维护元素的总顺序(这就是它们如此高效的原因),因此如果不执行轮询操作,就无法按顺序访问元素。 @chrispy 这在一定程度上取决于您究竟想用您的数据结构实现什么。堆是维护项目列表并稍后以有序方式检索它们的有效方法 - 使用迭代器不起作用,但如果您从中轮询,您将按顺序获取数据。检索具有破坏性,但在许多情况下仍然可以。所以我觉得这个答案很好。 @MartinProbst 请更正您的回答,明确指出该集合不能按预期顺序迭代。正如许多人所说,这非常具有误导性!【参考方案2】:TreeMap 和 TreeSet 将按排序顺序为您提供对内容的迭代。或者您可以使用 ArrayList 并使用 Collections.sort() 对其进行排序。所有这些类都在 java.util 中
【讨论】:
这有两个主要缺点,第一个是 Set 不能有重复项。第二个是,如果您使用列表和 Collections.sort(),您往往会不断地对巨大的列表进行排序并且性能很差。当然可以使用“脏”标志,但并不完全相同。 这带来了一个问题,我们是否在 Java 中有一个选项允许重复并提供类似于 Set(平衡二叉树)的性能 SortedSet/TreeMap 的另一个问题是,即使您的键是唯一的但您的不同键的比较器值相同,它们也无法工作。 A TreeSet((UniqueKey1, UniqueKey2) -> Comparator.compare(otherValueLookup.get(key1), otherValueLookup.get(key2)) 这将失败排序,因为如果 otherValue 相同,它将不会添加值!结论:TreeSet/TreeMap 获胜'如果两个键不同但根据比较器相等,则不添加值 需要补充一点,我们仍然可以使用TreeSet/TreeMap,解决方法是在key相同的情况下创建一个Randomizer:ThreadLocalRandom r = ThreadLocalRandom.current(); boolean randomBoolean = r.nextBoolean(); if (key1.equals(key2)) // ** 如果它们相等,随机比较器 ** 返回 randomBoolean 吗? 1:-1; else 返回 key1.compareTo(key2); 【参考方案3】:如果您想维护一个排序列表,您将经常修改(即,除了排序之外,还允许重复并且其元素可以有效地由索引引用),然后使用 ArrayList 但当您需要插入元素时,请始终使用 Collections.binarySearch() 来确定添加给定元素的索引。后一种方法告诉您需要插入的索引,以使您的列表保持排序。
【讨论】:
n 次插入将是 O(n^2)。 TreeSet 将为您提供更简洁的代码和 O(n log n)。 OTOH,对于 infrequent 修改,数组的二进制搜索将更快并且使用更少的内存(因此更少的 GC 开销)。 为了保持代码干净并且仍然允许重复,创建一个按排序顺序插入值的 SortedList 类很容易。 如果您能接受@TomHawtin-tackline 提到的警告,这比最受好评的答案 imo 更好的答案。对于大多数情况而言,迭代器按预期工作至关重要。 顺便提一下,Tom 在特定行为上是正确的:树集将为您提供更有效的修改。但是树集不是列表(严格意义上来说,有索引引用的元素并允许重复)并且发布者说他们想要一个列表。 谢谢你,尼尔,太棒了!【参考方案4】:使用 Google Guava 的 TreeMultiset 类。 Guava 有一个壮观的集合 API。
提供维护排序顺序的 List 实现的一个问题是在 add()
方法的 JavaDocs 中做出的承诺。
【讨论】:
感谢多集建议 +1 用于提及List
始终添加在末尾的要求。
请注意,TreeMultiSet 仍然不允许重复元素(compareTo() 返回 0,而不是 equals() 检查的元素)。如果您倾向于添加多个相同优先级的项目,它只会增加第一个添加的项目的计数,丢弃其他项目并有效地成为一个很好的计数袋实现。【参考方案5】:
您想要SortedSet 实现,即TreeSet。
【讨论】:
不一定;集合不能有重复的值。这取决于 OP 的要求。【参考方案6】:有几个选项。如果您不想重复并且您插入的对象具有可比性,我建议使用 TreeSet。
您也可以使用 Collections 类的静态方法来执行此操作。
请参阅Collections#sort(java.util.List) 和TreeSet 了解更多信息。
【讨论】:
【参考方案7】:如果您只想对列表进行排序,请使用任何类型的 List 并使用 Collections.sort()。如果您想确保列表中的元素是唯一的并且始终排序,请使用SortedSet。
【讨论】:
【参考方案8】:我所做的是实现 List 具有一个内部实例和所有委托的方法。
public class ContactList implements List<Contact>, Serializable
private static final long serialVersionUID = -1862666454644475565L;
private final List<Contact> list;
public ContactList()
super();
this.list = new ArrayList<Contact>();
public ContactList(List<Contact> list)
super();
//copy and order list
List<Contact>aux= new ArrayList(list);
Collections.sort(aux);
this.list = aux;
public void clear()
list.clear();
public boolean contains(Object object)
return list.contains(object);
之后,我实现了一个新方法“putOrdered”,如果元素不存在则插入到适当的位置,或者以防万一它存在时替换。
public void putOrdered(Contact contact)
int index=Collections.binarySearch(this.list,contact);
if(index<0)
index= -(index+1);
list.add(index, contact);
else
list.set(index, contact);
如果你想允许重复元素,只需实现 addOrdered 代替(或两者)。
public void addOrdered(Contact contact)
int index=Collections.binarySearch(this.list,contact);
if(index<0)
index= -(index+1);
list.add(index, contact);
如果您想避免插入,您还可以在“add”和“set”方法上抛出不受支持的操作异常。
public boolean add(Contact object)
throw new UnsupportedOperationException("Use putOrdered instead");
...而且您必须小心使用 ListIterator 方法,因为它们可能会修改您的内部列表。在这种情况下,您可以返回内部列表的副本或再次抛出异常。
public ListIterator<Contact> listIterator()
return (new ArrayList<Contact>(list)).listIterator();
【讨论】:
问题是这违反了List
合约。也许最好只实现Collection
。而如果ContactList
被排序,那么contains()
也可以使用binarySearch
来实现,效率更高。【参考方案9】:
实现您想要的排序列表的最有效方法是实现一个可索引的跳过列表,如下所示:Wikipedia: Indexable skiplist。 它将允许在 O(log(n)) 中进行插入/删除,并允许同时进行索引访问。它还允许重复。
Skiplist 是一种非常有趣的数据结构,我会说它被低估了。 不幸的是,Java 基础库中没有索引的跳过列表实现,但您可以使用开源实现之一或自己实现它。有常规的 Skiplist 实现,例如 ConcurrentSkipListSet 和 ConcurrentSkipListMap
【讨论】:
【参考方案10】:TreeSet 不起作用,因为它们不允许重复,而且它们不提供在特定位置获取元素的方法。 PriorityQueue 不起作用,因为它不允许在特定位置获取元素,这是列表的基本要求。 我认为您需要实现自己的算法来维护具有 O(logn) 插入时间的 Java 中的排序列表,除非您不需要重复项。 也许解决方案可能是使用 TreeMap ,其中键是覆盖 equals 方法的项目的子类,以便允许重复。
【讨论】:
【参考方案11】:使用 LambdaJ
如果您使用的是 java 8 的早期版本,您可以尝试使用 LambdaJ 解决这些任务。您可以在此处找到它:http://code.google.com/p/lambdaj/
这里有一个例子:
排序迭代
List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>()
public int compare(Person p1, Person p2)
return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
);
使用 LambdaJ 排序
List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge());
当然,有这种美感对性能有影响(平均2倍),但是你能找到更可读的代码吗?
使用 lambda 表达式与 java 8 进行排序
Collections.sort(persons, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
//or
persons.sort((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
【讨论】:
在您使用 java 8 lambda 表达式的最后一个示例中,如何反向排序? @RajatShah 我想你可以这样做-(p1.getAge().compareTo(p2.getAge()))
@RajatShah 很高兴为您提供帮助【参考方案12】:
PriorityQueue 的问题在于它是由一个简单的数组支持的,按顺序获取元素的逻辑是由“queue[2*n+1] 和 queue[2*(n+1)] “东西。如果你只是从头上拉,它会很好用,但如果你试图在某个时候调用 .toArray ,它就会变得毫无用处。
我通过使用 com.google.common.collect.TreeMultimap 解决了这个问题,但是我为值提供了一个自定义比较器,包装在 Ordering 中,永远不会返回 0。
例如。双倍:
private static final Ordering<Double> NoEqualOrder = Ordering.from(new Comparator<Double>()
@Override
public int compare(Double d1, Double d2)
if (d1 < d2)
return -1;
else
return 1;
);
这样我在调用 .toArray() 时按顺序获取值,并且也有重复。
【讨论】:
【参考方案13】:对于 Set,您可以使用 TreeSet。 TreeSet 根据自然排序或传递给该特定对象的 Comparable 的任何排序顺序对其元素进行排序。 对于地图,请使用 TreeMap。 TreeMap 提供对键的排序。要将对象作为键添加到 TreeMap,该类应实现可比较接口,该接口又强制实现包含排序顺序定义的 compare to() 方法。 http://techmastertutorial.in/java-collection-impl.html
【讨论】:
【参考方案14】:你想要的是一个二叉搜索树。它保持排序顺序,同时为搜索、删除和插入提供对数访问(除非你有一个退化的树 - 然后它是线性的)。它很容易实现,你甚至可以让它实现 List 接口,但是索引访问变得复杂了。
第二种方法是先使用 ArrayList,然后再使用冒泡排序实现。因为您一次插入或删除一个元素,所以插入和删除的访问时间是线性的。搜索是对数和索引访问常量(对于 LinkedList,时间可能会有所不同)。您需要的唯一代码是 5 行,可能是 6 行冒泡排序。
【讨论】:
【参考方案15】:你可以使用 Arraylist 和 Treemap,正如你所说的你想要重复的值,然后你不能使用 TreeSet,虽然它也是排序的,但是你必须定义比较器。
【讨论】:
【参考方案16】:使用 sort() 方法对列表进行如下排序:
List list = new ArrayList();
//add elements to the list
Comparator comparator = new SomeComparator();
Collections.sort(list, comparator);
参考链接: http://tutorials.jenkov.com/java-collections/sorting.html
【讨论】:
【参考方案17】:使用TreeSet
以排序顺序给出元素。或使用Collection.sort()
与Comparator()
进行外部排序。
【讨论】:
TreeSet 不允许重复元素。有时它是一个理想的功能,而另一些则不是。考虑到 OP 尝试了这些设置,我猜是没有 别客气,也指出TreeMap:docs.oracle.com/javase/10/docs/api/java/util/TreeMap.html【参考方案18】:import java.util.TreeSet;
public class Ass3
TreeSet<String>str=new TreeSet<String>();
str.add("dog");
str.add("doonkey");
str.add("rat");
str.add("rabbit");
str.add("elephant");
System.out.println(str);
【讨论】:
【参考方案19】:使用 Java 8 比较器,如果我们想对列表进行排序,那么这里是世界上人口最多的 10 个城市,我们想按时间报告的名称对其进行排序。 日本大阪。 ...墨西哥墨西哥城。 ... 中国北京。 ... 巴西圣保罗。 ...印度孟买。 ... 上海, 中国。 ... 印度德里。 ... 日本东京。
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class SortCityList
/*
* Here are the 10 most populated cities in the world and we want to sort it by
* name, as reported by Time. Osaka, Japan. ... Mexico City, Mexico. ...
* Beijing, China. ... São Paulo, Brazil. ... Mumbai, India. ... Shanghai,
* China. ... Delhi, India. ... Tokyo, Japan.
*/
public static void main(String[] args)
List<String> cities = Arrays.asList("Osaka", "Mexico City", "São Paulo", "Mumbai", "Shanghai", "Delhi",
"Tokyo");
System.out.println("Before Sorting List is:-");
System.out.println(cities);
System.out.println("--------------------------------");
System.out.println("After Use of List sort(String.CASE_INSENSITIVE_ORDER) & Sorting List is:-");
cities.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(cities);
System.out.println("--------------------------------");
System.out.println("After Use of List sort(Comparator.naturalOrder()) & Sorting List is:-");
cities.sort(Comparator.naturalOrder());
System.out.println(cities);
【讨论】:
【参考方案20】:根据用户定义的标准对 ArrayList 进行排序。
模型类
class Student
int rollno;
String name, address;
public Student(int rollno, String name, String address)
this.rollno = rollno;
this.name = name;
this.address = address;
public String toString()
return this.rollno + " " + this.name + " " + this.address;
排序类
class Sortbyroll implements Comparator<Student>
public int compare(Student a, Student b)
return a.rollno - b.rollno;
主类
class Main
public static void main (String[] args)
ArrayList<Student> ar = new ArrayList<Student>();
ar.add(new Student(111, "bbbb", "london"));
ar.add(new Student(131, "aaaa", "nyc"));
ar.add(new Student(121, "cccc", "jaipur"));
System.out.println("Unsorted");
for (int i=0; i<ar.size(); i++)
System.out.println(ar.get(i));
Collections.sort(ar, new Sortbyroll());
System.out.println("\nSorted by rollno");
for (int i=0; i<ar.size(); i++)
System.out.println(ar.get(i));
输出
Unsorted
111 bbbb london
131 aaaa nyc
121 cccc jaipur
Sorted by rollno
111 bbbb london
121 cccc jaipur
131 aaaa nyc
【讨论】:
【参考方案21】:为什么不自己做呢?
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
class SortedList<E extends Comparable<E>> extends ArrayList<E>
@Override
public boolean add(E e)
int i = Collections.binarySearch(this, e);
if (i < 0) i = ~i;
super.add(i, e);
return true;
// add(E e)
@Override
public void add(int index, E element)
this.add(element);
// add(int, E)
@Override
public boolean addAll(Collection<? extends E> c)
int oldSize = this.size();
for (E element : c) this.add(element);
return oldSize != this.size();
// addAll(Collection<? extends E>)
@Override
public boolean addAll(int index, Collection<? extends E> c)
int oldSize = this.size();
Iterator<? extends E> it = c.iterator();
for (int i = 0; i < index; ++i) it.next();
while (it.hasNext()) this.add(it.next());
return oldSize != this.size();
// addAll(Collection<? extends E>)
@Override
public E set(int index, E element)
E ret = this.get(index);
this.remove(index);
this.add(element);
return ret;
// set(int, E)
// SortedList<E> Class
public class Solution
public static void main(String[] args)
Random r = new Random(1);
List<Integer> sortedList = new SortedList<>();
List<Integer> unsortedList = new ArrayList<>();
for (int i = 0; i < 50; ++i)
int next = r.nextInt(1000);
sortedList.add(next);
unsortedList.add(next);
// for (int i = 0; i < 50; ++i)
System.out.println("unsortedList:");
System.out.println(unsortedList);
System.out.println("\nsortedList:");
System.out.println(sortedList);
sortedList.clear();
sortedList.addAll(unsortedList);
System.out.println("\ntest for addAll(Collection) method:");
System.out.println(sortedList);
sortedList.clear();
sortedList.addAll(30, unsortedList);
System.out.println("\ntest for addAll(int, Collection) method:");
System.out.println(sortedList);
sortedList.set(0, 999);
System.out.println("\ntest for set(int, E) method:");
System.out.println(sortedList);
// main(String[])
// Solution Class
输出:
unsortedList:
[985, 588, 847, 313, 254, 904, 434, 606, 978, 748, 569, 473, 317, 263, 562, 234, 592, 262, 596, 189, 376, 332, 310, 99, 674, 959, 298, 153, 437, 302, 205, 854, 800, 6, 363, 955, 689, 820, 75, 834, 415, 660, 477, 737, 477, 592, 220, 888, 500, 357]
sortedList:
[6, 75, 99, 153, 189, 205, 220, 234, 254, 262, 263, 298, 302, 310, 313, 317, 332, 357, 363, 376, 415, 434, 437, 473, 477, 477, 500, 562, 569, 588, 592, 592, 596, 606, 660, 674, 689, 737, 748, 800, 820, 834, 847, 854, 888, 904, 955, 959, 978, 985]
test for addAll(Collection) method:
[6, 75, 99, 153, 189, 205, 220, 234, 254, 262, 263, 298, 302, 310, 313, 317, 332, 357, 363, 376, 415, 434, 437, 473, 477, 477, 500, 562, 569, 588, 592, 592, 596, 606, 660, 674, 689, 737, 748, 800, 820, 834, 847, 854, 888, 904, 955, 959, 978, 985]
test for addAll(int, Collection) method:
[6, 75, 205, 220, 357, 363, 415, 477, 477, 500, 592, 660, 689, 737, 800, 820, 834, 854, 888, 955]
test for set(int, E) method:
[75, 205, 220, 357, 363, 415, 477, 477, 500, 592, 660, 689, 737, 800, 820, 834, 854, 888, 955, 999]
【讨论】:
以上是关于Java中的排序集合的主要内容,如果未能解决你的问题,请参考以下文章