Sắp xếp trong Java 8

Trong bài giới thiệu TreeSet & giới thiệu lớp tiện ích Collections, Arrays trong Java, tôi đã giới thiệu với các bạn cách tạo bộ so sánh sử dụng Comparable, Comparator & cách sắp xếp các phần tử trong một Collection, Array. Trong bài này, tất cả chúng ta sẽ thống kê lại các cách sắp xếp trong Java 7 & một vài cách sắp xếp mới trong Java 8.

Sắp xếp mảng (Array)

Để sắp xếp các phần tử của mảng, tất cả chúng ta sử dụng lớp tiện ích Arrays.sort().

  • Arrays.sort(arr) : Sắp xếp toàn bộ các phần tử của mảng
  • Arrays.sort(arr, fromIndex, toIndex) : Sắp xếp một phần của mảng
  • Arrays.parallelSort.sort(arr) : Sắp xếp toàn bộ các phần tử của mảng theo cách khắc phục đồng thời. Mẹo này chia nhỏ một mảng thành nhiều mảng con & thực hiện sắp xếp trên các mảng con này một cách đồng thời trên các luồng (Thread) khác nhau, sau đó merge lại để có một mảng được sắp xếp hoàn chình.
  • Arrays.parallelSort.sort(arr, fromIndex, toIndex) : Sắp xếp một phần của mảng theo cách khắc phục đồng thời.

Chẳng hạn:


package com.gpcoder.sorting;

import java.util.Arrays;

public class SortedArrayExample {
	public static final int NUMBERS[] = { 5, 1, 2, 4, 3, 6, 7, 9, 8 };

	public static void main(String[] args) {
		// Sorting Complete Array
		int arr1[] = Arrays.copyOf(NUMBERS, NUMBERS.length);
		Arrays.sort(arr1);
		System.out.println(Arrays.toString(arr1));
		// => [1, 2, 3, 4, 5, 6, 7, 8, 9]
		
		// Sorting Part of an Array
		int arr2[] = Arrays.copyOf(NUMBERS, NUMBERS.length);
		Arrays.sort(arr2, 2, 5);
		System.out.println(Arrays.toString(arr2));
		// => [5, 1, 2, 3, 4, 6, 7, 9, 8]
		
		// Java 8 parallelSort
		int arr3[] = Arrays.copyOf(NUMBERS, NUMBERS.length);
		Arrays.parallelSort(arr3);
		System.out.println(Arrays.toString(arr3));
		// => [1, 2, 3, 4, 5, 6, 7, 8, 9]
	}
}

Sắp xếp danh mục (Danh sách)

Để sắp xếp các phần tử của danh mục, tất cả chúng ta sử dụng lớp tiện ích Collections.sort().

Chẳng hạn:


package com.gpcoder.sorting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Danh sách;

public class SortedListExample {
	public static final Danh sáchvàlt;Integervàgt; NUMBERS = Arrays.asList( 5, 1, 2, 4, 3, 6, 7, 9, 8 );

	public static void main(String[] args) {
		// Sorting α Danh sách
		Danh sáchvàlt;Integervàgt; list1 = new ArrayListvàlt;>(NUMBERS);
		Collections.sort(list1);
		System.out.println(list1);
		// => [1, 2, 3, 4, 5, 6, 7, 8, 9]
	}
}


Sắp xếp tập hợp (Set)

Tất cả chúng ta phải sử dụng LinkedHashSet để có thể giữ được thứ tự các phần tử trong một tập hợp.

Lớp tiện ích Collections chỉ suport sắp xếp các phần tử trong một Danh sách. Do vậy, để có thể sắp xếp được một Set tất cả chúng ta cần chuyển một Set qua một Danh sách, sau đó thực hiện sắp xếp danh mục này & cuối cùng thực hiện chuyển Danh sách về Set.

Chẳng hạn:


package com.gpcoder.sorting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Danh sách;
import java.util.Set;

public class SortedSetExample {
	public static final Danh sáchvàlt;Integervàgt; NUMBERS = Arrays.asList( 5, 1, 2, 4, 3, 6, 7, 9, 8 );

	public static void main(String[] args) {
		// Original data
		Setvàlt;Integervàgt; set1 = new LinkedHashSetvàlt;>(NUMBERS);
		
		// Convert Set to Danh sách
		Danh sáchvàlt;Integervàgt; list1 = new ArrayListvàlt;>(set1);
		
		// Sorting α Danh sách
		Collections.sort(list1);
		
		// Convert Danh sách to Set
		set1 = new LinkedHashSetvàlt;>(list1);
		System.out.println(set1);
		// => [1, 2, 3, 4, 5, 6, 7, 8, 9]
	}
}

