เรียงลำดับการรวบรวมใน Java


161

ฉันเป็นผู้เริ่มต้นใน Java กรุณาแนะนำคอลเลกชันที่สามารถ / ควรใช้สำหรับการรักษารายการเรียงลำดับใน Java ฉันได้พยายามMapและSetแต่พวกเขาก็ไม่ได้สิ่งที่ผมกำลังมองหา

คำตอบ:


187

สิ่งนี้มาช้ามาก แต่มีคลาสใน JDK เพียงเพื่อจุดประสงค์ในการจัดเรียงรายการ มันถูกตั้งชื่อ (ค่อนข้างผิดปกติกับSorted*อินเทอร์เฟซอื่น) " java.util.PriorityQueue" มันสามารถจัดเรียงอย่างใดอย่างหนึ่งหรือใช้Comparable<?>Comparator

ความแตกต่างกับการListเรียงลำดับการใช้Collections.sort(...)คือสิ่งนี้จะรักษาลำดับบางส่วนตลอดเวลาด้วยประสิทธิภาพการแทรก O (log (n)) โดยใช้โครงสร้างข้อมูล heap ในขณะที่การแทรกในการเรียงArrayListจะเป็น O (n) (เช่น ใช้การค้นหาและย้ายแบบไบนารี่)

อย่างไรก็ตามแตกต่างจาก a List, PriorityQueueไม่รองรับการเข้าถึงที่จัดทำดัชนี ( get(5)), วิธีเดียวที่จะเข้าถึงไอเท็มในฮีปคือการนำพวกเขาออกทีละครั้ง (เช่นชื่อPriorityQueue)


4
imo คำตอบนี้สมควรได้รับ upvotes มากขึ้นเนื่องจากจะชี้ให้เห็นคอลเล็กชันเดียวที่มีความสามารถใน JDK
nimcap

96
จาก Javadoc: "Iterator ที่ระบุใน method iterator () ไม่รับประกันว่าจะสำรวจองค์ประกอบของ PriorityQueue ตามลำดับใด ๆ "
Christoffer Hammarström

10
@giraff: คิวลำดับความสำคัญเป็นเพียงโครงสร้างข้อมูลที่มีประสิทธิภาพมากในการรักษาลำดับความสำคัญ คุณสามารถสำรวจความคิดเห็นจากด้านหน้าและรับรายการข้อมูลในลำดับที่เรียงลำดับ อย่างไรก็ตามฮีปไม่ได้รักษาลำดับทั้งหมดขององค์ประกอบไว้ภายใน (นั่นเป็นสาเหตุที่ทำให้มีประสิทธิภาพมาก) ดังนั้นจึงไม่มีวิธีการเข้าถึงองค์ประกอบตามลำดับโดยไม่ต้องดำเนินการสำรวจความคิดเห็น
Martin Probst

2
@ chrispy มันขึ้นอยู่กับว่าคุณต้องการทำอะไรกับโครงสร้างข้อมูลของคุณ Heaps เป็นวิธีที่มีประสิทธิภาพในการเก็บรักษารายการและเรียกคืนในลำดับที่มีการสั่งซื้อในภายหลัง - การใช้ตัววนซ้ำไม่ทำงาน แต่ถ้าคุณทำแบบสำรวจคุณจะได้รับข้อมูลตามลำดับ การสืบค้นนั้นเป็นสิ่งที่ทำลายล้าง แต่ก็ยังสามารถใช้ได้ในหลายกรณี ดังนั้นฉันคิดว่าคำตอบนี้ดี
Martin Probst

4
@MartinProbst โปรดแก้ไขคำตอบของคุณให้ชัดเจนว่าการรวบรวมนั้นไม่สามารถทำซ้ำได้ตามคำสั่งที่คาดไว้ อย่างที่หลายคนบอกว่านี่เป็นสิ่งที่ทำให้เข้าใจผิดอย่างสุดขั้ว!
Jorge Galvão

53

TreeMap และ TreeSet จะให้คุณทำซ้ำเนื้อหาตามลำดับ หรือคุณสามารถใช้ ArrayList และใช้ Collections.sort () เพื่อจัดเรียง คลาสทั้งหมดเหล่านั้นอยู่ใน java.util


