Sortierung in Java

Sortierung in Java

1. Überblick

Dieser Artikel zeigt, wie die Sortierung aufArray,List,Set undMap in Java 7 und Java 8 angewendet wird.

2. Sortieren mitArray

Beginnen wir damit, ganzzahlige Arrays zuerst mit der MethodeArrays.sort()zu sortieren.

Wir definieren die folgendenint-Arrays in einer@Before jUnit-Methode:

@Before
public void initVariables () {
    toSort = new int[]
      { 5, 1, 89, 255, 7, 88, 200, 123, 66 };
    sortedInts = new int[]
      {1, 5, 7, 66, 88, 89, 123, 200, 255};
    sortedRangeInts = new int[]
      {5, 1, 89, 7, 88, 200, 255, 123, 66};
    ...
}

2.1. Komplettes Array sortieren

Verwenden wir jetzt die einfacheArray.sort()-API:

@Test
public void givenIntArray_whenUsingSort_thenSortedArray() {
    Arrays.sort(toSort);

    assertTrue(Arrays.equals(toSort, sortedInts));
}

Das unsortierte Array ist jetzt vollständig sortiert:

[1, 5, 7, 66, 88, 89, 123, 200, 255]

As mentioned in the official JavaDoc,Arrays.sort verwendet Quicksort mit zwei Pivots fürprimitives. Es bietet eine O (n log (n)) - Leistung und ist in der Regel schneller als herkömmliche (One-Pivot) -Quicksort-Implementierungen. Es wird jedoch eine stabile, adaptive, iterative Implementierung vonmergesort algorithm for Array of Objects verwendet.

2.2. Teil eines Arrays sortieren

Arrays.sort verfügt über eine weiteresort-API - die wir hier diskutieren werden:

Arrays.sort(int[] a, int fromIndex, int toIndex)

Dadurch wird nur ein Teil des Arrays zwischen den beiden Indizes sortiert.

Schauen wir uns ein kurzes Beispiel an:

@Test
public void givenIntArray_whenUsingRangeSort_thenRangeSortedArray() {
    Arrays.sort(toSort, 3, 7);

    assertTrue(Arrays.equals(toSort, sortedRangeInts));
}

Die Sortierung wird nur für folgende Sub-Array-Elemente durchgeführt (toIndex wären exklusiv):

[255, 7, 88, 200]

Das resultierende sortierte Unterarray einschließlich des Hauptarrays wäre:

[5, 1, 89, 7, 88, 200, 255, 123, 66]

2.3. Java 8Arrays.sort vsArrays.parallelSort

Java 8 wird mit einer neuen API geliefert -parallelSort - mit einer ähnlichen Signatur wie die API vonArrays.sort():

@Test
public void givenIntArray_whenUsingParallelSort_thenArraySorted() {
    Arrays.parallelSort(toSort);

    assertTrue(Arrays.equals(toSort, sortedInts));
}

Hinter den Kulissen vonparallelSort(), wird das Array in verschiedene Unterarrays unterteilt (gemäß der Granularität im Algorithmus vonparallelSort). Jedes Unterarray wird mitArrays.sort() in verschiedenen Threads sortiert, so dasssort parallel ausgeführt werden können und schließlich als sortiertes Array zusammengeführt werden.

Beachten Sie, dassForJoin common pool zum Ausführen dieser parallelen Aufgaben und zum anschließenden Zusammenführen der Ergebnisse verwendet wird.

Das Ergebnis vonArrays.parallelSort wird natürlich das gleiche sein wieArray.sort. Es geht nur darum, Multithreading zu nutzen.

Schließlich gibt es auch ähnliche Varianten von APIArrays.sort inArrays.parallelSort:

Arrays.parallelSort (int [] a, int fromIndex, int toIndex);

3. List sortieren

Verwenden wir jetzt dieCollections.sort()-API injava.utils.Collections, umList von Ganzzahlen zu sortieren:

@Test
public void givenList_whenUsingSort_thenSortedList() {
    List toSortList = Ints.asList(toSort);
    Collections.sort(toSortList);

    assertTrue(Arrays.equals(toSortList.toArray(),
    ArrayUtils.toObject(sortedInts)));
}

DieList vor dem Sortieren enthalten die folgenden Elemente:

[5, 1, 89, 255, 7, 88, 200, 123, 66]