Sắp xếp Map

Để có thể lưu trữ được thứ tự các phần tử của Map, tất cả chúng ta phải sử dụng LinkedHashMap.

Một Map gồm 2 thành phần Key & Value. Do vậy, tất cả chúng ta có thể sắp xếp theo Key hoặc Value tùy vào nhu cầu sử dụng.

Sắp xếp Map theo Key

Lớp tiện ích Collections chỉ suport sắp xếp các phần tử trong một Danh sách. Do vậy, để có thể sắp xếp được một Map tất cả chúng ta cần chuyển một Map qua một Danh sách, sau đó thực hiện sắp xếp danh mục này & cuối cùng thực hiện chuyển Danh sách về Map.

Xem Thêm  Cách tạo đường kẻ ngang
trong HTML & CSS 👩‍💻 - tạo một đường ngang css

Chẳng hạn:


package com.gpcoder.sorting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Danh sách;
import java.util.Map;
import java.util.Map.Entry;

public class SortedMapExample {

	public static void main(String[] args) {
		// Original data
		Mapvàlt;Integer, Stringvàgt; map = new HashMapvàlt;>();
		map.put(44, "Four");
		map.put(22, "Two");
		map.put(33, "Three");
		map.put(55, "Five");
		map.put(11, "One");

		// Convert danh mục to map
		Danh sáchvàlt;Map.Entryvàlt;Integer, Stringvàgt;> list1 = new ArrayListvàlt;>(map.entrySet());

		// Create α comparator to sort by key
		Comparatorvàlt;Entryvàlt;Integer, Stringvàgt;> keyComparator = new Comparatorvàlt;Entryvàlt;Integer, Stringvàgt;>() {
			@Override
			public int compare(Entryvàlt;Integer, Stringvàgt; o1, Entryvàlt;Integer, Stringvàgt; o2) {
				return o1.getKey().compareTo(o2.getKey());
			}
		};

		// Sorting α Danh sách
		Collections.sort(list1, keyComparator);

		// Convert Danh sách to Map
		Mapvàlt;Integer, Stringvàgt; sortedMap = new LinkedHashMapvàlt;>();
		for (Map.Entryvàlt;Integer, Stringvàgt; entry : list1) {
			sortedMap.put(entry.getKey(), entry.getValue());
		}
		System.out.println("Original map: " + map);
		System.out.println("Sorted map: " + sortedMap);
	}
}

Output của chương trình trên:


