タイトル: 要素の数を数える
SEOタイトル: Java ArrayList 要素数カウント完全ガイド
| この記事の要点 |
|
結論: 一行で数える
import java.util.*;
List list = new ArrayList<>(List.of("a", "b", "c", "b", null));
int n = list.size(); // 5
System.out.println(n);
ArrayList#size() は内部に保持している int size フィールドを返すだけなので、常に O(1)。100 万件あっても 1 億件あっても同じ速度です。
コレクション種別ごとの要素数
| 対象 | 取得方法 | 計算量 | 備考 |
|---|---|---|---|
配列 int[] / String[] | arr.length | O(1) | メソッドではなくフィールド。括弧なし |
| ArrayList / LinkedList | list.size() | O(1) | 内部カウント変数 |
| HashMap / TreeMap | map.size() | O(1) | キーの数 |
| HashSet / TreeSet | set.size() | O(1) | 重複排除後の数 |
| String の文字数 | s.length() | O(1) | サロゲートペア注意 |
| Stream の要素数 | stream.count() | O(N) | 全要素を消費 |
| Iterator | カウンタを自前で増やしてループ | O(N) | 専用メソッド無し |
| File 行数 | Files.lines(path).count() | O(N) | 全行読み込み |
条件付きカウント (Stream)
import java.util.stream.*;
List nums = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 偶数の個数
long evens = nums.stream().filter(n -> n % 2 == 0).count(); // 5
System.out.println(evens);
// 文字列リストで長さ 3 以上の数
List words = List.of("a", "ab", "abc", "abcd");
long longWords = words.stream().filter(s -> s.length() >= 3).count(); // 2
// User オブジェクトで age >= 20 の数
long adults = users.stream()
.filter(u -> u.getAge() >= 20)
.count();
// 注意: count() の戻り値は long
int adultsInt = (int) adults; // int への変換に注意
特定の値の出現回数
import java.util.*;
List list = List.of("apple", "banana", "apple", "orange", "apple");
// 方法1: Collections.frequency (推奨、シンプル)
int n = Collections.frequency(list, "apple"); // 3
// 方法2: Stream
long n2 = list.stream().filter("apple"::equals).count(); // 3
// 全要素の出現回数を Map で
Map counts = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// {apple=3, banana=1, orange=1}
System.out.println(counts);
null 要素のカウント
List list = Arrays.asList("a", null, "b", null, "c");
// 全体の数 (null も含む)
int total = list.size(); // 5
// null の数
long nulls = list.stream().filter(Objects::isNull).count(); // 2
// null でない数
long nonNulls = list.stream().filter(Objects::nonNull).count(); // 3
// Collections.frequency でも null を数えられる
int nulls2 = Collections.frequency(list, null); // 2
2 次元・ネストされた要素の合計
// 二重リスト
List> matrix = List.of(
List.of(1, 2, 3),
List.of(4, 5),
List.of(6, 7, 8, 9)
);
// 全要素の合計数
int total = matrix.stream().mapToInt(List::size).sum(); // 9
// flatMap で平坦化してから count
long total2 = matrix.stream().flatMap(List::stream).count(); // 9
// Map> の値合計
Map> byUser = ...;
int totalOrders = byUser.values().stream().mapToInt(List::size).sum();
パフォーマンス比較
| 方法 | 計算量 | 適用場面 |
|---|---|---|
list.size() | O(1) | 全要素数 |
arr.length | O(1) | 配列 |
stream().count() | O(N) | Stream の終端操作 |
stream().filter(...).count() | O(N) | 条件付き |
Collections.frequency(c, v) | O(N) | 特定値の出現回数 |
| ループでカウンタ++ | O(N) | 古典的 / Iterator しか取れないとき |
注意点
1. ArrayList の size() は同期化されていない
// マルチスレッドで add しながら size() を呼ぶと一貫性が崩れる
// → CopyOnWriteArrayList / Collections.synchronizedList / 専用ロック を使う
List safe = Collections.synchronizedList(new ArrayList<>());
int n;
synchronized (safe) {
n = safe.size(); // この瞬間の値
}
2. Iterator では size() が無い
Iterator it = list.iterator();
// ❌ size() メソッドは存在しない
// int n = it.size();
// ✅ ループで自前カウント
int count = 0;
while (it.hasNext()) {
it.next();
count++;
}
// この後 it は使い切られている (再利用不可)
3. Stream は一度しか消費できない
Stream s = list.stream();
long n = s.count(); // OK
// long m = s.count(); // ❌ IllegalStateException: stream has already been operated upon or closed
// 再度数えたいなら list から新しい Stream を作る
long n2 = list.stream().count(); // OK
FAQ
Q: list.length() と書けるか?
A: 書けません。配列だけが .length (フィールド)、List / Set / Map は .size() (メソッド)、String は .length() (メソッド)。Java の歴史的な不統一です。
Q: 1000 万件の ArrayList で size() は遅い?
A: O(1) なので件数に関係なく一瞬。LinkedList でも size() は O(1)(内部カウントを持つ)。ただし「N 番目の要素取得」は LinkedList で O(N)。
Q: int vs long のカウント値
A: list.size() は int (最大 21 億)、stream().count() は long。21 億超のデータでは int ではオーバーフロー。