Und natürlich nach dem Sortieren:

[1, 5, 7, 66, 88, 89, 123, 200, 255]

As mentioned in Oracle JavaDoc fürCollections.Sort verwendet ein modifiziertes Mergesort und bietet eine garantierte Leistung vonn log(n).

4. Set sortieren

Als nächstes verwenden wirCollections.sort(), umLinkedHashSet zu sortieren.

Wir verwendenLinkedHashSet, da die Einfügereihenfolge beibehalten wird.

Beachten Sie, wie, um diesort-API inCollections -we’re first wrapping the set in a list zu verwenden:

@Test
public void givenSet_whenUsingSort_thenSortedSet() {
    Set integersSet = new LinkedHashSet<>(Ints.asList(toSort));
    Set descSortedIntegersSet = new LinkedHashSet<>(
      Arrays.asList(new Integer[]
        {255, 200, 123, 89, 88, 66, 7, 5, 1}));

    List list = new ArrayList(integersSet);
    Collections.sort(list, (i1, i2) -> {
        return i2 - i1;
    });
    integersSet = new LinkedHashSet<>(list);

    assertTrue(Arrays.equals(
      integersSet.toArray(), descSortedIntegersSet.toArray()));
}

5. Map sortieren

In diesem Abschnitt werden wir uns mitsorting a Map – both by keys and by values. befassen

Definieren wir zunächst die Karte, die sortiert werden soll:

@Before
public void initVariables () {
    ....
    HashMap map = new HashMap<>();
    map.put(55, "John");
    map.put(22, "Apple");
    map.put(66, "Earl");
    map.put(77, "Pearl");
    map.put(12, "George");
    map.put(6, "Rocky");
    ....
}

5.1. Map nach Schlüsseln sortieren

Wir extrahieren jetzt die Einträgekeys undvalues aus denHashMap und sortieren sie nach den Werten der Schlüssel in diesem Beispiel:

@Test
public void givenMap_whenSortingByKeys_thenSortedMap() {
    Integer[] sortedKeys = new Integer[] { 6, 12, 22, 55, 66, 77 };

    List> entries
      = new ArrayList<>(map.entrySet());
    Collections.sort(entries, new Comparator>() {
        @Override
        public int compare(
          Entry o1, Entry o2) {
            return o1.getKey().compareTo(o2.getKey());
        }
    });
    Map sortedMap = new LinkedHashMap<>();
    for (Map.Entry entry : entries) {
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    assertTrue(Arrays.equals(sortedMap.keySet().toArray(), sortedKeys));
}

Beachten Sie, wie wir dieLinkedHashMap beim Kopieren der sortiertenEntries basierend auf Schlüsseln verwendet haben (daHashSet die Reihenfolge der Schlüssel nicht garantiert).

DieMap vor dem Sortieren:

[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]

DieMap nach dem Sortieren vonby keys:

[Key: 6 , Value: Rocky]
[Key: 12 , Value: George]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 66 , Value: Earl]
[Key: 77 , Value: Pearl]

5.2. Map nach Werten sortieren

Hier werden wir Werte vonHashMap Einträgen für die Sortierung basierend auf Werten vonHashMap vergleichen:

@Test
public void givenMap_whenSortingByValues_thenSortedMap() {
    String[] sortedValues = new String[]
      { "Apple", "Earl", "George", "John", "Pearl", "Rocky" };

    List> entries
      = new ArrayList<>(map.entrySet());
    Collections.sort(entries, new Comparator>() {
        @Override
        public int compare(
          Entry o1, Entry o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });
    Map sortedMap = new LinkedHashMap<>();
    for (Map.Entry entry : entries) {
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    assertTrue(Arrays.equals(sortedMap.values().toArray(), sortedValues));
}

DieMap vor dem Sortieren:

[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]

DieMap nach dem Sortieren vonby values:

[Key: 22 , Value: Apple]
[Key: 66 , Value: Earl]
[Key: 12 , Value: George]
[Key: 55 , Value: John]
[Key: 77 , Value: Pearl]
[Key: 6 , Value: Rocky]

6. Benutzerdefinierte Objekte sortieren

Lassen Sie uns jetzt mit einem benutzerdefinierten Objekt arbeiten:

public class Employee implements Comparable {
    private String name;
    private int age;
    private double salary;

    public Employee(String name, int age, double salary) {
        ...
    }

    // standard getters, setters and toString
}

In den folgenden Abschnitten wird das folgendeEmployee Array zum Sortieren verwendet:

@Before
public void initVariables () {
    ....
    employees = new Employee[] {
      new Employee("John", 23, 5000), new Employee("Steve", 26, 6000),
      new Employee("Frank", 33, 7000), new Employee("Earl", 43, 10000),
      new Employee("Jessica", 23, 4000), new Employee("Pearl", 33, 6000)};

    employeesSorted = new Employee[] {
      new Employee("Earl", 43, 10000), new Employee("Frank", 33, 70000),
      new Employee("Jessica", 23, 4000), new Employee("John", 23, 5000),
      new Employee("Pearl", 33, 4000), new Employee("Steve", 26, 6000)};

    employeesSortedByAge = new Employee[] {
      new Employee("John", 23, 5000), new Employee("Jessica", 23, 4000),
      new Employee("Steve", 26, 6000), new Employee("Frank", 33, 70000),
      new Employee("Pearl", 33, 4000), new Employee("Earl", 43, 10000)};
}

Wir können Arrays oder Sammlungen von benutzerdefinierten Objekten entweder sortieren:

  1. in der natürlichen Reihenfolge (Verwenden derComparable-Schnittstelle) oder

  2. in der Reihenfolge von aComparatorInterface

6.1. Using Comparable

The natural order in java bedeutet eine Reihenfolge, in der das Grundelement oder Objekt in einem bestimmten Array oder einer bestimmten Sammlung geordnet sortiert werden soll.

Sowohljava.util.Arrays als auchjava.util.Collections haben einesort()-Methode undIt’s highly recommended that natural orders should be consistent with the semantics of equals.

In diesem Beispiel betrachten wir Mitarbeiter mit denselbenname als gleich:

@Test
public void givenEmpArray_SortEmpArray_thenSortedArrayinNaturalOrder() {
    Arrays.sort(employees);

    assertTrue(Arrays.equals(employees, employeesSorted));
}

Sie können die natürliche Reihenfolge für Elemente definieren, indem Sie eineComparable-Schnittstelle implementieren, die über diecompareTo()-Methode zum Vergleichen des aktuellen Objekts und des als Argument übergebenen Objekts verfügt.

Um dies klar zu verstehen, sehen wir uns ein Beispiel für die KlasseEmployeean, die die SchnittstelleComparableimplementiert:

public class Employee implements Comparable {
    ...

    @Override
    public boolean equals(Object obj) {
        return ((Employee) obj).getName().equals(getName());
    }

    @Override
    public int compareTo(Object o) {
        Employee e = (Employee) o;
        return getName().compareTo(e.getName());
    }
}

Im Allgemeinen wird die Logik zum Vergleich mit der MethodecompareTo geschrieben. Hier vergleichen wir die Mitarbeiterreihenfolge odername des Mitarbeiterfelds. Zwei Mitarbeiter sind gleich, wenn sie denselben Namen haben.

Wenn nunArrays.sort(employees); im obigen Code aufgerufen wird, wissen wir jetzt, welche Logik und Reihenfolge bei der Sortierung der Mitarbeiter nach Alter gilt:

[("Earl", 43, 10000),("Frank", 33, 70000), ("Jessica", 23, 4000),
 ("John", 23, 5000),("Pearl", 33, 4000), ("Steve", 26, 6000)]

Wir können sehen, dass das Array nach dem Namen des Mitarbeiters sortiert ist - was jetzt zu einer natürlichen Reihenfolge für die Klasse vonEmployeewird.

6.2. Verwenden vonComparator

Sortieren wir nun die Elemente mithilfe der Schnittstellenimplementierung vonComparator. Dabei übergeben wir die anonyme innere Klasse im laufenden Betrieb an die API vonArrays.sort():

@Test
public void givenIntegerArray_whenUsingSort_thenSortedArray() {
    Integer [] integers = ArrayUtils.toObject(toSort);
    Arrays.sort(integers, new Comparator() {
        @Override
        public int compare(Integer a, Integer b) {
            return a - b;
        }
    });

    assertTrue(Arrays.equals(integers, ArrayUtils.toObject(sortedInts)));
}

Jetzt können Sie Mitarbeiter nachsalary sortieren - und eine andere Komparatorimplementierung übergeben:

Arrays.sort(employees, new Comparator() {
    @Override
    public int compare(Employee o1, Employee o2) {
       return (int) (o1.getSalary() - o2.getSalary());
    }
 });

Die sortierten Employees-Arrays basierend aufsalary sind:

[(Jessica,23,4000.0), (John,23,5000.0), (Pearl,33,6000.0), (Steve,26,6000.0),
(Frank,33,7000.0), (Earl,43,10000.0)]

Beachten Sie, dass wirCollections.sort() auf ähnliche Weise verwenden können, umList undSet von Objekten in natürlicher oder benutzerdefinierter Reihenfolge zu sortieren, wie oben für Arrays beschrieben.

7. Sortieren mit Lambdas

Beginnen Sie mit Java 8, wir können Lambdas verwenden, um die Funktionsschnittstelle vonComparatorzu implementieren.

Sie können sich die Beschreibung vonLambdas in Java 8ansehen, um die Syntax zu verbessern.

Ersetzen wir den alten Komparator:

Comparator c  = new Comparator<>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a - b;
    }
}

