मैं अलग-अलग समय पर अलग-अलग मापदंडों द्वारा एक सूची को कैसे सॉर्ट करता हूं


95

Personउदाहरण के लिए, मेरे पास कई गुणों के साथ एक वर्ग है :

public class Person {
    private int id;
    private String name, address;
    // Many more properties.
}

Person-Objects के बहुत से एक में संग्रहीत हैं ArrayList<Person>। मैं इस सूची को कई प्रकार के मापदंडों और समय-समय पर अलग-अलग करना चाहता हूं। उदाहरण के लिए, मैं एक बार nameआरोही और फिर addressनीचे उतर कर और कुछ समय के लिए उतरना चाहता हूं id

और मैं अपनी खुद की तरह के तरीकों का निर्माण नहीं करना चाहता (यानी, मैं उपयोग करना चाहता हूं Collections.sort(personList, someComparator)। यह प्राप्त करने वाला सबसे सुंदर समाधान क्या है?

जवाबों:


193

मुझे लगता है कि आपका एनम दृष्टिकोण मूल रूप से ध्वनि है, लेकिन स्विच स्टेटमेंट को वास्तव में अधिक ऑब्जेक्ट उन्मुख दृष्टिकोण की आवश्यकता है। विचार करें:

enum PersonComparator implements Comparator<Person> {
    ID_SORT {
        public int compare(Person o1, Person o2) {
            return Integer.valueOf(o1.getId()).compareTo(o2.getId());
        }},
    NAME_SORT {
        public int compare(Person o1, Person o2) {
            return o1.getFullName().compareTo(o2.getFullName());
        }};

    public static Comparator<Person> decending(final Comparator<Person> other) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                return -1 * other.compare(o1, o2);
            }
        };
    }

    public static Comparator<Person> getComparator(final PersonComparator... multipleOptions) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                for (PersonComparator option : multipleOptions) {
                    int result = option.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }
}

उपयोग का एक उदाहरण (स्थिर आयात के साथ)।

public static void main(String[] args) {
    List<Person> list = null;
    Collections.sort(list, decending(getComparator(NAME_SORT, ID_SORT)));
}

12
Enums का +1 स्मार्ट उपयोग। मुझे आपके द्वारा किए गए सुरुचिपूर्ण संयोजन, "उतरते" और "समग्र" पसंद हैं। मुझे लगता है कि अशक्त मान उपचार गायब है, लेकिन "अवरोही" के रूप में उसी तरह जोड़ना आसान है।
केएलई

1
कई अच्छे जवाब विचार के लिए भोजन प्रदान करते हैं। चूंकि कोई उत्तर स्पष्ट विकल्प के रूप में नहीं था, इसलिए मैं इसे स्वीकार करने वाला हूं क्योंकि मुझे लालित्य पसंद है, लेकिन मैं किसी को भी इस दृष्टिकोण को देखने के लिए आग्रह करता हूं कि अन्य दृष्टिकोणों की भी जांच करें।
रनरॉस

1
@ TheLittleNaruto, तुलना विधि एक नकारात्मक संख्या देता है यदि ओ 2 अधिक है, तो एक सकारात्मक यदि ओ 1 अधिक है, और शून्य है यदि वे समान हैं। -1 से गुणा करने पर परिणाम उल्टा हो जाता है, जो कि निर्णय लेने का विचार है (सामान्य आरोही क्रम के विपरीत), जबकि शून्य होने पर इसे छोड़ देते हैं यदि वे समान थे।
यिशै

5
ध्यान दें कि जावा 8 के बाद से आप comparator.reversed()अवरोही के लिए उपयोग कर सकते हैं और आप comparator1.thenComparing(comparator2)तुलना करने वालों का पीछा करने के लिए उपयोग कर सकते हैं ।
गुइसिम

1
@ जॉनबॉम, यदि पहला तुलनित्र एक गैर शून्य परिणाम देता है, तो वह परिणाम वापस आ जाता है और बाकी श्रृंखला निष्पादित नहीं होती है।
यिशैई

26

आप उन सभी गुणों के लिए तुलनित्र बना सकते हैं, जिन्हें आप क्रमबद्ध करना चाहते हैं और फिर "तुलनित्र" की कोशिश कर सकते हैं :-) इस तरह:

public class ChainedComparator<T> implements Comparator<T> {
    private List<Comparator<T>> simpleComparators; 
    public ChainedComparator(Comparator<T>... simpleComparators) {
        this.simpleComparators = Arrays.asList(simpleComparators);
    }
    public int compare(T o1, T o2) {
        for (Comparator<T> comparator : simpleComparators) {
            int result = comparator.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

आपको शायद एक चेतावनी मिलने वाली है जब इसका उपयोग किया जाता है (हालांकि JDK7 में आपको इसे दबाने में सक्षम होना चाहिए)।
टॉम हॉकिन -

मुझे भी यह पसंद है। क्या आप दिए गए उदाहरण के साथ इसका उपयोग करने के लिए एक नमूना प्रदान कर सकते हैं?
रनरॉस

@runaros: KLE के उत्तर से तुलनित्रों का उपयोग करना: Collections.sort (/ * संग्रह <व्यक्ति> * / लोग, नया ChainedComparator (NAME_ASC_ADRESS_DESC, ID_DESC);
जानूस ट्रॉल्सन

16

एक तरीका यह है Comparatorकि ऐसा करने के लिए गुणों की एक सूची के रूप में तर्क के रूप में लेता है, जैसा कि इस उदाहरण से पता चलता है।

public class Person {
    private int id;
    private String name, address;

    public static Comparator<Person> getComparator(SortParameter... sortParameters) {
        return new PersonComparator(sortParameters);
    }

    public enum SortParameter {
        ID_ASCENDING, ID_DESCENDING, NAME_ASCENDING,
        NAME_DESCENDING, ADDRESS_ASCENDING, ADDRESS_DESCENDING
    }

    private static class PersonComparator implements Comparator<Person> {
        private SortParameter[] parameters;

        private PersonComparator(SortParameter[] parameters) {
            this.parameters = parameters;
        }

        public int compare(Person o1, Person o2) {
            int comparison;
            for (SortParameter parameter : parameters) {
                switch (parameter) {
                    case ID_ASCENDING:
                        comparison = o1.id - o2.id;
                        if (comparison != 0) return comparison;
                        break;
                    case ID_DESCENDING:
                        comparison = o2.id - o1.id;
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_ASCENDING:
                        comparison = o1.name.compareTo(o2.name);
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_DESCENDING:
                        comparison = o2.name.compareTo(o1.name);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_ASCENDING:
                        comparison = o1.address.compareTo(o2.address);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_DESCENDING:
                        comparison = o2.address.compareTo(o1.address);
                        if (comparison != 0) return comparison;
                        break;
                }
            }
            return 0;
        }
    }
}

फिर इसे इस तरह कोड के लिए उपयोग किया जा सकता है:

cp = Person.getComparator(Person.SortParameter.ADDRESS_ASCENDING,
                          Person.SortParameter.NAME_DESCENDING);
Collections.sort(personList, cp);

हाँ। यदि आप चाहते हैं कि आपका कोड बहुत सामान्य हो, तो आपकी एनम केवल पढ़ने के लिए संपत्ति निर्दिष्ट कर सकती है (आप एनम नाम का उपयोग करके संपत्ति प्राप्त करने के लिए प्रतिबिंब का उपयोग कर सकते हैं), और आप एक अन्य एनम के साथ बाकी निर्दिष्ट कर सकते हैं: ASC और DESC, और संभवतः तीसरा (NULL_FIRST या NULL_LAST)।
KLE

8

एक दृष्टिकोण Comparatorएस रचना करना होगा । यह एक पुस्तकालय विधि हो सकती है (मुझे यकीन है कि यह कहीं बाहर मौजूद है)।

public static <T> Comparator<T> compose(
    final Comparator<? super T> primary,
    final Comparator<? super T> secondary
) {
    return new Comparator<T>() {
        public int compare(T a, T b) {
            int result = primary.compare(a, b);
            return result==0 ? secondary.compare(a, b) : result;
        }
        [...]
    };
}

उपयोग:

Collections.sort(people, compose(nameComparator, addressComparator));

वैकल्पिक रूप से, ध्यान दें कि Collections.sortएक स्थिर प्रकार है। यदि प्रदर्शन बिल्कुल महत्वपूर्ण नहीं है, तो आप प्राथमिक से पहले द्वितीयक क्रम को क्रमबद्ध करते हैं।

Collections.sort(people, addressComparator);
Collections.sort(people, nameComparator);

चतुर दृष्टिकोण, हालांकि, इसे और अधिक सामान्य बनाया जा सकता है, जैसे कि इसमें तुलनात्मक रूप से शून्य सहित एक चर संख्या शामिल है?
रनसरोस

compose(nameComparator, compose(addressComparator, idComparator))अगर जावा के विस्तार के तरीके हैं तो यह थोड़ा बेहतर होगा।
टॉम हॉकिन - 15

4

तुलनाकर्ता आपको बहुत आसानी से और स्वाभाविक रूप से ऐसा करने देता है। आप तुलना के एकल उदाहरण बना सकते हैं, या तो अपने व्यक्तिगत वर्ग में, या अपनी आवश्यकता से जुड़े सेवा वर्ग में।
अनाम आंतरिक कक्षाओं का उपयोग करते हुए उदाहरण:

    public static final Comparator<Person> NAME_ASC_ADRESS_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         int nameOrder = p1.getName().compareTo(p2.getName);
         if(nameOrder != 0) {
           return nameOrder;
         }
         return -1 * p1.getAdress().comparedTo(p2.getAdress());
         // I use explicit -1 to be clear that the order is reversed
      }
    };

    public static final Comparator<Person> ID_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         return -1 * p1.getId().comparedTo(p2.getId());
         // I use explicit -1 to be clear that the order is reversed
      }
    };
    // and other comparator instances as needed... 

