Comparable vs Comparator in Java - Java 147

Comparable vs Comparator in Java – Java 147

Comparable vs Comparator in Java

兩種接口都是常用於集合或陣列比較元素的方式,但在實作上卻有一些不一樣的地方, Comparable 是一個接口,只定義了一個名為 compareTo 的方法,傳回 int ,Comparator 也是一個接口,定義了一個傳入 2 個參數,傳回 int 的 compare 方法,這兩者都是用來對集合排序元素, Comparator vs Comparable in Java 提供這兩種介面的區別和應用,本篇增加了範例,並透過單元測試來驗證產出結果。

Comparable InterfaceComparator Interface
宣告的方法是 compareTo宣告的方法是 compare
提供了單一的排序序列 提供了多種排序順序
影響原類別,即實際類別被修改不影響原類別,即不修改實際類別
存在於 java.lang 中 存在於 java.util 中
使用 Collections.sort(List) 方法對 Comparable 類型的元素進行排序使用 Collections.sort(List, Comparator) 方法對 Comparator 類型的元素進行排序
使用 Arrays.sort(Array) 方法對 Comparable 類型的元素進行排序使用 Arrays.sort(Array, Comparator) 方法對 Comparator 類型的元素進行排序
public interface Comparable<T> {
    public int compareTo(T o);
}  

大於 o 傳回 1 。
等於 o 傳回 0 。
小於 o 傳回 -1 。

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}  

o1 大於 o2 傳回 1 。
o1 等於 o2 傳回 0 。
o1 小於 o2 傳回 -1 。

檔案目錄

./
   +- src
       +- test
       |   +- org
       |       +- ruoxue
       |           +- java_147
       |               +- comparator
       |                   +- ComparableVSComparatorTest.java  

單元測試

Java Comparable vs Comparator 提供集合或陣列排序元素等操作。

Fruit

建立 Fruit 類別,覆寫 toString ,定義屬性和方法,用來建立一個物件。

	@NoArgsConstructor
	@Getter
	@Setter
	@Builder
	public static class Fruit implements Comparable<Fruit> {
		private String name;
		private double quantity;
		private int type;

		public Fruit(String name, double quantity, int type) {
			this.name = name;
			this.quantity = quantity;
			this.type = type;
		}

		public String toString() {
			ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
			builder.appendSuper(super.toString());
			builder.append("name", name);
			builder.append("quantity", quantity);
			builder.append("type", type);
			return builder.toString();
		}

		@Override
		public int compareTo(Fruit o) {
			return Double.compare(this.quantity, o.quantity);
		}
	}

compareTo

Java Comparable vs Comparator 建立集合,比對字串或數字,由小到大排序。

	@Test
	public void compareTo() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Mango", -1, 3),
				new Fruit("Peach", 3, 1), new Fruit("Orange", 2, 1));
		System.out.println(list);

		Collections.sort(list);
		System.out.println(list);
		assertEquals(-1d, list.get(0).getQuantity(), 2);
		assertEquals(Double.MAX_VALUE, list.get(1).getQuantity(), 2);
	}
[{"name":"Mango","quantity":1.7976931348623157E308,"type":1}, {"name":"Mango","quantity":-1.0,"type":3}, {"name":"Peach","quantity":3.0,"type":1}, {"name":"Orange","quantity":2.0,"type":1}]
[{"name":"Mango","quantity":-1.0,"type":3}, {"name":"Orange","quantity":2.0,"type":1}, {"name":"Peach","quantity":3.0,"type":1}, {"name":"Mango","quantity":1.7976931348623157E308,"type":1}]

quantityCompare

Java Comparable vs Comparator 建立集合,比對字串或數字,由小到大排序。

	@Test
	public void quantityCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Peach", -1d, 1),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		Comparator<Fruit> quantityComparator = (o, o2) -> Double.compare(o.quantity, o2.quantity);
		Collections.sort(list, quantityComparator);
		System.out.println(list);
		assertEquals("Peach", list.get(0).getName());
		assertEquals("Orange", list.get(1).getName());
		assertEquals("Mango", list.get(2).getName());
	}
[{"name":"Mango","quantity":1.7976931348623157E308,"type":1}, {"name":"Peach","quantity":-1.0,"type":1}, {"name":"Orange","quantity":2.0,"type":1}]
[{"name":"Peach","quantity":-1.0,"type":1}, {"name":"Orange","quantity":2.0,"type":1}, {"name":"Mango","quantity":1.7976931348623157E308,"type":1}]

nameCompare

