Özel bir sıralama düzeni kullanarak nesnelerin DiziListesini sıralama


119

Adres defteri uygulamam için bir sıralama özelliği uygulamaya çalışıyorum.

Bir sıralamak istiyorum ArrayList<Contact> contactArray. Contactdört alan içeren bir sınıftır: isim, ev numarası, cep telefonu numarası ve adres. Sıralamak istiyorum name.

Bunu yapmak için özel bir sıralama işlevi nasıl yazabilirim?

Yanıtlar:


270

Nesnelerin sıralanmasıyla ilgili bir öğretici:

Bazı örnekler verecek olsam da yine de okumanızı tavsiye ederim.


Bir ArrayList. Doğal (varsayılan) bir sıralama tanımlamak istiyorsanız , Contactuygulamaya izin vermeniz gerekir Comparable. Varsayılan olarak sıralamak istediğinizi varsayarsak name, sonra yapın (basitlik için nullchecks atlanmıştır):

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

böylece yapabilirsin

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

Collections.sort(contacts);

Harici olarak kontrol edilebilir bir sıralama tanımlamak istiyorsanız (bu, doğal sıralamayı geçersiz kılar), o zaman bir yaratmanız gerekir 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());
    }
}); 

Her seferinde yeniden oluşturmak yerine onları yeniden kullanabilmeniz için s'leri kendi Comparatoriçinde bile tanımlayabilirsiniz 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);
        }
    };

}

aşağıdaki gibi kullanılabilir:

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

Üstünü kapatmak için genel bir javabe karşılaştırıcısı kullanmayı düşünebilirsiniz :

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

}

aşağıdaki gibi kullanabilirsiniz:

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