यदि आपके पास कई हैं, तो आप किसी भी तरह से अपने तुलनित्र कोड की संरचना कर सकते हैं। उदाहरण के लिए, आप कर सकते हैं:

  • दूसरे तुलनित्र से विरासत,
  • एक कंपोजिटकंपेटर है जो कुछ मौजूदा तुलनाकर्ताओं को एग्रिगेट करता है
  • एक NullComparator है जो अशक्त मामलों को संभालती है, फिर दूसरे तुलनित्र को सौंपती है
  • आदि...

2

मुझे लगता है कि आपके जवाब की तरह, पर्सन क्लास को सॉर्टर्स को कपल करना, एक अच्छा विचार नहीं है, क्योंकि यह कपल्स की तुलना (आमतौर पर बिजनेस संचालित) और मॉडल ऑब्जेक्ट को एक-दूसरे के करीब करने के लिए करता है। हर बार जब आप सॉर्टर को बदलना / जोड़ना चाहते हैं, तो आपको व्यक्ति वर्ग को छूने की जरूरत है, जो आमतौर पर कुछ ऐसा है जो आप नहीं करना चाहते हैं।

एक सेवा या कुछ इसी तरह का उपयोग करना, जो तुलनात्मक उदाहरण प्रदान करता है, जैसे केएलई प्रस्तावित, अधिक लचीला और एक्स्टेंसिबल लगता है।


जैसा कि मेरे लिए यह तंग युग्मन की ओर जाता है क्योंकि किसी तरह तुलना धारक धारक वर्ग व्यक्ति की विस्तृत डेटा संरचना (मूल रूप से तुलना करने के लिए व्यक्ति वर्ग के कौन से क्षेत्र) जानने के लिए है और यदि आप कभी किसी व्यक्ति के क्षेत्र में कुछ बदलने जा रहे हैं तो यह पता लगाने की ओर जाता है तुलनित्र वर्ग में परिवर्तन। मुझे लगता है कि व्यक्ति की तुलना एक व्यक्ति वर्ग का एक हिस्सा होना चाहिए। blog.sanaulla.info/2008/06/26/…
स्टेन

2

मेरा दृष्टिकोण Yishai पर निर्मित है। मुख्य अंतर यह है कि एक विशेषता के लिए पहले आरोही को सॉर्ट करने का कोई तरीका नहीं है और उसके बाद दूसरे के लिए निर्णय लेना है। यह गणना के साथ नहीं किया जा सकता है। उसके लिए मैंने कक्षाओं का उपयोग किया। क्योंकि SortOrder दृढ़ता से उस प्रकार पर निर्भर करता है जिसे मैंने व्यक्ति के आंतरिक वर्ग के रूप में लागू करना पसंद किया था।

आंतरिक वर्ग 'SortOrder' के साथ वर्ग 'व्यक्ति':

import java.util.Comparator;

public class Person {
    private int id;
    private String firstName; 
    private String secondName;

    public Person(int id, String firstName, String secondName) {
        this.id = id;
        this.firstName = firstName;
        this.secondName = secondName;   
    }

