Bộ sưu tập được sắp xếp trong Java


161

Tôi là người mới bắt đầu sử dụng Java. Vui lòng đề xuất (các) bộ sưu tập nào có thể / nên được sử dụng để duy trì danh sách được sắp xếp trong Java. Tôi đã thử MapSet, nhưng đó không phải là thứ tôi đang tìm kiếm.

Câu trả lời:


187

Điều này đến rất muộn, nhưng có một lớp trong JDK chỉ với mục đích có một danh sách được sắp xếp. Nó được đặt tên (hơi lỗi so với các Sorted*giao diện khác ) " java.util.PriorityQueue". Nó có thể sắp xếp Comparable<?>s hoặc sử dụng a Comparator.

Sự khác biệt với Listcách sử dụng Collections.sort(...)được sắp xếp là điều này sẽ duy trì một thứ tự từng phần, với hiệu suất chèn O (log (n)), bằng cách sử dụng cấu trúc dữ liệu heap, trong khi chèn vào một sắp xếp ArrayListsẽ là O (n) (nghĩa là, sử dụng tìm kiếm nhị phân và di chuyển).

Tuy nhiên, không giống như a List, PriorityQueuekhông hỗ trợ truy cập được lập chỉ mục ( get(5)), cách duy nhất để truy cập các mục trong một đống là lấy chúng ra, mỗi lần một cái (như tên PriorityQueue).


4
imo câu trả lời này xứng đáng nhận được nhiều sự ủng hộ hơn vì nó chỉ ra Bộ sưu tập duy nhất có khả năng trong JDK
nimcap

96
Từ Javadoc: "Trình lặp được cung cấp trong phương thức iterator () không được đảm bảo để duyệt qua các phần tử của PriorityQueue theo bất kỳ thứ tự cụ thể nào."
Christoffer Hammarström

10
@giraff: hàng đợi ưu tiên chỉ là một cấu trúc dữ liệu rất hiệu quả trong việc giữ hàng ưu tiên. Bạn có thể thăm dò ý kiến ​​từ phía trước và nhận các mục dữ liệu theo thứ tự được sắp xếp. Tuy nhiên, các đống không duy trì tổng số các phần tử bên trong (đó là lý do tại sao chúng rất hiệu quả), vì vậy không có cách nào để truy cập các phần tử theo thứ tự mà không thực hiện thao tác thăm dò ý kiến.
Martin Probst

2
@chrispy nó phụ thuộc một chút vào chính xác những gì bạn muốn đạt được với cấu trúc dữ liệu của bạn. Heaps là một cách hiệu quả để duy trì danh sách các mục và truy xuất chúng theo thứ tự sau này - sử dụng iterator không hoạt động, nhưng nếu bạn thăm dò ý kiến ​​từ đó, bạn sẽ nhận được dữ liệu của mình theo thứ tự. Truy xuất là phá hoại, nhưng điều đó vẫn có thể ổn trong nhiều trường hợp. Vì vậy, tôi nghĩ rằng câu trả lời này là tốt.
Martin Probst

4
@MartinProbst Vui lòng chỉnh sửa câu trả lời của bạn thành RAR RÀNG chỉ ra rằng bộ sưu tập KHÔNG THỂ ĐƯỢC BẮT ĐẦU theo thứ tự dự kiến. Như nhiều người đã nói, đây là sự hiểu lầm cực đoan!
Jorge Galvão

53

TreeMap và TreeSet sẽ cung cấp cho bạn một lần lặp lại các nội dung theo thứ tự được sắp xếp. Hoặc bạn có thể sử dụng một ArrayList và sử dụng Collections.sort () để sắp xếp nó. Tất cả các lớp đó đều ở trong java.util


27
Có hai nhược điểm chính của vấn đề này, đó là điều đầu tiên mà Bộ không thể có các bản sao. Thứ hai là nếu bạn sử dụng một danh sách và Collections.sort (), bạn có xu hướng liên tục sắp xếp các danh sách lớn và cho hiệu suất kém. Chắc chắn bạn có thể sử dụng cờ 'bẩn', nhưng nó không hoàn toàn giống nhau.
Jeach

Điều đó đặt ra một câu hỏi liệu chúng ta có một tùy chọn trong Java cho phép các bản sao và cũng cho các hiệu suất tương tự như Set (Cây nhị phân cân bằng)
mankadnandan