27
แม้ว่าจะมีข้อเสียเปรียบหลักสองประการประการแรกสิ่งที่ Set ไม่สามารถทำซ้ำได้ อย่างที่สองคือถ้าคุณใช้ list และ Collections.sort () คุณมักจะเรียงลำดับรายการใหญ่ ๆ และให้ประสิทธิภาพต่ำ แน่ใจว่าคุณสามารถใช้ธง 'สกปรก' แต่ก็ไม่เหมือนกัน
Jeach

นั่นนำมาสู่คำถามว่าเรามีตัวเลือกใน Java ซึ่งอนุญาตให้ดูลีเชียลและให้การแสดงที่คล้ายกับ Set (ต้นไม้ Binary แบบสมดุล) หรือไม่
mankadnandan

32

หากคุณต้องการรักษารายการที่เรียงซึ่งคุณมักจะแก้ไข (เช่นโครงสร้างซึ่งนอกเหนือจากการเรียงลำดับอนุญาตให้ทำซ้ำและองค์ประกอบที่สามารถอ้างอิงได้อย่างมีประสิทธิภาพโดยดัชนี) จากนั้นใช้ ArrayList แต่เมื่อคุณต้องการแทรกองค์ประกอบ ใช้Collections.binarySearch () เพื่อกำหนดดัชนีที่คุณเพิ่มองค์ประกอบที่กำหนด เมธอดหลังจะบอกดัชนีที่คุณต้องการแทรกที่จะเก็บรายการของคุณตามลำดับ


11
n ส่วนแทรกจะเป็น O (n ^ 2) TreeSet จะให้โค้ดที่สะอาดกว่าและ O (n log n) OTOH สำหรับการค้นหาไบนารีการปรับเปลี่ยนไม่บ่อยครั้งของอาเรย์จะเร็วขึ้นและใช้หน่วยความจำน้อยลง
Tom Hawtin - tackline

เพื่อรักษารหัสที่สะอาดและยังคงอนุญาตให้ทำซ้ำมันจะง่ายพอที่จะสร้างคลาส SortedList ที่แทรกค่าตามลำดับที่เรียง
นายเงางามและใหม่安宇

นี่เป็นคำตอบที่ดีกว่าคำตอบ upvoted ที่สุดถ้าคุณสามารถอยู่กับคำเตือนที่ @ TomHawtin-tackline พูดถึงได้ การทำงานซ้ำตามที่คาดไว้เป็นสิ่งสำคัญสำหรับกรณีส่วนใหญ่
DuneCat

อนึ่งทอมนั้นถูกต้องกับพฤติกรรมเฉพาะนั้น: ชุดต้นไม้จะให้การปรับเปลี่ยนที่มีประสิทธิภาพมากขึ้น แต่ชุดต้นไม้ไม่ได้เป็นรายการ (ในความหมายที่เข้มงวดที่สุดของการมีองค์ประกอบอ้างอิงโดยดัชนีและอนุญาตให้ทำซ้ำ) และโปสเตอร์บอกว่าพวกเขาต้องการรายการ
Neil Coffey

ขอบคุณนีลนั่นช่างน่ากลัวมาก!
vikingsteve

31

ใช้คลาสTreeMultisetของ Google Guava Guavaมี API คอลเล็กชันที่งดงาม

ปัญหาหนึ่งที่เกิดขึ้นกับการจัดให้มีการอิมพลีเมนต์ List ที่รักษาลำดับการเรียงไว้คือสัญญาที่ทำไว้ใน JavaDocs ของadd()เมธอด


รุ่งโรจน์สำหรับข้อเสนอแนะชุดมัลติ
darthtrevino

5
+1 สำหรับการกล่าวถึงข้อกำหนดที่Listเพิ่มเข้ามาเสมอในตอนท้าย
Roland Illig