Original map: {33=Three, 22=Two, 55=Five, 11=One, 44=Four}
Sorted map: {11=One, 22=Two, 33=Three, 44=Four, 55=Five

Trong chẳng hạn trên, tôi đã tạo một bộ so sánh (Comparator) để có thể so sánh các phần tử của Map theo Key. Cụ thể về Comparator sẽ được nói trong phần kế tiếp của nội dung.

Sắp xếp Map theo Value

Cũng giống như sắp xếp theo Key, tất cả chúng ta chỉ việc viết lại phương pháp Comparator so sánh theo value:


package com.gpcoder.sorting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Danh sách;
import java.util.Map;
import java.util.Map.Entry;

public class SortedMapExample2 {

	public static void main(String[] args) {
		// Original data
		Mapvàlt;Integer, Stringvàgt; map = new HashMapvàlt;>();
		map.put(44, "Four");
		map.put(22, "Two");
		map.put(33, "Three");
		map.put(55, "Five");
		map.put(11, "One");

		// Convert danh mục to map
		Danh sáchvàlt;Map.Entryvàlt;Integer, Stringvàgt;> list1 = new ArrayListvàlt;>(map.entrySet());

		// Create α comparator to sort by value
		Comparatorvàlt;Entryvàlt;Integer, Stringvàgt;> valueComparator = new Comparatorvàlt;Entryvàlt;Integer, Stringvàgt;>() {
			@Override
			public int compare(Entryvàlt;Integer, Stringvàgt; o1, Entryvàlt;Integer, Stringvàgt; o2) {
				return o1.getValue().compareTo(o2.getValue());
			}
		};

		// Sorting α Danh sách
		Collections.sort(list1, valueComparator);

		// Convert Danh sách to Map
		Mapvàlt;Integer, Stringvàgt; sortedMap = new LinkedHashMapvàlt;>();
		for (Map.Entryvàlt;Integer, Stringvàgt; entry : list1) {
			sortedMap.put(entry.getKey(), entry.getValue());
		}
		System.out.println("Original map: " + map);
		System.out.println("Sorted map: " + sortedMap);
	}
}

Output của chương trình:


Original map: {33=Three, 22=Two, 55=Five, 11=One, 44=Four}
Sorted map: {55=Five, 44=Four, 11=One, 33=Three, 22=Two}

(*8*)Sắp xếp Map với phương pháp được suport trong Java 8

Với Java 8, Map phân phối thêm một số phương pháp suport sắp xếp theo Key, Value:

  • comparingByKey()
  • comparingByKey(Comparatorvàlt;? super Kvàgt; cmp)
  • comparingByValue()
  • comparingByValue(Comparatorvàlt;? super ?vàgt; cmp)

package com.gpcoder.sorting;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class SortedMapExample3 {

	public static void main(String[] args) {
		// Original data
		Mapvàlt;Integer, Stringvàgt; map = new HashMapvàlt;>();
		map.put(44, "Four");
		map.put(22, "Two");
		map.put(33, "Three");
		map.put(55, "Five");
		map.put(11, "One");
		
		// sort by key
		Mapvàlt;Integer, Stringvàgt; sortedMapByKey = new LinkedHashMapvàlt;>();
		map.entrySet().stream()
                .sorted(Map.Entry.<Integer, Stringvàgt;comparingByKey())
                .forEachOrdered(e -> sortedMapByKey.put(e.getKey(), e.getValue()));
		
		// sort by value
		Mapvàlt;Integer, Stringvàgt; sortedMapByValue = new LinkedHashMapvàlt;>();
		map.entrySet().stream()
                .sorted(Map.Entry.<Integer, Stringvàgt;comparingByValue())
                .forEachOrdered(e -> sortedMapByValue.put(e.getKey(), e.getValue()));

        // print map
		System.out.println("Original map: " + map);
		System.out.println("Sorted map by key: " + sortedMapByKey);
		System.out.println("Sorted map by value: " + sortedMapByValue);
	}
}

Output của chương trình:


Original map: {33=Three, 22=Two, 55=Five, 11=One, 44=Four}
Sorted map by key: {11=One, 22=Two, 33=Three, 44=Four, 55=Five}
Sorted map by value: {55=Five, 44=Four, 11=One, 33=Three, 22=Two}

Sắp xếp các đối tượng bất kỳ

Thỉnh thoảng tất cả chúng ta cần sắp xếp một danh mục đối tượng bất kỳ ví dụ như đối tượng Student, Employee, … Khi đó tất cả chúng ta cần khái niệm một phương pháp so sánh giữa các đối tượng để thực hiện sắp xếp.

Trong Java, so với Arrays.sort() hoặc Collections.sort() tất cả chúng ta có 2 phương pháp để phân phối bộ Comparator:

  • Implement Comparable & override phương pháp compareTo(Ƭ obj).
  • Implement Comparator & override phương pháp compare(Ƭ obj1, Ƭ obj2).

Giá trị trả về của 2 phương pháp này:

  • Nếu < 0 : giá trị ưu tiên của đối tượng đầu tiên to hơn đối tượng thứ hai. Khi thực hiện sắp xếp thì đối tượng đầu tiên sẽ đứng trước đối tượng thứ hai.
  • Nếu = 0 : cả 2 có độ ưu tiên bằng nhau.
  • Nếu > 0 : giá trị ưu tiên của đối tượng đầu tiên bé hơn đối tượng thứ hai.
Xem Thêm  WP Social Fan Page Builder - plugin fanpage

Implement Comparable & override phương pháp compareTo(Ƭ obj)

So với phương pháp này, tất cả chúng ta cần biến đổi lớp gốc (original class), tức là lớp của đối tượng so sánh phải căn chỉnh & implement Comparable Interface để thiết lập bộ so sánh.

Chẳng hạn:


package com.gpcoder.sorting;

public class Student implements Comparablevàlt;Studentvàgt; {

	private int id;
	private String name;
	private int age;

	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	@Override
	public int compareTo(Student s) {
		return this.getName().compareTo(s.getName());
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}

Trong chẳng hạn trên, tôi đã implement một interface java.lang.Comparable & override lại phương pháp compareTo() để thực hiện so sánh các sinh viên theo tên.

Khi đã phân phối bộ so sánh, tất cả chúng ta có thể sắp xếp một danh mục sinh viên thông qua thương thức Collections.sort(ͼ) hay Arrays.sort(arr).

Chẳng hạn sử dụng Collections.sort()


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Collections;
import java.util.Danh sách;

public class SortedObjectExample1 {

	public static void main(String[] args) {

		Danh sáchvàlt;Studentvàgt; students = Arrays.asList( //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		);

		Collections.sort(students);
		students.forEach(System.out::println);
	}
}

Output của chương trình:


Student [id=5, name=Five, age=22]
Student [id=4, name=Four, age=19]
Student [id=1, name=One, age=22]
Student [id=3, name=Three, age=20]
Student [id=2, name=Two, age=18]

Chẳng hạn sử dụng Arrays.sort()


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.stream.Stream;

public class SortedObjectExample2 {

	public static void main(String[] args) {

		Student []students = { //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		};

		Arrays.sort(students);
		Stream.of(students).forEach(System.out::println);
	}
}

Implement Comparator & override phương pháp compare(Ƭ obj1, Ƭ obj2)

So với phương pháp này không làm biến đổi lớp gốc (original class). Tất cả chúng ta có thể tạo một class mới, sử dụng Anonymous function, inner class hoặc sử dụng lamda, implement Comparator Interface để thiết lập bộ so sánh.

Chẳng hạn tạo class mới thiết lập interface Comparator


package com.gpcoder.sorting;

import java.util.Comparator;

public class AgeComparator implements Comparatorvàlt;Studentvàgt; {
	
	@Override
	public int compare(Student s1, Student s2) {
		return s1.getAge() - s2.getAge();
	}
}

Chẳng hạn sử dụng Comparator với Collections.sort()


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Danh sách;

public class SortedObjectExample3 {

	public static void main(String[] args) {

		Danh sáchvàlt;Studentvàgt; students = Arrays.asList( //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		);

		// Anonymous function
		Comparatorvàlt;Studentvàgt; nameComparator1 = new Comparatorvàlt;Studentvàgt;() {
			@Override
			public int compare(Student s1, Student s2) {
				return s1.getName().compareTo(s2.getName());
			}
		};
		Collections.sort(students, nameComparator1);

		// Lambda
		Comparatorvàlt;Studentvàgt; nameComparator2 = (s1, s2) -> s1.getName().compareTo(s2.getName());
		Collections.sort(students, nameComparator2);

		students.forEach(System.out::println);
	}

	// Inner class
	class NameComparator implements Comparatorvàlt;Studentvàgt; {

		@Override
		public int compare(Student s1, Student s2) {
			return s1.getName().compareTo(s2.getName());
		}
	}
}

Chẳng hạn sử dụng Comparator với Arrays.sort()


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Stream;

public class SortedObjectExample4 {

	public static void main(String[] args) {

		Student []students = { //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		};

		Comparatorvàlt;Studentvàgt; nameComparator = new Comparatorvàlt;Studentvàgt;() {
			@Override
			public int compare(Student s1, Student s2) {
				return s1.getName().compareTo(s2.getName());
			}
		};

		Arrays.sort(students, nameComparator);
		Stream.of(students).forEach(System.out::println);
	}
}

So sánh Comparable vs Comparator

Đọc qua lại nội dung: So sánh Comparable vs Comparator.

Sắp xếp danh mục đối tượng sử dụng phương pháp tham chiếu trong Java 8

Chẳng hạn tất cả chúng ta có lớp Helper, lớp này chứa phương pháp compareByAge(). Mẹo này suport việc so sánh 2 đối tượng Student như bên dưới:


package com.gpcoder.sorting;

public class Helper {

	public static int compareByAge(Student s1, Student s2) {
		return s1.getAge() - s2.getAge();
	}
}

Với Java 8, tất cả chúng ta có thể sử dụng lại phương pháp đó cho việc sắp xếp danh mục Student như sau:


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Collections;
import java.util.Danh sách;

public class SortedObjectExample5 {

	public static void main(String[] args) {

		Danh sáchvàlt;Studentvàgt; students = Arrays.asList( //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		);

		Collections.sort(students, Helper::compareByAge);
		
		// Or
		students.sort(Helper::compareByAge);
		
		students.forEach(System.out::println);
	}
}

Sắp xếp theo nhiều điều kiện sử dụng Comparator.comparing() & Comparator.thenComparing() trong Java 8

Java 8 phân phối hai API mới hữu dụng cho việc sắp xếp các phần tử là: comparing() & thenComparing() trong interface Comparator. Hai phương pháp này khá thuận lợi cho việc sắp xếp chuỗi nhiều điều kiện của Comparator.

Xem Thêm  Cách thay đổi phông chữ trong HTML - cách thay đổi phông chữ html

Cú pháp:


public static <Ƭ, Uvàgt; Comparatorvàlt;Tvàgt; comparing(
            Functionvàlt;? super Ƭ, ? extends Uvàgt; keyExtractor) {}

public static <Ƭ, Uvàgt; Comparatorvàlt;Tvàgt; comparing(
            Functionvàlt;? super Ƭ, ? extends Uvàgt; keyExtractor,
            Comparatorvàlt;? super Uvàgt; keyComparator) {}

default <ᑗ extends Comparablevàlt;? super Uvàgt;> Comparatorvàlt;Tvàgt; thenComparing(
            Functionvàlt;? super Ƭ, ? extends Uvàgt; keyExtractor) {}

default <Uvàgt; Comparatorvàlt;Tvàgt; thenComparing(
            Functionvàlt;? super Ƭ, ? extends Uvàgt; keyExtractor,
            Comparatorvàlt;? super Uvàgt; keyComparator) {}

Trong số đó:

  • keyExtractor : là một Function, có trách nhiệm phân phối thông tin khóa cần so sánh.
  • keyComparator : là một Comparator, có trách nhiệm phân phối kết quả so sánh dựa theo khóa được phân phối (keyExtractor). Tham số này không bắt buộc.

Chẳng hạn tất cả chúng ta cần sắp xếp danh mục sinh viên theo tuổi, nếu cùng tuổi thì sắp xếp theo tên. Hãy xem đoạn code bên dưới để thấy sự tiện dụng của việc sử dụng phương pháp comparing() & thenComparing() đối với việc tự tạo Comparator.


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Danh sách;

public class SortedObjectExample5 {

	public static void main(String[] args) {

		Danh sáchvàlt;Studentvàgt; students = Arrays.asList( //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		);

		preJava8(students);
		java8(students);
	}

	private static void preJava8(Danh sáchvàlt;Studentvàgt; students) {
		Comparatorvàlt;Studentvàgt; comparator = new Comparatorvàlt;Studentvàgt;() {
			@Override
			public int compare(Student s1, Student s2) {
				if (s1.getAge() == s2.getAge()) {
					return s1.getName().compareTo(s2.getName());
				}
				return s1.getAge() - s2.getAge();
			}
		};

		Collections.sort(students, comparator);
		System.out.println("PreJava8: ");
		students.forEach(System.out::println);
	}

	private static void java8(Danh sáchvàlt;Studentvàgt; students) {
		Comparatorvàlt;Studentvàgt; comparator = Comparator.comparing(Student::getAge).thenComparing(Student::getName);

		Collections.sort(students, comparator);
		// Or
		students.sort(comparator);

		System.out.println("nJava8: ");
		students.forEach(System.out::println);
	}
}

Sắp xếp đảo ngược (Reverse Order)

Trước Java 8, tất cả chúng ta có thể sắp xếp đảo ngược một danh mục bằng cách đảo ngược bộ so sánh thông qua phương pháp Collections.reverseOrder(comparator).

Với Java 8 phân phối một phương pháp khác giúp tất cả chúng ta có thể sắp xếp đảo ngược danh mục một cách trực tiếp thông qua phương pháp comparator.reversed().

Chẳng hạn:


package com.gpcoder.sorting;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Danh sách;

public class SortedObjectExample7 {

	public static void main(String[] args) {

		Danh sáchvàlt;Studentvàgt; students = Arrays.asList( //
				new Student(1, "One", 22), //
				new Student(2, "Two", 18), //
				new Student(3, "Three", 20), //
				new Student(4, "Four", 19), //
				new Student(5, "Five", 22) //
		);

		// Anonymous function
		Comparatorvàlt;Studentvàgt; nameComparator = new Comparatorvàlt;Studentvàgt;() {
			@Override
			public int compare(Student s1, Student s2) {
				return s1.getName().compareTo(s2.getName());
			}
		};

		Collections.sort(students, Collections.reverseOrder(nameComparator));

		// Or
		Collections.sort(students, nameComparator.reversed());

		students.forEach(System.out::println);
	}
}

Output của chương trình trên:


Student [id=2, name=Two, age=18]
Student [id=3, name=Three, age=20]
Student [id=1, name=One, age=22]
Student [id=4, name=Four, age=19]
Student [id=5, name=Five, age=22]

Ebook đọc qua:

5.0

Nếu bạn thấy hay thì hãy chia sẻ nội dung cho mọi người nhé!

Shares

Comment

phản hồi

Viết một bình luận