    public abstract static class SortOrder implements Comparator<Person> {
        public static SortOrder PERSON_ID = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return Integer.valueOf(p1.getId()).compareTo(p2.getId());
            }
        };
        public static SortOrder PERSON_FIRST_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getFirstName().compareTo(p2.getFirstName());
            }
        };
        public static SortOrder PERSON_SECOND_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getSecondName().compareTo(p2.getSecondName());
            }
        };

        public static SortOrder invertOrder(final SortOrder toInvert) {
            return new SortOrder() {
                public int compare(Person p1, Person p2) {
                    return -1 * toInvert.compare(p1, p2);
                }
            };
        }

        public static Comparator<Person> combineSortOrders(final SortOrder... multipleSortOrders) {
            return new Comparator<Person>() {
                public int compare(Person p1, Person p2) {
                    for (SortOrder personComparator: multipleSortOrders) {
                        int result = personComparator.compare(p1, p2);
                        if (result != 0) {
                            return result;
                        }
                    }
                    return 0;
                }
            };
        }
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();

        result.append("Person with id: ");
        result.append(id);
        result.append(" and firstName: ");
        result.append(firstName);
        result.append(" and secondName: ");
        result.append(secondName);
        result.append(".");

        return result.toString();
    }
}

क्लास पर्सन और उसके SortOrder का उपयोग करने के लिए एक उदाहरण:

import static multiplesortorder.Person.SortOrder.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import multiplesortorder.Person;

public class Application {

    public static void main(String[] args) {
        List<Person> listPersons = new ArrayList<Person>(Arrays.asList(
                 new Person(0, "...", "..."),
                 new Person(1, "...", "...")
             ));

         Collections.sort(listPersons, combineSortOrders(PERSON_FIRST_NAME, invertOrder(PERSON_ID)));

         for (Person p: listPersons) {
             System.out.println(p.toString());
         }
    }
}

oRUMOo


तुलना करने वालों की इस तरह की जटिलता की जटिलता क्या होगी? क्या हम अनिवार्य रूप से हर बार तुलना करने वालों की श्रृंखला बना रहे हैं? तो हम प्रत्येक तुलनित्र के लिए एक * लॉग (एन) ऑपरेशन करते हैं?
जॉन बॉम

0

मैंने हाल ही में एक सीमांकित स्ट्रिंग रिकॉर्ड के भीतर कई क्षेत्रों को सॉर्ट करने के लिए एक तुलनित्र लिखा था। यह आपको सीमांकक, रिकॉर्ड संरचना और सॉर्टिंग नियमों (जिनमें से कुछ प्रकार विशिष्ट हैं) को परिभाषित करने की अनुमति देता है। आप एक व्यक्ति रिकॉर्ड को एक सीमांकित स्ट्रिंग में परिवर्तित करके इसका उपयोग कर सकते हैं।

आवश्यक जानकारी की तुलना प्रोग्रामर से की जाती है, या तो प्रोग्राम या XML फ़ाइल के माध्यम से।

एक्सएमएल एक पैकेज एम्बेडेड XSD फ़ाइल द्वारा मान्य है। उदाहरण के लिए, नीचे चार क्षेत्रों के साथ एक टैब सीमांकित रिकॉर्ड लेआउट है (जिनमें से दो क्रमबद्ध हैं):

<?xml version="1.0" encoding="ISO-8859-1"?> 
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <delimiter>&#009;</delimiter>

    <column xsi:type="Decimal">
        <name>Column One</name>
    </column>

    <column xsi:type="Integer">
        <name>Column Two</name>
    </column>

    <column xsi:type="String">
        <name>Column Three</name>
        <sortOrder>2</sortOrder>
        <trim>true</trim>
        <caseSensitive>false</caseSensitive>        
        <stripAccents>true</stripAccents>
    </column>

    <column xsi:type="DateTime">
        <name>Column Four</name>
        <sortOrder>1</sortOrder>
        <ascending>true</ascending>
        <nullLowSortOrder>true</nullLowSortOrder>
        <trim>true</trim>
        <pattern>yyyy-MM-dd</pattern>
    </column>

</row>

तुम तो जावा में इस तरह का उपयोग करेंगे:

Comparator<String> comparator = new RowComparator(
              new XMLStructureReader(new File("layout.xml")));

पुस्तकालय यहाँ पाया जा सकता है:

http://sourceforge.net/projects/multicolumnrowcomparator/


0

मान लीजिए कि एक वर्ग Coordinateहै और किसी को एक्स-समन्वय और वाई-समन्वय के अनुसार इसे दोनों तरीकों से क्रमबद्ध करना है। इसके लिए दो विभेदक तुलनित्रों की आवश्यकता होती है। नीचे नमूना है

class Coordinate
{

    int x,y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    static Comparator<Coordinate> getCoordinateXComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.x < Coordinate2.x)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate x
        };
    }

    static Comparator<Coordinate> getCoordinateYComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.y < Coordinate2.y)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate y
        };
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.