Java中的排序集合

Posted

技术标签:

【中文标题】Java中的排序集合【英文标题】:Sorted collection in Java 【发布时间】:2010-09-29 19:09:24 【问题描述】:

我是 Java 初学者。请建议可以/应该使用哪些集合来维护 Java 中的排序列表。我试过MapSet,但它们不是我想要的。

【问题讨论】:

【参考方案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中的排序集合的主要内容,如果未能解决你的问题,请参考以下文章

Java中的集合Collections

万字长文浅析Java集合中的排序算法

java范型集合中的成员排序

Java 中List集合中自定义排序

Java:List集合内的对象进行排序

java中 List 与Set 有啥区别?