1. 概要

この記事では、ListタイプとArrayListタイプの使用の違いについて説明します。

まず、ArrayListを使用したサンプル実装を確認します。 次に、 List インターフェースに切り替えて、違いを比較します。

2. ArrayListを使用する

ArrayListは、Javaで最も一般的に使用されるリスト実装の1つです。 配列の上に構築されており、要素を追加/削除すると動的に拡大および縮小できます。 リストが大きくなることがわかっている場合は、初期容量でリストを初期化することをお勧めします。

ArrayList<String> list = new ArrayList<>(25);

ArrayList を参照型として使用することで、 ListAPIにないArrayList APIのメソッドを使用できます。たとえば、 ensureCapacity、 TrimToSize 、またはremoveRange

2.1. 簡単な例

基本的な乗客処理アプリケーションを作成しましょう。

public class ArrayListDemo {
    private ArrayList<Passenger> passengers = new ArrayList<>(20);

    public ArrayList<Passenger> addPassenger(Passenger passenger) {
        passengers.add(passenger);
        return passengers;
    }
    
    public ArrayList<Passenger> getPassengersBySource(String source) {
        return new ArrayList<Passenger>(passengers.stream()
            .filter(it -> it.getSource().equals(source))
            .collect(Collectors.toList()));
    }
    
    // Few other functions to remove passenger, get by destination, ... 
}

ここでは、 ArrayList タイプを使用して、乗客のリストを格納および返しました。 乗客の最大数は20人であるため、リストの初期容量はこれに設定されます。

2.2. 可変サイズのデータの問題

上記の実装は、使用しているListのタイプを変更する必要がない限り正常に機能します。 この例では、 ArrayList を選択し、それが私たちのニーズを満たしていると感じました。

ただし、アプリケーションが成熟するにつれて、乗客の数がかなり変化することが明らかになると仮定しましょう。 たとえば、予約された乗客が5人だけで、初期容量が20の場合、メモリの浪費は75%になります。 よりメモリ効率の高いリストに切り替えることにしたとしましょう。

2.3. 実装タイプの変更

Javaは別のものを提供しますリストと呼ばれる実装 LinkedList 可変サイズのデータを保存する LinkedListは、リンクされたノードのコレクションを使用して、要素を格納および取得します。 基本実装をから変更することにした場合はどうなりますか配列リスト LinkedList

private LinkedList<Passenger> passengers = new LinkedList<>();

デモアプリケーションのすべての関数はArrayListタイプで動作することを想定しているため、この変更はアプリケーションのより多くの部分に影響します。

3. リストに切り替えます

List インターフェイスタイプを使用して、この状況をどのように処理できるかを見てみましょう。

private List<Passenger> passengers = new ArrayList<>(20);

ここでは、より具体的な ArrayList 型ではなく、Listインターフェイスを参照型として使用しています。 すべての関数呼び出しと戻り値に同じ原則を適用できます。 例えば:

public List<Passenger> getPassengersBySource(String source) {
    return passengers.stream()
        .filter(it -> it.getSource().equals(source))
        .collect(Collectors.toList());
}

ここで、同じ問題ステートメントを検討し、基本実装をLinkedListタイプに変更しましょう。 ArrayListクラスとLinkedListクラスはどちらも、Listインターフェイスの実装です。 これで、アプリケーションの他の部分に影響を与えることなく、基本実装を安全に変更できます。 クラスは引き続きコンパイルされ、以前と同様に正常に動作します。

4. アプローチの比較

プログラム全体で具体的なリストタイプを使用する場合、すべてのコードがそのリストタイプと不必要に結合されます。 これにより、将来的にリストタイプを変更することが難しくなります。

さらに、Javaで使用可能なユーティリティクラスは、具象型ではなく抽象型を返します。 たとえば、以下のユーティリティ関数はリストタイプを返します。

Collections.singletonList(...), Collections.unmodifiableList(...)
Arrays.asList(...), ArrayList.sublist(...)

具体的には、元のオブジェクトが ArrayList タイプであっても、ArrayList.sublistListタイプを返します。 そのため、 List APIのメソッドは、同じタイプのリストを返すことを保証しません。

5. 結論

この記事では、ListタイプとArrayListタイプの違いとベストプラクティスを検討しました。

特定のタイプを参照すると、アプリケーションが後で変更されやすくなる可能性があることを確認しました。 具体的には、基盤となる実装が変更されると、アプリケーションの他のレイヤーに影響します。 したがって、特定の参照型を使用するよりも、最も抽象型(最上位のクラス/インターフェース)を使用する方が好まれることがよくあります。

いつものように、例のソースコードはGitHubから入手できます。