การเรียงลำดับ ArrayList ของวัตถุโดยใช้ลำดับการเรียงลำดับที่กำหนดเอง


119

ฉันต้องการใช้คุณลักษณะการจัดเรียงสำหรับแอปพลิเคชันสมุดที่อยู่ของฉัน

ฉันต้องการจัดเรียงArrayList<Contact> contactArrayไฟล์. Contactเป็นชั้นเรียนที่มีสี่ช่อง ได้แก่ ชื่อหมายเลขบ้านหมายเลขโทรศัพท์มือถือและที่อยู่ nameฉันต้องการที่จะเรียงลำดับใน

ฉันจะเขียนฟังก์ชันการจัดเรียงแบบกำหนดเองเพื่อทำสิ่งนี้ได้อย่างไร

คำตอบ:


270

นี่คือบทแนะนำเกี่ยวกับการสั่งซื้อวัตถุ:

ถึงแม้จะยกตัวอย่างไปบ้าง แต่ก็อยากแนะนำให้อ่านอยู่ดี


มีหลายวิธีในการจัดเรียงArrayListไฟล์. หากคุณต้องการที่จะกำหนดธรรมชาติ (เริ่มต้น) การสั่งซื้อแล้วคุณต้องปล่อยให้ดำเนินการContact Comparableสมมติว่าคุณต้องการจัดเรียงตามค่าเริ่มต้นnameจากนั้นทำ (ละเว้น nullcheck เพื่อความเรียบง่าย):

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

เพื่อให้คุณสามารถทำได้

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

หากคุณต้องการกำหนดคำสั่งที่ควบคุมได้ภายนอก (ซึ่งจะแทนที่ลำดับธรรมชาติ) คุณจะต้องสร้างComparator:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

คุณสามารถกำหนดComparators ในContactตัวเองเพื่อให้คุณสามารถใช้ซ้ำได้แทนที่จะสร้างขึ้นใหม่ทุกครั้ง:

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

ซึ่งสามารถใช้ได้ดังต่อไปนี้:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

และในการทาครีมด้านบนคุณสามารถพิจารณาใช้ตัวเปรียบเทียบ javabean ทั่วไป :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

ซึ่งคุณสามารถใช้ได้ดังต่อไปนี้:

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(ตามที่คุณเห็นในโค้ดอาจมีการครอบคลุมช่องว่างเพื่อหลีกเลี่ยง NPE ระหว่างการเรียงลำดับ)


2
ฉันจะเพิ่มความเป็นไปได้ในการกำหนดตัวเปรียบเทียบล่วงหน้าหลายตัวแล้วใช้ตามชื่อ ...
Stobor

2
อันที่จริงฉันเพิ่งทำ ง่ายกว่าการพยายามอธิบายตัวเอง
Stobor

@BalusC: ไม่มี probs. ฉันไม่สามารถรับเครดิตสำหรับแนวคิดนี้ฉันได้รับมาจากString.CASE_INSENSITIVE_ORDERและเพื่อน ๆ แต่ฉันชอบมัน ทำให้รหัสผลลัพธ์อ่านง่ายขึ้น
Stobor

1
คำจำกัดความของตัวเปรียบเทียบเหล่านั้นก็น่าจะเป็นstaticและอาจจะfinalเกินไป ... หรืออะไรทำนองนั้น ..
Stobor

อิอิ ... BeanComparator ก็เหมือน Awesome On A Stick! :-) (ฉันจำการเปรียบเทียบตรรกะที่แน่นอนของ null ไม่ได้ แต่มันต้องการ(o1 == null && o2 == null) ? 0 :ที่จุดเริ่มต้นของบรรทัดผลตอบแทนนั้นหรือไม่)
Stobor

28

นอกเหนือจากสิ่งที่โพสต์ไปแล้วคุณควรทราบว่าเนื่องจาก Java 8 เราสามารถย่อโค้ดของเราให้สั้นลงและเขียนได้ดังนี้:

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

