以下のインターフェースのいずれかを実装したクラスを定義する必要がある。
-
java.lang.Comparable
- Javaコアクラスが実装している
- オブジェクト全体に自然順序づけを強制する。(compareToメソッド)
- 実装したオブジェクトの配列はCollection.sort()もしくはArrays.sort()によってソートされる。
-
java.util.Comparator
- クライアントが独自の順序でオブジェクトソートしたい時に使う。
Comparableインターフェース
public interface Comparable<T> {
public int compareTo(T o);
}
Comparableを実装しているクラス
Integer, Double, File, Enum など...
Comparableインターフェースを実装したクラスは、保存されるオブジェクト自身で比較が行われる。
Comparableを実装しているInteger
具体的にIntegerクラスで、比較できる仕組みをみてみる。
public final class Integer extends Number implements Comparable<Integer> {
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
// 保持する値の比較
// 自Obj < 比較対象Obj ならば-1
// 自Obj = 比較対象Obj ならば0
// 自Obj > 比較対象Obj ならば1
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
使用例
// 並び替え
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(new Integer(5));
integerList.add(new Integer(2));
integerList.add(new Integer(3));
integerList.sort(null);
for (Integer integer : integerList) {
System.out.println(integer);
} // 2 3 5
// 比較
System.out.println(new Integer(3).compareTo(new Integer(5))); // -1
System.out.println(new Integer(3).compareTo(new Integer(3))); // 0
System.out.println(new Integer(5).compareTo(new Integer(3))); // 1
List<String> stringList = new ArrayList<String>();
stringList.add("c");
stringList.add("d");
stringList.add("a");
stringList.sort(null);
for(String str : stringList) {
System.out.println(str);
} // a c d
Comparatorインターフェース
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
// そのほかにもメソッドを持つ
......
}
Comparatorによる独自順序づけとComparableによる自然順序づけとTreeSetの使用例
java.util.TreeMap
-
このクラスでは、格納する要素はComparableを実装したオブジェクトであることを前提とする。
-
Comparableを実装していないとエラーがでる。
class Employee implements Comparable<Employee>{
private String name;
private Integer id;
private Date birth;
public Employee(String name, Integer id, Date birth) {
this.name = name;
this.id = id;
this.birth = birth;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Date getBirth() {
return birth;
}
public String toString() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
Calendar cal = Calendar.getInstance();
cal.setTime(birth);
return "name=" + name + " id=" + id + " birth" + dateFormat.format(cal.getTime());
}
/**
* idを比較します
* @param obj1
* @param obj2
* @return
*/
public int compare(Employee obj1, Employee obj2) {
return obj1.getId().compareTo(obj2.getId());
}
@Override
public int compareTo(Employee o) {
return compare(this, o);
}
}
/**
* EmployeeのIDを元に比較をします
*
*/
class SortID implements Comparator<Employee> {
@Override
public int compare(Employee obj1, Employee obj2) {
return obj1.getId().compareTo(obj2.getId());
}
}
/**
* EmployeeのBirthを元に比較をします
*
*/
class SortBirth implements Comparator<Employee>{
@Override
public int compare(Employee o1, Employee o2) {
return o1.getBirth().compareTo(o2.getBirth());
}
}
class SortSample {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(1990, 01, 05);
Employee e1 = new Employee("aya", 20, cal.getTime());
cal.set(1990, 01, 01);
Employee e2 = new Employee("jon", 10, cal.getTime());
cal.set(1990, 01, 02);
Employee e3 = new Employee("ryu", 50, cal.getTime());
TreeSet<Employee> treeset = new TreeSet<Employee>();
treeset.add(e1);
treeset.add(e2);
treeset.add(e3);
System.out.println("TreeSetに格納されたEmployeeのcompareTo()に基づきIDの昇順で表示");
print(treeset);
TreeSet<Employee> treesetBirth = new TreeSet<Employee>(new SortBirth());
treesetBirth.add(e1);
treesetBirth.add(e2);
treesetBirth.add(e3);
System.out.println("TreeSetに格納されたEmployeeをSortBirthで定義したbirthの昇順での表示");
print(treeset);
ArrayList<Employee> ary = new ArrayList<Employee>();
ary.add(e1);
ary.add(e2);
ary.add(e3);
System.out.println("ArrayList のインデックス順での表示");
print(ary);
Collections.sort(ary, new SortID());
System.out.println("SortID で定義したid の昇順での表示");
print(ary);
Collections.sort(ary, new SortBirth());
System.out.println("SortBirth で定義したbirthの昇順での表示");
print(ary);
}
public static void print(Collection<Employee> ary) {
for (Employee obj : ary) {
System.out.println(obj);
}
}
}
実行結果
TreeSetに格納されたEmployeeのcompareTo()に基づきIDの昇順で表示
name=jon id=10 birth1990/02/01
name=aya id=20 birth1990/02/05
name=ryu id=50 birth1990/02/02
TreeSetに格納されたEmployeeをSortBirthで定義したbirthの昇順での表示
name=jon id=10 birth1990/02/01
name=aya id=20 birth1990/02/05
name=ryu id=50 birth1990/02/02
ArrayList のインデックス順での表示
name=aya id=20 birth1990/02/05
name=jon id=10 birth1990/02/01
name=ryu id=50 birth1990/02/02
SortID で定義したid の昇順での表示
name=jon id=10 birth1990/02/01
name=aya id=20 birth1990/02/05
name=ryu id=50 birth1990/02/02
SortBirth で定義したbirthの昇順での表示
name=jon id=10 birth1990/02/01
name=ryu id=50 birth1990/02/02
name=aya id=20 birth1990/02/05
TreeSetにStringを入れる
class TreeSetSample{
public static void main(String[] args) {
HashSet<String> hash = new HashSet<String>();
hash.add("D"); hash.add("B"); hash.add("C");
System.out.println("HashSet : " + hash);
TreeSet<String> tree = new TreeSet<String>();
tree.add("D"); tree.add("B"); tree.add("C");
System.out.println("TreeSet : " + tree);
}
}
StringでのComparableについて
StringクラスはComparableのcompareToを実装しつつ、
大文字小文字を無視するためにComparatorも定義してString.CASE_INSENSITIVE_ORDERフィールドを持っている。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
}
class StringSortSample {
public static void main(String[] args) {
List<String> list = Arrays.asList("B", "a", "123", "c");
System.out.println("Collections.sortを使う");
Collections.sort(list);
System.out.println(list);
System.out.println("String.CASE_INSENSITIVE_ORDERを使う");
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
System.out.println(list);
System.out.println("compareToIgnoreCaseを使う");
Collections.sort(list, (s1, s2) -> s1.compareToIgnoreCase(s2));
System.out.println(list);
}
}
実行結果
Collections.sortを使う
[123, B, a, c]
String.CASE_INSENSITIVE_ORDERを使う
[123, a, B, c]
compareToIgnoreCaseを使う
[123, a, B, c]