Java Comparator vs Comparable 建立集合,比對字串或數字,由小到大排序。

	public static Comparator<Fruit> nameComparator = new Comparator<Fruit>() {
		@Override
		public int compare(Fruit o1, Fruit o2) {
			return o1.name.compareTo(o2.name);
		}
	};

	@Test
	public void nameCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Peach", -1d, 1),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		Collections.sort(list, nameComparator);
		System.out.println(list);
		assertEquals("Mango", list.get(0).getName());
		assertEquals("Orange", list.get(1).getName());
		assertEquals("Peach", list.get(2).getName());
	}
[{"name":"Mango","quantity":1.7976931348623157E308,"type":1}, {"name":"Peach","quantity":-1.0,"type":1}, {"name":"Orange","quantity":2.0,"type":1}]
[{"name":"Mango","quantity":1.7976931348623157E308,"type":1}, {"name":"Orange","quantity":2.0,"type":1}, {"name":"Peach","quantity":-1.0,"type":1}]

typeCompare

Java Comparator vs Comparable 建立集合,比對字串或數字,由小到大排序。

	@Test
	public void typeCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", 1.2, 2), new Fruit("Peach", 1.1, 3),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		list.sort(Comparator.comparing(Fruit::getType));
		System.out.println(list);
		assertEquals("Orange", list.get(0).getName());
		assertEquals("Mango", list.get(1).getName());
		assertEquals("Peach", list.get(2).getName());
	}
[{"name":"Mango","quantity":1.2,"type":2}, {"name":"Peach","quantity":1.1,"type":3}, {"name":"Orange","quantity":2.0,"type":1}]
[{"name":"Orange","quantity":2.0,"type":1}, {"name":"Mango","quantity":1.2,"type":2}, {"name":"Peach","quantity":1.1,"type":3}]

ComparableVSComparatorTest.java

Java Comparator vs Comparable 新增單元測試,驗證 Comparable vs Comparator 是否符合預期。

package org.ruoxue.java_147.comparator;

import static org.junit.Assert.assertEquals;

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

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Test;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class ComparableVSComparatorTest {

	@NoArgsConstructor
	@Getter
	@Setter
	@Builder
	public static class Fruit implements Comparable<Fruit> {
		private String name;
		private double quantity;
		private int type;

		public Fruit(String name, double quantity, int type) {
			this.name = name;
			this.quantity = quantity;
			this.type = type;
		}

		public String toString() {
			ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
			builder.appendSuper(super.toString());
			builder.append("name", name);
			builder.append("quantity", quantity);
			builder.append("type", type);
			return builder.toString();
		}

		@Override
		public int compareTo(Fruit o) {
			return Double.compare(this.quantity, o.quantity);
		}
	}

	@Test
	public void compareTo() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Mango", -1, 3),
				new Fruit("Peach", 3, 1), new Fruit("Orange", 2, 1));
		System.out.println(list);

		Collections.sort(list);
		System.out.println(list);
		assertEquals(-1d, list.get(0).getQuantity(), 2);
		assertEquals(Double.MAX_VALUE, list.get(3).getQuantity(), 2);
	}

	@Test
	public void quantityCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Peach", -1d, 1),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		Comparator<Fruit> quantityComparator = (o, o2) -> Double.compare(o.quantity, o2.quantity);
		Collections.sort(list, quantityComparator);
		System.out.println(list);
		assertEquals("Peach", list.get(0).getName());
		assertEquals("Orange", list.get(1).getName());
		assertEquals("Mango", list.get(2).getName());
	}

	public static Comparator<Fruit> nameComparator = new Comparator<Fruit>() {
		@Override
		public int compare(Fruit o1, Fruit o2) {
			return o1.name.compareTo(o2.name);
		}
	};

	@Test
	public void nameCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", Double.MAX_VALUE, 1), new Fruit("Peach", -1d, 1),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		Collections.sort(list, nameComparator);
		System.out.println(list);
		assertEquals("Mango", list.get(0).getName());
		assertEquals("Orange", list.get(1).getName());
		assertEquals("Peach", list.get(2).getName());
	}

	@Test
	public void typeCompare() {
		List<Fruit> list = Arrays.asList(new Fruit("Mango", 1.2, 2), new Fruit("Peach", 1.1, 3),
				new Fruit("Orange", 2, 1));
		System.out.println(list);

		list.sort(Comparator.comparing(Fruit::getType));
		System.out.println(list);
		assertEquals("Orange", list.get(0).getName());
		assertEquals("Mango", list.get(1).getName());
		assertEquals("Peach", list.get(2).getName());
	}
}

心得分享

Comparable vs Comparator 兩個介面的區別, Comparable 是一個接口,定義了一個名為 compareTo 的方法,傳回 int ,Comparator 也是一個接口,定義了一個傳入 2 個參數,傳回 int 的 compare 方法,使用 Collections.sort(List) 、 Collections.sort(List, Comparator)  對 Comparable 、 Comparator 類型的元素進行排序。

發佈留言