(kodda gördüğünüz gibi, sıralama sırasında NPE'leri önlemek için muhtemelen boş alanlar zaten kaplanmıştır)


2
Birkaç karşılaştırıcıyı önceden tanımlama ve sonra bunları adıyla kullanma
olasılığını eklerdim

2
Aslında yaptım. Kendimi açıklamaya çalışmaktan daha kolay.
Stobor

@BalusC: Sorun yok. Fikir için kredi alamam, onu String.CASE_INSENSITIVE_ORDERve arkadaşlarımdan aldım ama hoşuma gitti. Elde edilen kodun daha kolay okunmasını sağlar.
Stobor

1
Bu Karşılaştırıcı tanımları muhtemelen staticve belki de finalöyle olmalı ... Veya bunun gibi bir şey ..
Stobor

Heheh ... BeanComparator, Awesome On A Stick gibidir! :-) ( (o1 == null && o2 == null) ? 0 :
Boşların

28

Önceden gönderilenlere ek olarak, Java 8'den beri kodumuzu kısaltabileceğimizi ve şu şekilde yazabileceğimizi bilmelisiniz:

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

veya List artık sortmetoda sahip olduğundan

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

Açıklama:

Java 8'den beri, işlevsel arayüzler (yalnızca bir soyut yönteme sahip arayüzler - daha fazla varsayılan veya statik yönteme sahip olabilirler) aşağıdakiler kullanılarak kolayca uygulanabilir:

Bu yana Comparator<T>, yalnızca tek bir soyut yöntemi vardırint compare(T o1, T o2) işlevsel arabirimdir.

Bunun yerine ( @BalusC cevabından örnek )

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

bu kodu şu şekilde azaltabiliriz:

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

Bu (veya herhangi bir) lambda'yı atlayarak basitleştirebiliriz

  • bağımsız değişken türleri (Java bunları yöntem imzasına göre çıkaracaktır)
  • veya {return...}

Yani yerine

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

yazabiliriz

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

Ayrıca artık veya Comparatorgibi statik yöntemler varcomparing(FunctionToComparableValue)comparing(FunctionToValue, ValueComparator) nesnelerden bazı belirli değerleri karşılaştırması gereken Karşılaştırıcıları kolayca oluşturmak için kullanabileceğimiz .

Başka bir deyişle, yukarıdaki kodu şu şekilde yeniden yazabiliriz:

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

8

Bu sayfa , ArrayList gibi koleksiyonları sıralama hakkında bilmeniz gereken her şeyi anlatır.

Temelde ihtiyacın var

  • senin yapmak Contactsınıf uygulamak Comparabletarafından arabirimi
    • public int compareTo(Contact anotherContact)içinde bir yöntem yaratmak .
  • Bunu yaptıktan sonra arayabilirsin Collections.sort(myContactList);,
    • nerede myContactListolduğu ArrayList<Contact>(veya başka bir koleksiyonu Contact).

Bir Comparator sınıfı oluşturmanın başka bir yolu daha var ve bunu bağlantılı sayfadan da okuyabilirsiniz.

Misal:

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 ve bguiz, Java'nın yerleşik Karşılaştırıcılarının nasıl kullanılacağına dair zaten çok eksiksiz yanıtlar verdiler.

Sadece google-koleksiyonlarının standart Karşılaştırıcılardan daha "güçlü" bir Sıralama sınıfına sahip olduğunu eklemek istiyorum . Kontrol etmeye değer olabilir. Sıralamaları birleştirmek, ters çevirmek, nesneleriniz için bir fonksiyonun sonucuna göre sıralama gibi harika şeyler yapabilirsiniz ...

İşte bazı faydalarından bahseden bir blog yazısı.


Google-collections'ın artık Guava'nın (Google'ın ortak java kitaplıkları) bir parçası olduğunu unutmayın, bu nedenle Ordering sınıfını kullanmak istiyorsanız Guava'ya (veya Guava'nın koleksiyon modülüne) güvenmek isteyebilirsiniz.
Etienne Neveu

4

Contact sınıflarınızın Comparable uygulamasını yapmanız ve ardından compareTo(Contact)yöntemi uygulamanız gerekir . Bu şekilde, Collections.sort bunları sizin için sıralayabilir. Bağlandığım sayfa başına, CompareTo ', bu nesne belirtilen nesneden küçük, ona eşit veya daha büyük olduğundan negatif bir tamsayı, sıfır veya pozitif bir tamsayı döndürüyor.'

Örneğin, isme göre (A'dan Z'ye) sıralamak isterseniz, sınıfınız şöyle görünecektir:

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

Benimle iyi çalıştı, teşekkürler! Ayrıca, durumu yok saymak için CompareToIgnoreCase kullandım.
Rani Kheir

3

Lambdaj kullanarak , kişilerinizin bir koleksiyonunu (örneğin adlarına göre) aşağıdaki şekilde sıralayabilirsiniz.

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

veya adreslerine göre:

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

ve bunun gibi. Daha genel olarak, koleksiyonlarınıza birçok yolla erişmek ve bunları yönetmek için bir DSL sunar, örneğin kişilerinizi bazı koşullara göre filtrelemek veya gruplamak, mülk değerlerinden bazılarını bir araya getirmek vb.


0

Collections.sort, iyi bir sıralama uygulamasıdır. Contact için karşılaştırılabilir uygulanmamışsa, bir Karşılaştırıcı uygulamasına geçmeniz gerekecektir.

Notun:

Sıralama algoritması, değiştirilmiş bir birleştirme sıralamasıdır (burada, alt alt listedeki en yüksek öğe, üst alt listedeki en düşük öğeden daha azsa, birleştirme atlanır). Bu algoritma, garantili n log (n) performansı sunar. Belirtilen liste değiştirilebilir, ancak yeniden boyutlandırılabilir olması gerekmez. Bu uygulama, belirtilen listeyi bir diziye döker, diziyi sıralar ve her öğeyi dizideki karşılık gelen konumdan sıfırlayarak liste üzerinde yineler. Bu, bağlantılı bir listeyi yerinde sıralama girişiminden kaynaklanacak n2 log (n) performansını önler.

Birleştirme sıralaması, muhtemelen yapabileceğiniz çoğu arama algoritmasından daha iyidir.


0

Bunu şu şekilde yaptım. sayı ve ad iki dizi listesidir. İsmi sıralamak zorundayım. Arralist sırayla isimde herhangi bir değişiklik olursa, sayı dizisi listesi de sırasını değiştirir.

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


                        }
                    }
                }
            }



}

Bunu yazmanız, daha az optimum sıralama performansı elde etmeniz, daha fazla hata yazmanız (ve umarız daha fazla test) yapmanız ve kodun diğer insanlara aktarılması daha zor olacak. Yani bu doğru değil. İşe yarayabilir ama bu onu doğru yapmaz.
Eric

0

bu yöntemi kullanın:

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

'

ve kullanın: mySortedlist = sortList(myList); Sınıfınızda karşılaştırıcı uygulamaya gerek yok. Ters emir takası istiyorsanız 1ve-1


0

Tamam, bunun uzun zaman önce yanıtlandığını biliyorum ... ama işte bazı yeni bilgiler:

Söz konusu Contact sınıfının, Comparable'ı uygulayarak zaten tanımlanmış bir doğal sıralamaya sahip olduğunu, ancak bu sıralamayı, örneğin adıyla geçersiz kılmak istediğinizi varsayalım. İşte bunu yapmanın modern yolu:

List<Contact> contacts = ...;

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

Bu şekilde, önce isme göre sıralar (ters sırada) ve sonra isim çakışmaları için, Contact sınıfının kendisi tarafından uygulanan 'doğal' sıralamaya geri döner.


-1

Arrays.sort işlevini kullanmanız gerekir. İçeren sınıflar Comparable uygulamalıdır.


Bu yüzden Diziler dedim.
monksy

Sorun, OP'nin dizi değil, ArrayList kullanıyor olmasıdır.
Pshemo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.