1. ภาพรวม
บทความนี้จะอธิบายวิธีใช้การเรียงลำดับกับArray , List , SetและMapใน Java 7 และ Java 8
2. การเรียงลำดับด้วยอาร์เรย์
เริ่มจากการเรียงลำดับอาร์เรย์จำนวนเต็มก่อนโดยใช้เมธอดArrays.sort ()
เราจะกำหนดintอาร์เรย์ต่อไปนี้ในวิธี@Before jUnit:
@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. การเรียงลำดับอาร์เรย์ที่สมบูรณ์
ตอนนี้ให้ใช้Array.sort () API แบบง่าย:
@Test public void givenIntArray_whenUsingSort_thenSortedArray() { Arrays.sort(toSort); assertTrue(Arrays.equals(toSort, sortedInts)); }
อาร์เรย์ที่ไม่ได้เรียงลำดับถูกจัดเรียงอย่างสมบูรณ์แล้ว:
[1, 5, 7, 66, 88, 89, 123, 200, 255]
เป็นที่กล่าวถึงใน JavaDoc อย่างเป็นทางการArrays.sortใช้ dual-เดือย Quicksort บนพื้นฐาน มีประสิทธิภาพ O (n log (n)) และโดยปกติจะเร็วกว่าการใช้ Quicksort แบบดั้งเดิม (one-pivot) อย่างไรก็ตามมันใช้การปรับใช้อัลกอริธึม mergesort ที่เสถียรปรับตัวซ้ำได้สำหรับArray of Objects
2.2. การเรียงลำดับส่วนหนึ่งของอาร์เรย์
Arrays.sortมีAPI การจัดเรียงอีกหนึ่งรายการซึ่งเราจะพูดถึงที่นี่:
Arrays.sort(int[] a, int fromIndex, int toIndex)
สิ่งนี้จะจัดเรียงเฉพาะบางส่วนของอาร์เรย์ระหว่างดัชนีทั้งสอง
ลองดูตัวอย่างสั้น ๆ :
@Test public void givenIntArray_whenUsingRangeSort_thenRangeSortedArray() { Arrays.sort(toSort, 3, 7); assertTrue(Arrays.equals(toSort, sortedRangeInts)); }
การเรียงลำดับจะทำได้เฉพาะในองค์ประกอบอาร์เรย์ย่อยต่อไปนี้ ( toIndexจะเป็นเอกสิทธิ์):
[255, 7, 88, 200]
อาร์เรย์ย่อยที่เรียงลำดับผลลัพธ์รวมกับอาร์เรย์หลักจะเป็น:
[5, 1, 89, 7, 88, 200, 255, 123, 66]
2.3. Java 8 Arrays.sort VS Arrays.parallelSort
Java 8 มาพร้อมกับ API ใหม่ - parallelSort - ซึ่งมีลายเซ็นคล้ายกับArrays.sort () API:
@Test public void givenIntArray_whenUsingParallelSort_thenArraySorted() { Arrays.parallelSort(toSort); assertTrue(Arrays.equals(toSort, sortedInts)); }
เบื้องหลังของparallelSort ()จะแบ่งอาร์เรย์ออกเป็นอาร์เรย์ย่อยที่แตกต่างกัน (ตามรายละเอียดในอัลกอริทึมของparallelSort ) อาร์เรย์ย่อยแต่ละอาร์เรย์จะถูกจัดเรียงด้วยArrays.sort ()ในเธรดที่แตกต่างกันเพื่อให้การเรียงลำดับสามารถดำเนินการในรูปแบบคู่ขนานและรวมเป็นอาร์เรย์ที่เรียงลำดับได้ในที่สุด
โปรดทราบว่ากลุ่มทั่วไป ForJoin ใช้สำหรับการดำเนินการงานคู่ขนานเหล่านี้แล้วรวมผลลัพธ์เข้าด้วยกัน
ผลลัพธ์ของArrays.parallelSortจะเหมือนกับArray.sortแน่นอนมันเป็นเพียงเรื่องของการใช้ประโยชน์จากมัลติเธรด
ในที่สุดก็มีสายพันธุ์ที่คล้ายกันของ API Arrays.sortในArrays.parallelSortเช่นกัน:
Arrays.parallelSort (int [] a, int fromIndex, int toIndex);
3. การจัดเรียงรายการ
ตอนนี้เรามาใช้Collections.sort () API ในjava.utils.Collections - เพื่อจัดเรียงรายการจำนวนเต็ม:
@Test public void givenList_whenUsingSort_thenSortedList() { List toSortList = Ints.asList(toSort); Collections.sort(toSortList); assertTrue(Arrays.equals(toSortList.toArray(), ArrayUtils.toObject(sortedInts))); }
รายการก่อนที่จะมีการเรียงลำดับองค์ประกอบต่อไปนี้:
[5, 1, 89, 255, 7, 88, 200, 123, 66]
และเป็นธรรมชาติหลังจากการเรียงลำดับ:
[1, 5, 7, 66, 88, 89, 123, 200, 255]
เป็นที่กล่าวถึงใน Oracle JavaDoc สำหรับCollections.sortจะใช้ mergesort แก้ไขและข้อเสนอการรับประกันn log (n)ผลการดำเนินงาน
4. การจัดเรียงชุด
ถัดไปให้การใช้งานของCollections.sort ()เพื่อจัดเรียงLinkedHashSet
เรากำลังใช้LinkedHashSetเนื่องจากรักษาลำดับการแทรก
แจ้งให้ทราบว่าในการที่จะใช้การจัดเรียง API ในคอลเลกชัน - เรากำลังตัดชุดแรกในรายการ :
@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(Comparator.reverseOrder()); integersSet = new LinkedHashSet(list); assertTrue(Arrays.equals( integersSet.toArray(), descSortedIntegersSet.toArray())); }
Comparator.reverseOrder ()วิธีการกลับรายการสั่งซื้อที่กำหนดโดยการสั่งซื้อทางธรรมชาติ
5. การจัดเรียงแผนที่
ในส่วนนี้เราจะเริ่มดูการจัดเรียงแผนที่ - ทั้งตามคีย์และตามค่า
ก่อนอื่นให้กำหนดแผนที่ที่เราจะจัดเรียง:
@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. การจัดเรียงแผนที่ตามคีย์
We'll now extract keys and values entries from the HashMap and sort it based on the values of the keys in this example:
@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)); }
Note how we used the LinkedHashMap while copying the sorted Entries based on keys (because HashSet doesn't guarantee the order of keys).
The Map before sorting :
[Key: 66 , Value: Earl] [Key: 22 , Value: Apple] [Key: 6 , Value: Rocky] [Key: 55 , Value: John] [Key: 12 , Value: George] [Key: 77 , Value: Pearl]
The Map after sorting by 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. Sorting Map by Values
Here we will be comparing values of HashMap entries for sorting based on values of HashMap:
@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)); }
The Map before sorting:
[Key: 66 , Value: Earl] [Key: 22 , Value: Apple] [Key: 6 , Value: Rocky] [Key: 55 , Value: John] [Key: 12 , Value: George] [Key: 77 , Value: Pearl]
The Map after sorting by 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. Sorting Custom Objects
Let's now work with a custom object:
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 }
We'll be using the following Employee Array for sorting example in the following sections:
@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)}; }
We can sort arrays or collections of custom objects either:
- in the natural order (Using the Comparable Interface) or
- in the order provided by a ComparatorInterface
6.1. Using Comparable
The natural order in java means an order in which primitive or Object should be orderly sorted in a given array or collection.
Both java.util.Arrays and java.util.Collections have a sort() method, and It's highly recommended that natural orders should be consistent with the semantics of equals.
In this example, we will consider employees with the same name as equal:
@Test public void givenEmpArray_SortEmpArray_thenSortedArrayinNaturalOrder() { Arrays.sort(employees); assertTrue(Arrays.equals(employees, employeesSorted)); }
You can define the natural order for elements by implementing a Comparable interface which has compareTo() method for comparing current object and object passed as an argument.
To understand this clearly, let's see an example Employee class which implements Comparable Interface:
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()); } }
Generally, the logic for comparison will be written the method compareTo. Here we are comparing the employee order or name of the employee field. Two employees will be equal if they have the same name.
Now when Arrays.sort(employees); is called in the above code, we now know what is the logic and order which goes in sorting the employees as per the age :
[("Earl", 43, 10000),("Frank", 33, 70000), ("Jessica", 23, 4000), ("John", 23, 5000),("Pearl", 33, 4000), ("Steve", 26, 6000)]
We can see the array is sorted by name of the employee – which now becomes a natural order for Employee Class.
6.2. Using Comparator
Now, let's sort the elements using a Comparator interface implementation – where we pass the anonymous inner class on-the-fly to the Arrays.sort() API:
@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 Integer.compare(a, b); } }); assertTrue(Arrays.equals(integers, ArrayUtils.toObject(sortedInts))); }
Now lets sort employees based on salary – and pass in another comparator implementation:
Arrays.sort(employees, new Comparator() { @Override public int compare(Employee o1, Employee o2) { return Double.compare(o1.getSalary(), o2.getSalary()); } });
The sorted Employees arrays based on salary will be:
[(Jessica,23,4000.0), (John,23,5000.0), (Pearl,33,6000.0), (Steve,26,6000.0), (Frank,33,7000.0), (Earl,43,10000.0)]
Note that we can use Collections.sort() in a similar fashion to sort List and Set of Objects in Natural or Custom order as described above for Arrays.
7. Sorting With Lambdas
Start with Java 8, we can use Lambdas to implement the Comparator Functional Interface.
You can have a look at the Lambdas in Java 8 writeup to brush up on the syntax.
Let's replace the old comparator:
Comparator c = new Comparator() { @Override public int compare(Integer a, Integer b) { return Integer.compare(a, b); } }
With the equivalent implementation, using Lambda expression:
Comparator c = (a, b) -> Integer.compare(a, b);
Finally, let's write the test:
@Test public void givenArray_whenUsingSortWithLambdas_thenSortedArray() { Integer [] integersToSort = ArrayUtils.toObject(toSort); Arrays.sort(integersToSort, (a, b) -> { return Integer.compare(a, b); }); assertTrue(Arrays.equals(integersToSort, ArrayUtils.toObject(sortedInts))); }
As you can see, a much cleaner and more concise logic here.
8. Using Comparator.comparing and Comparator.thenComparing
Java 8 comes with two new APIs useful for sorting – comparing() and thenComparing() in the Comparator interface.
These are quite handy for the chaining of multiple conditions of the Comparator.
Let's consider a scenario where we may want to compare Employee by age and then by name:
@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 this example, Employee::getAge is the sorting key for Comparator interface implementing a functional interface with compare function.
Here's the array of Employees after sorting:
[(John,23,5000.0), (Jessica,23,4000.0), (Steve,26,6000.0), (Frank,33,7000.0), (Pearl,33,6000.0), (Earl,43,10000.0)]
Here the employees are sorted based on age.
We can see John and Jessica are of same age – which means that the order logic should now take their names into account- which we can achieve with thenComparing():
... employees.sort(Comparator.comparing(Employee::getAge) .thenComparing(Employee::getName)); ...
After sorting with above code snippet, the elements in employee array would be sorted as:
[(Jessica,23,4000.0), (John,23,5000.0), (Steve,26,6000.0), (Frank,33,7000.0), (Pearl,33,6000.0), (Earl,43,10000.0) ]
Thus comparing() and thenComparing() definitely make more complex sorting scenarios a lot cleaner to implement.
9. Conclusion
In this article, we saw how we can apply sorting to Array, List, Set, and Map.
นอกจากนี้เรายังเห็นการแนะนำสั้น ๆ เกี่ยวกับวิธีการคุณสมบัติของ Java 8 อาจเป็นประโยชน์ในการเรียงลำดับเช่นการใช้งานของ Lambdas, เปรียบเทียบ ()และthenComparing ()และparallelSort ()
ตัวอย่างทั้งหมดที่ใช้ในบทความมีอยู่ใน GitHub