Zasadniczo mam arraylistę takich obiektów:

[{name: 'A'}, {name: 'B'}, {name:'?'}]

Chcę je posortować tak, aby znak zapytania był na końcu jak powyżej.

Ale używając poniższego kodu.

Collections.sort(myList);

Powoduje to zawsze obiekt ze znakiem zapytania jako pierwszym, myślę, że jest to spowodowane uporządkowaniem ASCII? Myślę, że właściwym rozwiązaniem jest użycie funkcji komparatora, ale nie jestem pewien, jak by to wyglądało z literami i znakami specjalnymi?

Jak bym to zaimplementował?

2
Strobe_ 3 kwiecień 2020, 18:57

3 odpowiedzi

Najlepsza odpowiedź

W Javie 8 możesz użyć dwupoziomowego komparatora niestandardowego:

// given
List<YourObject> list;
list.sort((o1, o2) -> "?".equals(o1.getName()) ? 1 :
    ("?".equals(o2.getName()) ? -1 : o1.getName().compareTo(o2.getName())));

Logika sortowania jest taka, że jeśli jedną lub drugą nazwą jest ?, to zawsze sortujemy to ? jako ostatnie. Jeśli obie nazwy to ? lub żadna z nich nie jest ?, to sortujemy używając domyślnego leksykograficznego sortowania ciągów.

1
Tim Biegeleisen 3 kwiecień 2020, 16:22

Alternatywne podejście - takie, które sortuje dowolną interpunkcję do końca - można osiągnąć za pomocą kolator oparty na regułach.

Przykład:

List<String> words = Arrays.asList(
        "?dog", "rat", "456", "123", "dog", "pig", "?cat", "!rat", "cat"
);
String englishRules = ("< a,A < b,B < c,C < d,D < e,E < f,F "
        + "< g,G < h,H < i,I < j,J < k,K < l,L "
        + "< m,M < n,N < o,O < p,P < q,Q < r,R "
        + "< s,S < t,T < u,U < v,V < w,W < x,X "
        + "< y,Y < z,Z < 0,1,2,3,4,5,6,7,8,9");

RuleBasedCollator rbc = new RuleBasedCollator(englishRules);
rbc.setStrength(Collator.PRIMARY);

Collections.sort(words, rbc);
words.forEach((word) -> {
    out.print(word + " ");
});

To daje:

cat dog pig rat 123 456 !rat ?cat ?dog 

Punkty do zapamiętania:

1) Ten konkretny przykład jest ograniczony do zestawienia w języku angielskim.

2) Ogólna technika działa, ponieważ wszystkie niewymienione znaki są posortowane do końca. Tak więc nie tylko symbole interpunkcyjne są sortowane po angielskich literach i cyfrach - ale tak samo jak każdy inny znak / symbol (np. Te używane w innych skryptach).

3) Jeśli chcesz, aby symbole interpunkcyjne były uporządkowane w inny sposób niż Unicode, musisz je ująć w pojedyncze cudzysłowy w ciągu reguły:

"... < 0,1,2,3,4,5,6,7,8,9 < '?' < '!'"
2
andrewjames 3 kwiecień 2020, 20:45

Możesz użyć kilku przydatnych metod z java.util.Comparator, aby ułatwić sobie życie, a kod był mniej podatny na błędy niż myślenie o operatorach if-else lub trójskładnikowych:

class MyObj {
    private String name;

    MyObj(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "{name: '" + name + "'}";
    }
}

public class Demo {
    public static void main(String[] args) {
        List<MyObj> lst = Arrays.asList(new MyObj("B"), new MyObj("?"), new MyObj("A"));
        Comparator<String> questionMarksLast = Comparator
                .<String, Boolean>comparing("?"::equals)
                .thenComparing(Comparator.naturalOrder());

        lst.sort(Comparator.comparing(MyObj::getName, questionMarksLast));
        System.out.println(lst);  // prints [{name: 'A'}, {name: 'B'}, {name: '?'}]
    }
}
1
Klitos Kyriacou 3 kwiecień 2020, 18:00