Java中的排序集合


161

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

Answers:


187

这来得很晚,但是JDK中只有一个类是为了拥有排序列表。它被命名(与其他Sorted*接口有些混乱)“ java.util.PriorityQueue”。它可以排序Comparable<?>或使用Comparator

Listsorted using 的不同之Collections.sort(...)处在于,通过使用堆数据结构,这将始终保持部分顺序,具有O(log(n))插入性能,而在sorted中插入ArrayList将为O(n)(即,使用二进制搜索和移动)。

但是,与不支持索引访问()不同的是List访问堆中项目的唯一方法是一次将它们取出(因此命名为)。PriorityQueueget(5)PriorityQueue


4
imo这个答案值得更多的批评,因为它指出了唯一具有JDK功能的Collection
nimcap 2010年

96
在Javadoc中:“不保证方法iterator()中提供的Iterator以任何特定顺序遍历PriorityQueue的元素。”
ChristofferHammarström

10
@giraff:优先级队列就是这样,它是一种非常有效地保留优先级队列的数据结构。您可以从前面进行轮询,并按排序顺序获取数据项。但是,堆不会在内部维护元素的总顺序(这就是为什么它们如此高效的原因),因此,如果不执行poll操作,就无法按顺序访问元素。
Martin Probst

2
@chrispy取决于您要使用数据结构实现的目标。堆是维护项目列表并在以后以有序方式检索它们的有效方法-使用迭代器不起作用,但是如果从中进行轮询,则可以按顺序获取数据。检索具有破坏性,但在许多情况下仍然可以。所以我认为这个答案很好。
马丁·普罗布斯特

4
@MartinProbst请更正您的答案,以明确表明不能按预期顺序对该收藏进行迭代。正如许多人所说,这极具误导性!
豪尔赫·加尔文2014年

53

TreeMap和TreeSet将按排序顺序为您提供内容的迭代。或者,您可以使用ArrayList并使用Collections.sort()对其进行排序。所有这些类都在java.util中


27
但是,这样做有两个主要缺点,第一个是Set不能有重复项。第二个问题是,如果使用列表和Collections.sort(),则往往会不断对巨大的列表进行排序,从而导致性能不佳。当然可以使用“脏”标志,但这并不完全相同。
Jeach 2010年

这就带来了一个问题,我们是否在Java中是否有一个选项可以允许复制并提供类似于Set(平衡二叉树)的性能
mankadnandan

32

如果你想保持一个排序列表,你会经常修改,当你需要插入的元素(即一个结构,除了进行排序,允许重复,其内容可以通过索引有效地引用),然后使用ArrayList但,请始终使用Collections.binarySearch()确定添加给定元素的索引。后一种方法告诉您需要插入的索引,以使列表保持排序。


11
n个插入将为O(n ^ 2)。TreeSet将为您提供更清晰的代码和O(n log n)。OTOH,对于不经常修改的数组,二进制搜索将更快并且使用更少的内存(因此,更少的GC开销)。
Tom Hawtin-定位线

为了保持代码整洁并仍允许重复,创建一个SortedList类以按顺序插入值将很容易。
Shiny先生和新安宇1'Jan

如果您可以忍受@ TomHawtin-tackline提到的警告,那么这是比最喜欢的答案imo更好的答案。在大多数情况下,迭代器按预期工作至关重要。
DuneCat 2012年

顺便说一句,汤姆(Tom)对这种特定行为是正确的:树集将为您提供更有效的修改。但是树集不是列表(从严格意义上说,元素被索引引用并允许重复),并且张贴者说他们想要一个列表。
尼尔·科菲

谢谢尼尔,那真是太棒了!
vikingsteve 2014年

31

使用Google Guava的TreeMultiset类。番石榴有一个壮观的收藏API。

提供保持排序顺序的List实现的一个问题是该add()方法的JavaDocs中做出的承诺。


多重建议的荣誉
darthtrevino 2011年

5
+1是指List始终在末尾加上a的要求。
罗兰·伊利格

2
注意TreeMultiSet仍然不允许重复的元素(compareTo()返回0的元素,而不是equals()检查的元素)。如果您倾向于添加多个优先级相同的项,则只会增加第一个添加项的数量,而丢弃其他项,实际上是一个很好的Counting Bag实现。
bekce 2012年




5

我要做的是实现具有内部实例的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,从而更加高效。
Marcono1234 '19

5

实现所需排序列表的最有效方法是实现可索引的跳转列表,如下所示:Wikipedia:可索引的跳转列表。它将允许在O(log(n))中进行插入/删除操作,并允许同时进行索引访问。而且还允许重复。

跳过列表非常有趣,我想说是低估的数据结构。不幸的是,Java基库中没有索引的跳过列表实现,但是您可以使用一种开源实现,也可以自己实现。有常规的Skiplist实现,例如ConcurrentSkipListSetConcurrentSkipListMap


4

TreeSet将不起作用,因为它们不允许重复,而且它们不提供在特定位置获取元素的方法。PriorityQueue无法使用,因为它不允许在特定位置获取元素,这是列表的基本要求。我认为您需要实现自己的算法以使用O(logn)插入时间在Java中维护一个排序列表,除非您不需要重复的。可能的解决方案是使用TreeMap,其中键是覆盖equals方法的项的子类,以便允许重复项。


4

使用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表达式的示例中,如何进行反向排序?
Rajat Shah

1
@RajatShah我想你可以办到-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza

@RajatShah很高兴为您提供帮助
Federico Piazza

2

PriorityQueue的问题在于它由一个简单的数组备份,而按顺序获取元素的逻辑由“ queue [2 * n + 1]和queue [2 *(n + 1)]”事物完成。如果您只是拔掉头,它会很好用,但是如果您试图在某个时候在其上调用.toArray,它将使其无用。

我通过使用com.google.common.collect.TreeMultimap解决了此问题,但我为定值包装的值提供了自定义比较器,该值从不返回0。

例如 对于Double:

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()时就可以按顺序获取值,并且也有重复项。


1

您想要的是二叉搜索树。它保持排序顺序,同时提供对数搜索,删除和插入操作(除非您有退化的树-然后是线性的)。它非常容易实现,甚至可以使它实现List接口,但是索引访问变得很复杂。

第二种方法是先创建一个ArrayList,然后再进行冒泡排序。因为您一次要插入或删除一个元素,所以插入和删除的访问时间是线性的。搜索是对数和索引访问常量(LinkedList的时间可能不同)。您所需的唯一代码是5,也许是6行冒泡排序。


1

您可以使用Arraylist和Treemap,就像您所说的那样,您还需要重复的值,然后就不能使用TreeSet了,尽管TreeSet也进行了排序,但是您必须定义比较器。


1

对于Set,您可以使用TreeSet。TreeSet根据自然顺序或传递给该特定对象的Comparable的任何排序顺序对元素进行排序。对于地图,请使用TreeMap。TreeMap提供了对键的排序。为了将一个对象添加为TreeMap的键,该类应实现可比较的接口,从而强制实现具有排序顺序定义的compare()方法。 http://techmastertutorial.in/java-collection-impl.html




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

0

使用Java 8 Comparator,如果我们想对列表进行排序,则这是世界上人口最多的10个城市,我们希望按名称对它进行排序(如Time所报道)。日本大阪。墨西哥墨西哥城。... 中国北京。……巴西圣保罗。……印度孟买。... 上海,中国。……印度德里。... 日本东京。

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

    }

}

0

根据用户定义的标准对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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.