32

Nếu bạn muốn duy trì một danh sách được sắp xếp mà bạn sẽ thường xuyên sửa đổi (nghĩa là một cấu trúc, ngoài việc được sắp xếp, cho phép trùng lặp và các phần tử có thể được tham chiếu hiệu quả theo chỉ mục), sau đó sử dụng ArrayList nhưng khi bạn cần chèn một phần tử , luôn luôn sử dụng Collections.binarySearch () để xác định chỉ mục mà bạn thêm một yếu tố nhất định. Phương thức sau cho bạn biết chỉ mục bạn cần chèn vào để giữ danh sách của bạn theo thứ tự được sắp xếp.


11
n chèn sẽ là O (n ^ 2). Một TreeSet sẽ cung cấp cho bạn mã sạch hơn và O (n log n). OTOH, để tìm kiếm nhị phân sửa đổi không thường xuyên của một mảng sẽ nhanh hơn và sử dụng ít bộ nhớ hơn (do đó chi phí sử dụng ít hơn).
Tom Hawtin - tackline

Để giữ mã sạch và vẫn cho phép sao chép, việc tạo một lớp SortedList sẽ chèn các giá trị theo thứ tự được sắp xếp là đủ dễ dàng.
Ông Shiny và Mới 安

Đây là một cách trả lời tốt hơn so với câu trả lời được đánh giá cao nhất, imo, nếu bạn có thể sống với lời cảnh báo được đề cập bởi @ TomHawtin-tackline. Việc lặp đó hoạt động như mong đợi là rất quan trọng đối với hầu hết các trường hợp.
DuneCat

Ngẫu nhiên, Tom đúng với hành vi cụ thể đó: một bộ cây sẽ cho bạn sửa đổi hiệu quả hơn. Nhưng một tập hợp cây không phải là một danh sách (theo nghĩa chặt chẽ nhất là có các yếu tố được tham chiếu bởi chỉ mục và cho phép trùng lặp) và người đăng cho biết họ muốn có một danh sách.
Neil Coffey

Cảm ơn Neil, điều đó thật tuyệt vời!
vikingsteve

31

Sử dụng lớp TreeMultiset của Google Guava . Quả ổi có một bộ sưu tập API ngoạn mục.

Một vấn đề với việc cung cấp một triển khai Danh sách duy trì thứ tự được sắp xếp là lời hứa được thực hiện trong JavaDocs của add()phương thức.


Kudos cho đề xuất nhiều trang
darthtrevino

5
+1 để đề cập đến yêu cầu Listluôn luôn thêm vào cuối.
Roland Illig

2
Lưu ý rằng TreeMultiSet vẫn không cho phép các phần tử trùng lặp (các phần tử so sánh () trả về 0, không phải kiểm tra bằng ()). Nếu bạn có xu hướng thêm nhiều hơn một mục có cùng mức độ ưu tiên, nó sẽ chỉ tăng số lượng của mục đầu tiên được thêm vào, loại bỏ mục khác và thực sự là một cách thực hiện tốt cho việc đếm túi.
bekce


12

Có một vài lựa chọn. Tôi muốn đề xuất TreeSet nếu bạn không muốn trùng lặp và các đối tượng bạn đang chèn có thể so sánh được.

Bạn cũng có thể sử dụng các phương thức tĩnh của lớp Bộ sưu tập để làm điều này.

Xem Bộ sưu tập # sort (java.util.List)Treeset để biết thêm thông tin.


10

Nếu bạn chỉ muốn sắp xếp một danh sách, hãy sử dụng bất kỳ loại Danh sách nào và sử dụng Collections.sort () . Nếu bạn muốn đảm bảo các thành phần trong danh sách là duy nhất và luôn được sắp xếp, hãy sử dụng Sắp xếp .


5

Những gì tôi đã làm là triển khai Danh sách có một thể hiện bên trong với tất cả các phương thức được ủy quyền.

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

Sau đó, tôi đã triển khai một phương thức mới "putOrdered", chèn vào vị trí thích hợp nếu phần tử không tồn tại hoặc thay thế chỉ trong trường hợp nó tồn tại.

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

