LoginSignup
6
8

More than 5 years have passed since last update.

HashSet/LinkedHashSet/TreeSetのtoArrayで出力順序を確認する

Last updated at Posted at 2016-08-09

SetインターフェースにtoArrayメソッドが定義されています。Setインターフェースの実装クラスであるHashSet/LinkedHashSet/TreeSetでtoArrayメソッドの出力結果を確認しました。この記事の趣旨はtoArrayメソッドで以下の出力順序、特にTreeSetの出力を確認することです。

クラス 順序
HashSet 決まってない
LinkedHashSet 格納順
TreeSet ソート順

ここでソート順はComparable#compareToメソッドの実装で決まります。

Integer型を格納した場合

API仕様書を見るとcompareToの実装は「2つのIntegerオブジェクトを数値的に比較します。」となっています。結果を見ると一目瞭然ですが、数値の昇順です。

以下、実験ソースと結果です。

SetInteger.java
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つの文字列を辞書的に比較します。」となってます。

以下、実験ソースと結果です。

SetString.java
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メソッドをオーバーライドしてます。

Student.java
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);
        }
    }

}

以下、上記クラスを使った例と実行結果です。

SetStudent.java
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)の例

例えば以下のようにします。

ComparatorStudent.java
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);
        }
    }

}
TreeSetの生成
        Set<Student> treeSet = new TreeSet<>(new ComparatorStudent());

修正履歴

saka1029さんの指摘に対応。

  • 「自作クラスの場合」の冒頭の記述を修正。
  • TreeSet(Comparator<? super E> comparator)の例を追加。
6
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8