SetインターフェースにtoArrayメソッドが定義されています。Setインターフェースの実装クラスであるHashSet/LinkedHashSet/TreeSetでtoArrayメソッドの出力結果を確認しました。この記事の趣旨はtoArrayメソッドで以下の出力順序、特にTreeSetの出力を確認することです。
クラス | 順序 |
---|---|
HashSet | 決まってない |
LinkedHashSet | 格納順 |
TreeSet | ソート順 |
ここでソート順はComparable#compareToメソッドの実装で決まります。
#Integer型を格納した場合
API仕様書を見るとcompareToの実装は「2つのIntegerオブジェクトを数値的に比較します。」となっています。結果を見ると一目瞭然ですが、数値の昇順です。
以下、実験ソースと結果です。
package practice.test20160809;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
public class SetInteger {
public static void main(String[] args) {
Set<Integer> hashSet = new HashSet<>();
Set<Integer> linkedHashSet = new LinkedHashSet<>();
Set<Integer> treeSet = new TreeSet<>();
// 乱数格納
Random random = new Random();
int randomNum = 0;
for (int i = 0; i < 10; i++) {
randomNum = random.nextInt(1000);
hashSet.add(randomNum);
linkedHashSet.add(randomNum);
treeSet.add(randomNum);
}
// 配列に変換
Integer[] a = hashSet.toArray(new Integer[0]);
Integer[] b = linkedHashSet.toArray(new Integer[0]);
Integer[] c = treeSet.toArray(new Integer[0]);
// 出力
System.out.printf("%-3s %-3s %-3s", "H", "L", "T");
System.out.println();
System.out.println("-----------");
for (int i = 0; i < a.length; i++) {
System.out.printf("%3d %3d %3d", a[i], b[i], c[i]);
System.out.println();
}
}
}
H L T
-----------
145 611 116
449 987 145
706 910 436
611 436 449
436 116 611
116 706 706
916 916 772
772 145 910
987 449 916
910 772 987
#String型の場合
API仕様書を確認すると、compareToの実装は「2つの文字列を辞書的に比較します。」となってます。
以下、実験ソースと結果です。
package practice.test20160809;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
public class SetString {
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
Set<String> linkedHashSet = new LinkedHashSet<>();
Set<String> treeSet = new TreeSet<>();
// ランダム文字列格納
Random random = new Random();
for (int i = 0; i < 10; i++) {
// 単語生成
int randomLength = random.nextInt(10) + 1;
StringBuilder sb = new StringBuilder();
for (int j = 0; j < randomLength; j ++) {
char c = (char)('a' + random.nextInt(26));
sb.append(c);
}
String s = sb.toString();
hashSet.add(s);
linkedHashSet.add(s);
treeSet.add(s);
}
// 配列に変換
String[] a = hashSet.toArray(new String[0]);
String[] b = linkedHashSet.toArray(new String[0]);
String[] c = treeSet.toArray(new String[0]);
// 出力
System.out.printf("%-10s %-10s %-10s", "Hash", "LinkedHash", "Tree");
System.out.println();
System.out.println("--------------------------------");
for (int i = 0; i < a.length; i++) {
System.out.printf("%-10s %-10s %-10s", a[i], b[i], c[i]);
System.out.println();
}
}
}
Hash LinkedHash Tree
--------------------------------
grsenjlhnf ic ehwsfshpi
ehwsfshpi grsenjlhnf grsenjlhnf
xokldpxsc uidkyjo hojvhutuh
ifis xokldpxsc ic
hojvhutuh hojvhutuh ifis
ic zziqsg jfk
uidkyjo jfk rlfu
zziqsg ehwsfshpi uidkyjo
rlfu ifis xokldpxsc
jfk rlfu zziqsg
TreeSetでは辞書式に出力されているのが分かります。
#自作クラスの場合
TreeSetのインスタンスをコンストラクタTreeSet()で生成した場合、このインスタンスに要素を格納するためにはComparableインターフェースを実装し、compareToメソッドを実装する必要があります。もし、実装していないクラスをaddの引数に指定すると以下のエラーが出ます。TreeSet(Comparator super E> comparator) を使った場合、前述の実装は不要です。
Exception in thread "main" java.lang.ClassCastException: practice.test20160809.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1290)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at ...
実験的にStudentクラスを作成しました。フィールドにint型、String型の変数を持つクラスです。Comparableインターフェースを実装しました。
並べ方の優先順位は以下のように実装しました。
- yearで数値の昇順
- nameで辞書式順序
また、出力結果を出しやすくするため、toStringメソッドをオーバーライドしてます。
package practice.test20160809;
public class Student implements Comparable<Student> {
public int year;
public String name;
public Student(int year, String name) {
super();
this.year = year;
this.name = name;
}
@Override
public String toString() {
return String.format("[ year: %d, name: %-10s ]", year, name);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + year;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (year != other.year)
return false;
return true;
}
@Override
public int compareTo(Student o) {
if (this.year != o.year) {
return this.year - o.year;
} else {
return this.name.compareTo(o.name);
}
}
}
以下、上記クラスを使った例と実行結果です。
package practice.test20160809;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
public class SetStudent {
public static void main(String[] args) {
Set<Student> hashSet = new HashSet<>();
Set<Student> linkedHashSet = new LinkedHashSet<>();
Set<Student> treeSet = new TreeSet<>();
// ランダム文字列格納
Random random = new Random();
for (int i = 0; i < 10; i++) {
// 学年生成
int year = random.nextInt(3) + 1;
// 名前生成
int randomLength = random.nextInt(10) + 1;
StringBuilder sb = new StringBuilder();
for (int j = 0; j < randomLength; j ++) {
char c = (char)('a' + random.nextInt(26));
sb.append(c);
}
String name = sb.toString();
// 格納
hashSet.add(new Student(year, name));
linkedHashSet.add(new Student(year, name));
treeSet.add(new Student(year, name));
}
// 配列に変換
Student[] a = hashSet.toArray(new Student[0]);
Student[] b = linkedHashSet.toArray(new Student[0]);
Student[] c = treeSet.toArray(new Student[0]);
// 出力
System.out.printf("%-29s %-29s %-29s", "Hash", "LinkedHash", "Tree");
System.out.println();
System.out.println("-----------------------------------------------------------------------------------------");
for (int i = 0; i < a.length; i++) {
System.out.printf("%-10s %-10s %-10s", a[i].toString(), b[i].toString(), c[i].toString());
System.out.println();
}
}
}
Hash LinkedHash Tree
-----------------------------------------------------------------------------------------
[ year: 3, name: tpn ] [ year: 3, name: slq ] [ year: 1, name: bgqndpfr ]
[ year: 2, name: axobm ] [ year: 1, name: bgqndpfr ] [ year: 1, name: vgcdsse ]
[ year: 3, name: swez ] [ year: 2, name: axobm ] [ year: 2, name: axobm ]
[ year: 2, name: grdhguyhw ] [ year: 3, name: uija ] [ year: 2, name: grdhguyhw ]
[ year: 2, name: kgfcfsckl ] [ year: 1, name: vgcdsse ] [ year: 2, name: kgfcfsckl ]
[ year: 3, name: slq ] [ year: 3, name: ojdgacgzf ] [ year: 3, name: ojdgacgzf ]
[ year: 3, name: uija ] [ year: 3, name: swez ] [ year: 3, name: slq ]
[ year: 1, name: vgcdsse ] [ year: 3, name: tpn ] [ year: 3, name: swez ]
[ year: 1, name: bgqndpfr ] [ year: 2, name: grdhguyhw ] [ year: 3, name: tpn ]
[ year: 3, name: ojdgacgzf ] [ year: 2, name: kgfcfsckl ] [ year: 3, name: uija ]
TreeSetで並べ替えが機能しているが分かります。
##TreeSet(Comparator super E> comparator)の例
例えば以下のようにします。
package practice.test20160809StudentComparator;
import java.util.Comparator;
public class ComparatorStudent implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
if (o1.year != o2.year) {
return o1.year - o2.year;
} else {
return o1.name.compareTo(o2.name);
}
}
}
Set<Student> treeSet = new TreeSet<>(new ComparatorStudent());
#修正履歴
saka1029さんの指摘に対応。
- 「自作クラスの場合」の冒頭の記述を修正。
- TreeSet(Comparator super E> comparator)の例を追加。