Nếu bạn muốn cho phép các phần tử lặp đi lặp lại, chỉ cần thực hiện addOrdered thay thế (hoặc cả hai).

public void addOrdered(Contact contact) {
    int index=Collections.binarySearch(this.list,contact);
    if(index<0){
        index= -(index+1);
    }
    list.add(index, contact);
}

Nếu bạn muốn tránh chèn, bạn cũng có thể ném và ngoại lệ hoạt động không được hỗ trợ trên các phương thức "thêm" và "đặt".

public boolean add(Contact object) {
    throw new UnsupportedOperationException("Use putOrdered instead");
}

... Và bạn cũng phải cẩn thận với các phương thức ListIterator vì chúng có thể sửa đổi danh sách nội bộ của bạn. Trong trường hợp này, bạn có thể trả lại một bản sao của danh sách nội bộ hoặc ném lại một ngoại lệ.

public ListIterator<Contact> listIterator() {
    return (new ArrayList<Contact>(list)).listIterator();
}

Vấn đề là điều này vi phạm Listhợp đồng. Có lẽ sẽ tốt hơn nếu chỉ thực hiện Collection. Và nếu ContactListđược sắp xếp, sau đó contains()có thể được thực hiện bằng cách sử dụng binarySearchđể có hiệu quả hơn.
Marcono1234

5

Cách hiệu quả nhất để thực hiện một danh sách được sắp xếp như bạn muốn là triển khai một skiplist có thể lập chỉ mục như ở đây: Wikipedia: Skiplist có thể lập chỉ mục . Nó sẽ cho phép có các phần chèn / xóa trong O (log (n)) và sẽ cho phép có quyền truy cập được lập chỉ mục cùng một lúc. Và nó cũng sẽ cho phép trùng lặp.

Skiplist là một thứ khá thú vị và, tôi muốn nói, cấu trúc dữ liệu bị đánh giá thấp. Thật không may, không có triển khai skiplist được lập chỉ mục trong thư viện cơ sở Java, nhưng bạn có thể sử dụng một trong các triển khai nguồn mở hoặc tự thực hiện nó. Có triển khai Skiplist thường xuyên như ConcurrentSkipListSetConcurrentSkipListMap


4

TreeSet sẽ không hoạt động vì chúng không cho phép trùng lặp cộng với việc chúng không cung cấp phương thức để tìm nạp phần tử ở vị trí cụ thể. PriorityQueue sẽ không hoạt động vì nó không cho phép tìm nạp các phần tử ở vị trí cụ thể, đây là một yêu cầu cơ bản cho danh sách. Tôi nghĩ rằng bạn cần phải thực hiện thuật toán của riêng mình để duy trì một danh sách được sắp xếp trong Java với thời gian chèn O (logn), trừ khi bạn không cần trùng lặp. Có lẽ một giải pháp có thể đang sử dụng TreeMap trong đó khóa là lớp con của mục ghi đè phương thức bằng để sao cho phép trùng lặp.


4

Sử dụng LambdaJ

Bạn có thể thử giải quyết các tác vụ này với LambdaJ nếu bạn đang sử dụng các phiên bản trước cho java 8. Bạn có thể tìm thấy nó ở đây: http://code.google.com.vn/p/lambdaj/

Ở đây bạn có một ví dụ:

Sắp xếp lặp đi lặp lại

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

Sắp xếp với LambdaJ

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

Tất nhiên, có loại tác động làm đẹp này trong hiệu suất (trung bình 2 lần), nhưng bạn có thể tìm thấy một mã dễ đọc hơn không?

Sắp xếp với java 8 bằng biểu thức lambda

