Java アドバンス

Java LinkedList

1. Java LinkedList

前章ではArrayListクラスについて学びました。LinkedListクラスは、利用方法においてArrayListとほぼ同じです。

1.1 基本的な実装例

// LinkedListクラスをインポート
import java.util.LinkedList;

public class Main {
  public static void main(String[] args) {
    // 文字列を格納するLinkedListオブジェクトを作成
    LinkedList<String> cars = new LinkedList<String>();
    cars.add("Volvo");
    cars.add("BMW");
    cars.add("Ford");
    cars.add("Mazda");
    System.out.println(cars);
  }
}

2. ArrayList と LinkedList の比較

LinkedListクラスは、ArrayListと同様に同じ型のオブジェクトを多数保持できるコレクションです。

LinkedListArrayListと同じメソッドを持っています。これは、両方のクラスがListインターフェースを実装しているためです。つまり、ArrayListと同じように要素の追加、変更、削除、クリアを行うことができます。

しかし、使い方は似ていても、その内部構造(仕組み)は大きく異なります。

2.1 ArrayListの仕組み

ArrayListクラスは、内部に通常の配列(Array)を持っています。要素が追加されると、その配列内に配置されます。もし配列のサイズが不足した場合は、より大きな新しい配列が作成され、古い配列からデータが移行された後に古い配列は破棄されます。

2.2 LinkedListの仕組み

LinkedListは、要素を「コンテナ(ノード)」に格納して管理します。リストは最初のコンテナへのリンクを持っており、各コンテナはリスト内の次のコンテナへのリンクを持っています。リストに要素を追加する場合、その要素は新しいコンテナに入れられ、そのコンテナがリスト内の既存のコンテナのいずれかとリンクされます。

3. 使い分けのタイミング

基本的には、データの保存とアクセスにはArrayListを、データの頻繁な変更(挿入や削除)にはLinkedListを使用するのが一般的です。

  • ArrayListを使用すべきケース:
    • データの格納と、インデックスによるランダムアクセスが主な目的である場合。
  • LinkedListを使用すべきケース:
    • リストの先頭や中間、末尾に対して、要素の追加や削除を頻繁に行う場合。

4. LinkedList 固有のメソッド

多くの場合、ランダムアクセス(特定の要素への直接アクセス)が必要とされるためArrayListの方が効率的ですが、LinkedListには特定の操作をより効率的に行うための独自のメソッドがいくつか用意されています。

メソッド説明
addFirst()要素をリストの先頭に追加する
addLast()要素をリストの末尾に追加する
removeFirst()リストの先頭にある要素を削除する
removeLast()リストの末尾にある要素を削除する
getFirst()リストの先頭にある要素を取得する
getLast()リストの末尾にある要素を取得する

5. varキーワード

Java 10以降では、型を2回書かずにvarキーワードを使用してLinkedList変数を宣言できます。コンパイラが代入された値から型を自動的に推論します。

これによりコードが短縮されますが、可読性を重視して完全な型記述を好む開発者も少なくありません。モダンなJavaコードでは一般的に見られる構文です。

5.1 実装比較

// varを使わない場合
LinkedList<String> cars = new LinkedList<String>();

// varを使う場合
var cars = new LinkedList<String>();

6. Listインターフェース

Javaのソースコードでは、以下のようにListLinkedListの両方を組み合わせて記述しているのをよく見かけます。

import java.util.List;
import java.util.LinkedList;

List<String> cars = new LinkedList<>();

これは、変数(cars)をList(インターフェース)として宣言し、そこにLinkedList(実際の実装オブジェクト)を格納していることを意味します。LinkedListListインターフェースを実装しているため、このような代入が可能です。

このスタイルは、将来的に実装クラスを別のもの(例えばArrayListなど)に差し替える必要がある場合に、型定義を書き換える手間を最小限に抑えられるため、柔軟な設計手法として推奨されています。