หรือตั้งแต่ตอนนี้รายการมีsortวิธีการ

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

คำอธิบาย:

ตั้งแต่ Java 8 อินเทอร์เฟซการทำงาน (อินเทอร์เฟซที่มีวิธีนามธรรมเพียงวิธีเดียว - สามารถมีวิธีการเริ่มต้นหรือแบบคงที่มากกว่า) สามารถใช้งานได้อย่างง่ายดายโดยใช้:

  • lambdas arguments -> body
  • หรืออ้างอิงวิธี source::method

เนื่องจากComparator<T>มีวิธีนามธรรมเพียงวิธีเดียวint compare(T o1, T o2)จึงเป็นส่วนต่อประสานการทำงาน

แทน (ตัวอย่างจากคำตอบ@BalusC )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

เราสามารถลดรหัสนี้เป็น:

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

เราสามารถทำให้แลมด้า (หรืออื่น ๆ ) ง่ายขึ้นได้โดยการข้าม

  • ประเภทอาร์กิวเมนต์ (Java จะอนุมานตามลายเซ็นของวิธีการ)
  • หรือ{return...}

ดังนั้นแทนที่จะเป็น

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

เราเขียนได้

(one, other) -> one.getAddress().compareTo(other.getAddress())

ขณะนี้Comparatorมีวิธีการคงที่เช่นcomparing(FunctionToComparableValue)หรือcomparing(FunctionToValue, ValueComparator)ที่เราสามารถใช้เพื่อสร้างตัวเปรียบเทียบได้อย่างง่ายดายซึ่งควรเปรียบเทียบค่าเฉพาะบางอย่างจากวัตถุ

กล่าวอีกนัยหนึ่งเราสามารถเขียนโค้ดด้านบนใหม่เป็นไฟล์

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).

8

หน้านี้จะบอกทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับการเรียงลำดับคอลเลกชันเช่น ArrayList

โดยทั่วไปคุณต้อง

  • ทำให้Contactชั้นเรียนของคุณใช้Comparableอินเทอร์เฟซโดย
    • สร้างวิธีการpublic int compareTo(Contact anotherContact)ภายใน
  • เมื่อคุณทำเช่นนี้คุณก็สามารถโทรCollections.sort(myContactList);,
    • ที่myContactListเป็นArrayList<Contact>(หรือคอลเลกชันอื่น ๆ ของContact)

มีอีกวิธีหนึ่งเช่นกันซึ่งเกี่ยวข้องกับการสร้างคลาสตัวเปรียบเทียบและคุณสามารถอ่านเกี่ยวกับเรื่องนี้ได้จากหน้าที่เชื่อมโยงเช่นกัน

ตัวอย่าง:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5

BalusC และ bguiz ได้ให้คำตอบที่สมบูรณ์มากเกี่ยวกับวิธีใช้ตัวเปรียบเทียบในตัวของ Java

ฉันแค่อยากจะเพิ่มว่าคอลเล็กชันของ Google มีคลาสการสั่งซื้อซึ่ง "ทรงพลัง" มากกว่าเครื่องมือเปรียบเทียบมาตรฐาน อาจคุ้มค่าที่จะลองดู คุณสามารถทำสิ่งที่ยอดเยี่ยมเช่นการเรียงลำดับการเรียงลำดับการย้อนกลับการสั่งซื้อโดยขึ้นอยู่กับผลลัพธ์ของฟังก์ชันสำหรับวัตถุของคุณ ...

นี่คือบล็อกโพสต์ที่กล่าวถึงประโยชน์บางประการ


โปรดทราบว่า Google คอลเลคชันเป็นส่วนหนึ่งของ Guava (ไลบรารี java ทั่วไปของ Google) ดังนั้นคุณอาจต้องพึ่งพา Guava (หรือโมดูลคอลเลกชันของ Guava) หากคุณต้องการใช้คลาส Ordering
Etienne Neveu