Collections.sort(persons, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
//or
persons.sort((p1, p2) -> p1.getAge().compareTo(p2.getAge()));

Trong ví dụ cuối cùng của bạn với biểu thức lambda java 8, làm thế nào để sắp xếp ngược lại?
Rajat Shah

1
@RajatShah Tôi đoán bạn chỉ có thể làm-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza

@RajatShah rất vui khi được giúp đỡ
Federico Piazza

2

Vấn đề với PriorityQueue là nó được sao lưu bởi một mảng đơn giản và logic lấy các phần tử theo thứ tự được thực hiện bởi "queue [2 * n + 1] và queue [2 * (n + 1)]". Nó hoạt động rất tốt nếu bạn chỉ cần kéo từ đầu, nhưng làm cho nó vô dụng nếu bạn đang cố gắng gọi .toArray trên đó vào một lúc nào đó.

Tôi giải quyết vấn đề này bằng cách sử dụng com.google.common.collect.TreeMultimap, nhưng tôi cung cấp Trình so sánh tùy chỉnh cho các giá trị, được gói trong Đơn đặt hàng, không bao giờ trả về 0.

Ví dụ. cho đôi:

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

Bằng cách này, tôi nhận được các giá trị theo thứ tự khi tôi gọi .toArray () và cũng có các bản sao.


1

Những gì bạn muốn là một cây tìm kiếm nhị phân. Nó duy trì thứ tự được sắp xếp trong khi cung cấp quyền truy cập logarit cho các tìm kiếm, xóa và chèn (trừ khi bạn có một cây bị thoái hóa - sau đó là tuyến tính). Nó khá dễ thực hiện và thậm chí bạn có thể làm cho nó thực hiện giao diện Danh sách, nhưng sau đó việc truy cập chỉ mục trở nên phức tạp.

Cách tiếp cận thứ hai là có một ArrayList và sau đó thực hiện sắp xếp bong bóng. Bởi vì bạn đang chèn hoặc xóa một phần tử tại một thời điểm, thời gian truy cập để chèn và xóa là tuyến tính. Các tìm kiếm là hằng số truy cập logarit và chỉ mục (thời gian có thể khác nhau đối với LinkedList). Mã duy nhất bạn cần là 5, có thể là 6 dòng sắp xếp bong bóng.


1

Bạn có thể sử dụng Arraylist và Treemap, như bạn đã nói rằng bạn cũng muốn các giá trị lặp lại, sau đó bạn không thể sử dụng TreeSet, mặc dù nó cũng được sắp xếp, nhưng bạn phải xác định bộ so sánh.


1

Đối với Set, bạn có thể sử dụng Treeset. TreeSet sắp xếp các phần tử của nó trên cơ sở thứ tự tự nhiên hoặc bất kỳ thứ tự sắp xếp nào được chuyển đến So sánh cho đối tượng cụ thể đó. Đối với bản đồ, sử dụng TreeMap. TreeMap cung cấp việc sắp xếp các khóa. Để thêm một đối tượng làm khóa cho TreeMap, lớp đó sẽ triển khai giao diện có thể so sánh, từ đó buộc phải thực hiện so sánh với phương thức () có chứa định nghĩa của thứ tự sắp xếp. http://techmastertutorial.in/java-collection-impl.html



0

Sử dụng TreeSetmà đưa ra các yếu tố theo thứ tự sắp xếp. HOẶC sử dụng Collection.sort()để phân loại bên ngoài với Comparator().


Treeset không cho phép sao chép các phần tử. Đôi khi nó là một tính năng mong muốn, những người khác thì không. Xem xét OP đã thử các bộ, tôi đoán không
usr-local-ΕΨΗΕΛΩΝ

Đừng có ý đó, hãy chỉ ra TreeMap nữa: docs.oracle.com/javase/10/docs/api/java/util/TreeMap.html
Haakon Løtveit

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

với Trình so sánh Java 8, nếu chúng tôi muốn sắp xếp danh sách thì đây là 10 thành phố đông dân nhất thế giới và chúng tôi muốn sắp xếp nó theo tên, theo báo cáo của Time. Osaka, Nhật Bản. ... Thành phố Mexico, Mexico. ... Bắc Kinh, Trung Quốc. ... São Paulo, Brazil. ... Mumbai, Ấn Độ. ... Thượng Hải, Trung Quốc. ... Delhi, Ấn Độ. ... Tokyo, Nhật Bản.

 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

Sắp xếp một ArrayList theo tiêu chí do người dùng xác định.

Lớp mẫu

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

Lớp sắp xếp

 class Sortbyroll implements Comparator<Student> 
 {         
     public int compare(Student a, Student b) 
     { 
         return a.rollno - b.rollno; 
     } 
 } 

Lớp chính

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

Đầu ra

 Unsorted
 111 bbbb london
 131 aaaa nyc
 121 cccc jaipur

 Sorted by rollno
 111 bbbb london
 121 cccc jaipur
 131 aaaa nyc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.