Mit der entsprechenden Implementierung unter Verwendung des Lambda-Ausdrucks:

Comparator c = (a, b) -> a - b;

Zum Schluss schreiben wir den Test:

@Test
public void givenArray_whenUsingSortWithLambdas_thenSortedArray() {
    Integer [] integersToSort = ArrayUtils.toObject(toSort);
    Arrays.sort(integersToSort, (a, b) -> {
        return a - b;
    });

    assertTrue(Arrays.equals(integersToSort,
      ArrayUtils.toObject(sortedInts)));
}

Wie Sie sehen können, hier eine viel klarere und prägnantere Logik.

8. Verwenden vonComparator.comparing undComparator.thenComparing

Java 8 enthält zwei neue APIs, die zum Sortieren nützlich sind -comparing() undthenComparing() in derComparator-Schnittstelle.

Diese sind sehr praktisch für die Verkettung mehrerer Bedingungen derComparator.

Betrachten wir ein Szenario, in dem wirEmployee nachage und dann nachname vergleichen möchten:

@Test
public void givenArrayObjects_whenUsingComparing_thenSortedArrayObjects() {
    List employeesList = Arrays.asList(employees);
    employees.sort(Comparator.comparing(Employee::getAge));

    assertTrue(Arrays.toString(employees.toArray())
      .equals(sortedArrayString));
}

In diesem Beispiel istEmployee::getAge der Sortierschlüssel für dieComparator-Schnittstelle, die eine funktionale Schnittstelle mit Vergleichsfunktion implementiert.

Hier ist die Anzahl der Mitarbeiter nach dem Sortieren:

[(John,23,5000.0), (Jessica,23,4000.0), (Steve,26,6000.0), (Frank,33,7000.0),
(Pearl,33,6000.0), (Earl,43,10000.0)]

Hier werden die Mitarbeiter nachage sortiert.

Wir können sehen, dassJohn undJessica gleich alt sind - was bedeutet, dass die Ordnungslogik jetzt ihre Namen berücksichtigen sollte - was wir mitthenComparing() erreichen können:

...
employees.sort(Comparator.comparing(Employee::getAge)
  .thenComparing(Employee::getName));
...

Nach dem Sortieren mit dem obigen Code-Snippet werden die Elemente im Mitarbeiter-Array wie folgt sortiert:

[(Jessica,23,4000.0),
 (John,23,5000.0),
 (Steve,26,6000.0),
 (Frank,33,7000.0),
 (Pearl,33,6000.0),
 (Earl,43,10000.0)
]

Somit machencomparing() undthenComparing() komplexere Sortierszenarien definitiv viel sauberer zu implementieren.

Weitere Lektüre:

Leitfaden zur Sortierung in Kotlin

Eine kurze Anleitung zum Sortieren mit der Kotlin-Standardbibliothek.

Read more

9. Fazit

In diesem Artikel haben wir gesehen, wie wir die Sortierung aufArray,List,Set undMap anwenden können.

Wir haben auch eine kurze Einführung darüber gesehen, wie Funktionen von Java 8 beim Sortieren nützlich sein können, z. B. die Verwendung von Lambdas,comparing() undthenComparing() undparallelSort().

Alle im Artikel verwendeten Beispiele sind inover on GitHub verfügbar.