4

คุณต้องทำให้คลาสติดต่อของคุณใช้งานComparableจากนั้นจึงใช้compareTo(Contact)วิธีการ ด้วยวิธีนี้ Collections.sort จะสามารถจัดเรียงให้คุณได้ ตามหน้าที่ฉันเชื่อมโยงไป CompareTo 'ส่งคืนจำนวนเต็มลบศูนย์หรือจำนวนเต็มบวกเนื่องจากวัตถุนี้มีค่าน้อยกว่าเท่ากับหรือมากกว่าวัตถุที่ระบุ'

ตัวอย่างเช่นหากคุณต้องการจัดเรียงตามชื่อ (A ถึง Z) ชั้นเรียนของคุณจะมีลักษณะดังนี้:

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

ทำงานได้ดีกับฉันขอบคุณ! ฉันยังใช้ CompareToIgnoreCase เพื่อละเว้นกรณี
Rani Kheir

3

เมื่อใช้lambdajคุณสามารถจัดเรียงคอลเล็กชันผู้ติดต่อของคุณ (เช่นตามชื่อ) ดังต่อไปนี้

sort(contacts, on(Contact.class).getName());

หรือตามที่อยู่:

sort(contacts, on(Contacts.class).getAddress());

และอื่น ๆ โดยทั่วไปมี DSL เพื่อเข้าถึงและจัดการคอลเล็กชันของคุณในหลาย ๆ วิธีเช่นการกรองหรือการจัดกลุ่มผู้ติดต่อของคุณตามเงื่อนไขบางประการรวมมูลค่าคุณสมบัติบางอย่างเป็นต้น


0

Collections.sort เป็นการใช้งานแบบเรียงลำดับที่ดี หากคุณไม่มีการใช้งานที่เทียบเคียงได้สำหรับการติดต่อคุณจะต้องผ่านการใช้งานตัวเปรียบเทียบ

หมายเหตุ:

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

การเรียงลำดับการผสานน่าจะดีกว่าอัลกอริทึมการค้นหาส่วนใหญ่ที่คุณทำได้


0

ฉันทำได้โดยวิธีต่อไปนี้ หมายเลขและชื่อเป็นรายการอาร์เรย์สองรายการ ฉันต้องเรียงลำดับชื่อหากมีการเปลี่ยนแปลงใด ๆ เกิดขึ้นกับลำดับชื่ออาร์เรย์รายการอาร์เรย์ก็จะเปลี่ยนลำดับด้วย

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}

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

0

ใช้วิธีนี้:

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

`

และการใช้งาน: mySortedlist = sortList(myList); ไม่จำเป็นต้องใช้ตัวเปรียบเทียบในชั้นเรียนของคุณ หากคุณต้องการสลับคำสั่งผกผัน1และ-1


0

ตกลงฉันรู้ว่ามีคำตอบเมื่อนานมาแล้ว ... แต่นี่คือข้อมูลใหม่:

สมมติว่าคลาสผู้ติดต่อที่เป็นปัญหามีลำดับตามธรรมชาติที่กำหนดไว้แล้วโดยใช้การเปรียบเทียบ แต่คุณต้องการลบล้างลำดับนั้นให้พูดตามชื่อ นี่คือวิธีที่ทันสมัยในการทำ:

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

วิธีนี้จะจัดเรียงตามชื่อก่อน (ตามลำดับย้อนกลับ) จากนั้นสำหรับการชนกันของชื่อมันจะกลับไปเป็นลำดับ 'ธรรมชาติ' ที่ดำเนินการโดยคลาสผู้ติดต่อเอง


-1

คุณใช้ฟังก์ชัน Arrays.sort ชั้นเรียนที่มีควรใช้การเปรียบเทียบ


นั่นคือเหตุผลที่ฉันพูดว่า Arrays
พระสงฆ์

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