2
โปรดทราบว่า TreeMultiSet ยังคงไม่อนุญาตให้มีองค์ประกอบที่ซ้ำกัน (องค์ประกอบที่ compareTo () ส่งคืนค่า 0 ไม่ใช่การตรวจสอบเท่ากับ () หากคุณมีแนวโน้มที่จะเพิ่มรายการที่มีลำดับความสำคัญเดียวกันมากกว่าหนึ่งรายการมันจะเพิ่มจำนวนของรายการที่เพิ่มครั้งแรกเพียงการละทิ้งรายการอื่น ๆ และเป็นการใช้งานกระเป๋านับที่มีประสิทธิภาพ
bekce


12

มีตัวเลือกน้อย ฉันขอแนะนำ TreeSet หากคุณไม่ต้องการให้มีรายการที่ซ้ำกันและวัตถุที่คุณใส่มีการเปรียบเทียบ

คุณยังสามารถใช้วิธีการคงที่ของคลาส Collections เพื่อทำสิ่งนี้

ดูที่Collections # sort (java.util.List)และTreeSetสำหรับข้อมูลเพิ่มเติม


10

หากคุณเพียงแค่ต้องการที่จะเรียงลำดับรายการให้ใช้ชนิดของใด ๆรายการและใช้Collections.sort () หากคุณต้องการให้แน่ใจว่าองค์ประกอบในรายการที่เป็นเอกลักษณ์และเรียงเสมอใช้SortedSet


5

สิ่งที่ฉันได้ทำคือการใช้รายการที่มีอินสแตนซ์ภายในที่มีวิธีการทั้งหมดที่ได้รับมอบหมาย

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

หากคุณต้องการหลีกเลี่ยงการแทรกคุณสามารถโยนและไม่สนับสนุนข้อยกเว้นการดำเนินการในวิธีการ "เพิ่ม" และ "ตั้ง"

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

5

วิธีที่มีประสิทธิภาพมากที่สุดในการใช้รายการที่เรียงลำดับตามที่คุณต้องการคือการใช้ skiplist ที่จัดทำดัชนีได้เช่นเดียวกับที่นี่: Wikipedia: skiplist ที่จัดทำดัชนีได้ มันจะช่วยให้มีการแทรก / ลบใน O (log (n)) และจะอนุญาตให้มีการเข้าถึงดัชนีในเวลาเดียวกัน และมันก็จะอนุญาตให้ซ้ำกัน

Skiplist นั้นค่อนข้างน่าสนใจและผมอยากบอกว่าโครงสร้างข้อมูลที่มีการประเมินต่ำเกินไป น่าเสียดายที่ไม่มีการใช้งาน skiplist ที่จัดทำดัชนีไว้ในไลบรารีฐาน Java แต่คุณสามารถใช้การใช้งานโอเพนซอร์ซหรือใช้งานด้วยตัวคุณเอง มีการใช้งาน Skiplist ปกติเช่นConcurrentSkipListSetและConcurrentSkipListMap


4

TreeSet จะไม่ทำงานเพราะไม่อนุญาตให้มีการทำซ้ำและจะไม่ให้วิธีการดึงข้อมูลองค์ประกอบที่ตำแหน่งที่เฉพาะเจาะจง PriorityQueue จะไม่ทำงานเพราะไม่อนุญาตให้เรียกองค์ประกอบที่ตำแหน่งเฉพาะซึ่งเป็นข้อกำหนดขั้นพื้นฐานสำหรับรายการ ฉันคิดว่าคุณต้องใช้อัลกอริทึมของคุณเองเพื่อรักษารายการเรียงลำดับใน Java ที่มีเวลาแทรก O (logn) เว้นแต่ว่าคุณไม่ต้องการทำซ้ำ บางทีโซลูชันอาจใช้ TreeMap โดยที่คีย์เป็นคลาสย่อยของรายการที่แทนที่เมธอด equals เพื่ออนุญาตการทำซ้ำ


4

การใช้ LambdaJ

คุณสามารถลองแก้ปัญหาเหล่านี้ด้วยLambdaJหากคุณใช้รุ่นก่อนหน้านี้กับ java 8 คุณสามารถค้นหาได้ที่นี่: 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 ครั้ง) แต่คุณสามารถหารหัสที่อ่านได้มากขึ้นหรือไม่?

เรียงลำดับด้วย java 8 โดยใช้แลมบ์ดานิพจน์

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

ในตัวอย่างสุดท้ายของคุณด้วยนิพจน์แลมบ์ดาของ java 8 จะเรียงลำดับแบบย้อนกลับได้อย่างไร?
Rajat Shah

