【Java入門】ListとSetクラスの変換・違いまとめ

皆さんはListクラスを使っていますか?
またSetクラスを使っていますか?
これらのクラスを使っていくと次のような疑問を聞くことがあります。

「ListのデータをSetクラスに変換したい」
「ListとSetはどうやって使い分けるの?」
「データ量が多い場合ListとSetどちらが高速に処理できるの?」

この記事ではそんな疑問・要望を解決しListとSetの使い分けについて学ぶ事ができます!

なお、Javaの記事については、こちらにまとめています。

目次

Setクラスとは

SetクラスListクラスと同じように扱えるため一見すると何が違うのかわかりづらいかもしれません。
しかし大きな違いがありその差を理解していないと思わぬ不具合を生むことにもなってしまいます。

Setクラスは主にHashSetTreeSetといったクラスがありますが今回はHashSetを例に用いて説明をしていきます。

まずListとSetの大きな違いについてですが「Setクラスは同じ値を持つことができない」という特性があります。

例えば次のコードを見てみましょう。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Set hash_set = new HashSet();
        hash_set.add(1);
        hash_set.add(2);
        hash_set.add(3);
        hash_set.add(1);
        
        for(Integer value : hash_set)
        {
            System.out.println(value);
        }
    }
}

実行結果:

1
2
3

このようになります。
あれ?おかしいと思いませんか?

has_set.add(1);

を二回書いているにも関わらず表示されている値は3つしかありません。
1は一度しか表示されないのです。

つまり、Setクラスは重複する値をいくつも持つことができないという特性があるのです。

では同じプログラムをListで書き直すとどうなるのでしょうか。
次のプログラムで実行結果も併せてみてみましょう。

import java.util.*;
 
public class Main {
    public static void main(String[] args) {       
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        
        for(Integer value : list)
        {
            System.out.println(value);
        }
    }
}

実行結果:

1
2
3
1

こちらの場合では追加した通りの値が入っていることがわかります。
これでListとSetの大きな違いについて分かって頂けたのではないかと思います!

List⇔Set変換方法

では次にList⇔Setへ変換する方法について解説したいと思います。

List→Setへ変換

ListからSetの変換方法について見てみましょう。

import java.util.*;
 
public class Main{
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        
        Set set = new HashSet(list);
        
        for(Integer value : set)
        {
            System.out.println(value);
        }
    }
}

実行結果:

1
2
3

HashSetをnewした際に引数にListを渡しています。
これで自動的に値が設定されて変換することができます。

上記の例を見て頂ければわかる通り、もし重複する値があったとしても自動で取り除かれます。

Set→Listへ変換

今度は逆のSetからListへ変換する場合について見ていきましょう。

import java.util.*;

public class Main{
    public static void main(String[] args) {
        Set set = new HashSet();
        set.add(1);
        set.add(2);
        set.add(3);
        
        List list = new ArrayList(set);
        
        for(Integer value : list)
        {
            System.out.println(value);
        }
    }
}

実行結果:

1
2
3

先程のList→Setと似たようなプログラムですね。
最初にHashSetをnewして値を追加していき、ArrayListをnewした際にコンストラクタにsetを渡してあげることで自動的に値が格納されていきます。

方法としてはどちらも同じようなやり方になるため覚えやすいのではないかと思います。

応用編:多くのデータを扱う場合

この項ではさらに掘り下げて解説をしていきたいと思います。

ListクラスとSetクラスでどちらのほうが速度面で有利なのでしょうか?

今回は「追加」「値の検索」の2つの速度について解説をしていきたいと思います。

この測定ではArrayListHashSetを用いて比較をしていることご了承下さい。

List.add VS Set.add

まずはList.addのコードを示します。

List<Integer> list = new ArrayList<Integer>();

long start = System.nanoTime();

for(int i = 0; i < 10000; i++)
{
    list.add(i);
}

long end = System.nanoTime();
System.out.println("result : " + (end - start) / 1000000f + "ms");

次にSet.addのコードを示します。

Set<Integer> set = new HashSet<Integer>();

long start = System.nanoTime();

for(int i = 0; i < 10000; i++)
{
    set.add(i);
}

long end = System.nanoTime();
System.out.println("result : " + (end - start) / 1000000f + "ms");

List.addの実行結果(3回計測)

result : 2.450521ms
result : 1.695349ms
result : 2.176481ms

Set.addの実行結果(3回計測)

result : 4.246ms
result : 4.048387ms
result : 4.963594ms

追加ではListに軍配が上がりました。
これは恐らくSet側で追加時に重複する値がないかどうかのチェックを行っているからではないかと思われます。

List.contains VS Set.contains

では次にcontainsメソッドではどうなるのでしょうか。
(Setクラスにはgetメソッドが無いため値の取得という意味で一番近いcontainsメソッドで計測をしています。)

最初にList.containsの計測に用いたコードを示します。

List<Integer> list = new ArrayList<Integer>();

for(int i = 0; i < 10000; i++)
{
    list.add(i);
}

long start = System.nanoTime();

for(int i = 0; i < 10000; i++)
{
    list.contains(i);
}

long end = System.nanoTime();
System.out.println("result : " + (end - start) / 1000000f + "ms");

次にSet.containsの計測に用いたコードを示します。

Set<Integer> set = new HashSet<Integer>();

for(int i = 0; i < 10000; i++)
{
    set.add(i);
}

long start = System.nanoTime();

for(int i = 0; i < 10000; i++)
{
    set.contains(i);
}

long end = System.nanoTime();
System.out.println("result : " + (end - start) / 1000000f + "ms");

List.containsの実行結果(3回計測)

result : 63.436344ms
result : 57.827072ms
result : 56.581024ms

Set.containsの実行結果(3回計測)

result : 3.901148ms
result : 5.384363ms
result : 2.951122ms

containsではSet側に軍配が上がりました。
内容としてはListに大きく差をつけています。

追加時の速度ではListが勝っていましたがこのcontainsの結果を見ると、重複しないデータの場合でかなりの量のデータを扱う場面となればSetクラスを使うのが良いのではないかと思います。

まとめ

いかがでしょうか。
今回はListからSet、SetからListの変換方法やどちらのクラスが速度面で優れているかを見ていきました。

SetはListと比べると制約がありますが問題なければSetを使うことをオススメいたします。

もし、ListとSetの比較について忘れてしまった場合はこの記事を思い出してくださいね!

この記事を書いた人

30歳、フリーランスプログラマ。中学の頃よりプログラミングに興味を持ちゲーム開発やWebサイト構築などを経験
新しいフレームワークやライブラリに興味があり革新的な機能が含まれていると泣いて喜ぶ。

目次