1
@RajatShah ฉันคิดว่าคุณสามารถทำได้-(p1.getAge().compareTo(p2.getAge()))
Federico Piazza

@RajatShah ดีใจที่ได้ช่วยเหลือ
Federico Piazza

2

ปัญหาของ PriorityQueue คือมันถูกสำรองข้อมูลโดยอาเรย์อย่างง่ายและตรรกะที่ได้รับองค์ประกอบตามลำดับนั้นทำได้โดย "คิว [2 * n + 1] และคิว [2 * (n + 1)]" thingie มันใช้งานได้ดีถ้าคุณดึงออกจากหัว แต่ทำให้มันไร้ประโยชน์ถ้าคุณพยายามโทรหา. toArray ในบางจุด

ฉันแก้ไขปัญหานี้โดยใช้ com.google.common.collect.TreeMultimap แต่ฉันให้ตัวเปรียบเทียบที่กำหนดเองสำหรับค่าในการสั่งซื้อซึ่งจะไม่ส่งคืนค่า 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 () และยังมีซ้ำ


1

สิ่งที่คุณต้องการคือแผนภูมิการค้นหาแบบไบนารี มันรักษาลำดับการจัดเรียงในขณะที่ให้การเข้าถึงลอการิทึมสำหรับการค้นหาการลบและการแทรก (เว้นแต่ว่าคุณจะมีต้นไม้ที่เสื่อมโทรม มันค่อนข้างง่ายที่จะติดตั้งและคุณสามารถทำให้มันใช้ List Interface ได้ แต่การเข้าถึงดัชนีนั้นซับซ้อน

วิธีที่สองคือการมี ArrayList และการปรับใช้การเรียงลำดับฟอง เนื่องจากคุณกำลังแทรกหรือลบองค์ประกอบหนึ่งครั้งเวลาเข้าถึงสำหรับการแทรกและการลบจึงเป็นแบบเชิงเส้น การค้นหาเป็นลอการิทึมและค่าคงที่การเข้าถึงดัชนี (ครั้งสามารถแตกต่างกันสำหรับ LinkedList) รหัสเดียวที่คุณต้องการคือ 5 อาจจะเรียงลำดับฟองได้ 6 บรรทัด


1

คุณสามารถใช้ Arraylist และ Treemap อย่างที่คุณบอกว่าคุณต้องการค่าซ้ำ ๆ เช่นกันจากนั้นคุณไม่สามารถใช้ TreeSet ได้แม้ว่ามันจะถูกจัดเรียงเช่นกัน แต่คุณต้องกำหนดตัวเปรียบเทียบ


1

สำหรับ Set คุณสามารถใช้ TreeSet ชุดคำสั่ง TreeSet เป็นองค์ประกอบตามลำดับธรรมชาติหรือลำดับการเรียงลำดับใด ๆ ที่ส่งผ่านไปเปรียบเทียบได้สำหรับวัตถุนั้น ๆ สำหรับแผนที่ใช้ TreeMap TreeMap จัดให้มีการเรียงลำดับมากกว่ากุญแจ ในการเพิ่มวัตถุเป็นกุญแจสำคัญใน TreeMap นั้นคลาสนั้นควรใช้อินเตอร์เฟสที่เทียบเคียงกันซึ่งจะบังคับให้ใช้วิธีการเปรียบเทียบกับ () ซึ่งมีคำจำกัดความของคำสั่งการเรียงลำดับ http://techmastertutorial.in/java-collection-impl.html



0

ใช้TreeSetที่ให้องค์ประกอบในการเรียงลำดับ หรือใช้สำหรับการเรียงลำดับภายนอกที่มีCollection.sort()Comparator()


TreeSet ไม่อนุญาตให้ทำซ้ำองค์ประกอบ บางครั้งมันเป็นคุณสมบัติที่ต้องการคนอื่นไม่ได้ เมื่อพิจารณาจาก OP ลองใช้ชุดฉันเดาว่าไม่มี
usr-local-ΕΨΗΕΛΩΝ

อย่าหมายความว่าชี้ให้เห็น TreeMap ด้วยเช่นกัน: 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

ด้วย 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 Model

 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.