1. {1, 2, 2, 3, 3, 3} は重複を除くと 3 種類
  2. 空配列は 0 種類
  3. {5, 5, 5} は同じ値のみなので 1 種類
  4. {1, 2, 3, 4, 5} は重複なしなのでそのまま 5 種類
  5. 負の数と 0 を含むケースでも値ごとに数える
コース一覧
Java 中級:コレクション・Stream・例外処理
distinct で重複排除

Java 中級:コレクション・Stream・例外処理

コレクション、ジェネリクス、例外処理、Stream API、関数型インターフェース、日付処理、ファイル I/O、継承など Java の中級トピックを実践的に学べるコースです。Java の基礎文法とオブジェクト指向を理解している学習者を対象に、現場の Java コードを読み書きするための知識を補強します。約 16 時間 (1 日 30 分 × 32 日) で 64 レッスンを修了でき、修了後は Spring Boot や業務システム開発に必要な Java 力が身につきます。

1
コレクション基礎
01. ArrayList を使う5分
02. List を走査する5分
03. 要素の削除と検索5分
04. HashMap の基本5分
05. Map を走査する5分
06. Set で重複を排除5分
07. Collections クラスの便利メソッド5分
08. コレクション基礎 まとめクイズ5分
2
ジェネリクス
01. ジェネリッククラスを作る5分
02. ジェネリックメソッド5分
03. 境界型パラメータ5分
04. ワイルドカード ?5分
05. Pair<K,V> を作る5分
06. ジェネリクス まとめクイズ5分
3
例外処理
01. try-catch で例外をつかむ5分
02. 複数の catch ブロック5分
03. throws と例外の伝播5分
04. 独自の例外を作る5分
05. try-with-resources5分
06. finally ブロック5分
07. 例外処理 まとめクイズ5分
4
Stream API
01. Stream を作る5分
02. filter で絞り込む5分
03. map で変換する5分
04. reduce で集約する5分
05. collect で集める5分
06. sorted で並び替え5分
07. distinct で重複排除5分
08. limit と skip5分
09. 並列ストリーム5分
10. Stream API まとめクイズ5分
5
関数型インターフェース
01. ラムダ式の基本5分
02. メソッド参照 ::5分
03. Function インターフェース5分
04. Predicate インターフェース5分
05. Consumer と Supplier5分
06. BiFunction と 2 引数の関数5分
07. 関数型インターフェース まとめクイズ5分
6
文字列操作
01. String.format で書式化5分
02. StringBuilder で連結5分
03. 正規表現の基本5分
04. split と join5分
05. 文字列操作 まとめクイズ5分
7
日付と時刻
01. LocalDate で日付を扱う5分
02. LocalDateTime で時刻も扱う5分
03. Duration で時間差を計算5分
04. 日付の書式化とパース5分
05. 日付の加減算5分
06. 日付と時刻 まとめクイズ5分
8
ファイル I/O
01. Files でファイルに書き込む5分
02. ファイルを行単位で読む5分
03. Path でファイルパスを扱う5分
04. BufferedReader と Stream API5分
05. ファイルの存在確認と削除5分
06. ファイル I/O まとめクイズ5分
9
継承とポリモーフィズム
01. extends で継承する5分
02. super で親を呼ぶ5分
03. メソッドのオーバーライド5分
04. abstract で抽象クラス5分
05. interface で契約を定義5分
06. default メソッド5分
07. instanceof と型パターン5分
08. ポリモーフィズムを活かす5分
09. 継承とポリモーフィズム まとめクイズ5分

distinct で重複排除

同じ人を 3 回数えてしまう

アクセスログのユーザー ID を並べたリストから「何人が来たか」を出したいとします。行数をそのまま数えると、答えがずれます。

Java

List<String> ids = List.of("u1", "u2", "u1", "u3", "u1"); System.out.println(ids.size()); // 5

来たのは 3 人ですが、u1 が 3 回訪れているので 5 と出ます。自力で数えようとすると、すでに見た ID を覚えておく Set を用意して、for の中で「入っていなければ足す」を書くことになります。ここまで来ると、やりたいことより準備の方が長くなります。

distinct() は、この「すでに見たかどうか」を代わりに覚えてくれる中間操作です。

Java

List<String> unique = ids.stream().distinct().toList(); // [u1, u2, u3]

最初に出てきた 1 つを残し、後から来た同じものを捨てます。残った要素の順番は、元の並びのままです。

同じかどうかは equals と hashCode で決まる

String や Integer なら、distinct() は素直に効きます。困るのは自作クラスです。

Java

List<User> users = List.of(new User("u1"), new User("u1")); System.out.println(users.stream().distinct().toList()); // 2 件とも残る

id はどちらも u1 なのに、2 件とも残ります。distinct() は中身を見比べているのではなく、要素に equals と hashCode を尋ねているからです。何も書いていないクラスは、この 2 つを Object から受け継いだままで、その中身は「同じ場所にあるオブジェクトだけを同じとみなす」というものです。new を 2 回すれば別の場所にできるので、別物と判定されます。

自作クラスで中身の一致を見てほしいなら、両方を自分で書きます。

Java

class User { String id; User(String id) { this.id = id; } @Override public boolean equals(Object o) { if (!(o instanceof User)) return false; return id.equals(((User) o).id); } @Override public int hashCode() { return id.hashCode(); } }

hashCode を書き忘れると、equals だけでは重複が残ります。distinct() は速く探すために先に hashCode で振り分けてから equals を呼ぶので、hashCode がばらばらだと、equals を呼ぶところまで辿り着けないからです。この 2 つは必ずセットで書きます。

解説

IDE には equals と hashCode をまとめて生成する機能があります。片方だけ手書きするより、生成させて必要なフィールドだけ選ぶ方が確実です。

やってみよう

Solution.uniqueCount(int[] arr) を完成させて、配列に何種類の値が入っているかを返してください。

ファイルの先頭に import java.util.stream.IntStream; を書き、IntStream.of(arr) から Stream を作ります。distinct() を通してから、要素の個数を数えてください。今回扱うのは int なので、equals や hashCode を自分で書く必要はありません。

数えた結果は long で返ってくるので、(int) にキャストしてから return します。空配列を渡したときは、通る要素が 1 つも無いので 0 になります。

要件

  1. ファイル先頭で import java.util.stream.IntStream; を書くこと
  2. IntStream.of(arr) から Stream を作り、distinct() を必ず使うこと
  3. count() の戻り値 (long) を int にキャストして return すること

入出力例

uniqueCount([1,2,2,3,3,3]) → 3 uniqueCount([]) → 0 uniqueCount([5,5,5]) → 1 uniqueCount([1,2,3,4,5]) → 5 uniqueCount([-1,0,0,-1,2]) → 3

ヒント

`IntStream.of(arr).distinct().count()` で重複を除いた要素数 (`long`) が取れます

`count()` は `long` を返すので、戻り値の型 `int` に合わせて `(int)` キャストしましょう

空配列でも `count()` が `0L` を返してくれるので、`if` で分岐する必要はありません

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • limit と skip

    Stream API の中間操作 limit と skip を使い、要素を先頭から数件だけ取り出したり、先頭の数件を飛ばしたりする方法を学んで、配列のスライス処理を Stream で書いてみよう。

  • 並列ストリーム

    Stream API の parallel() で配列を複数 CPU コアに分割集計させる並列ストリームの仕組みと、結合性・スレッド安全・順序保証など並列化の落とし穴を学びます。

  • Stream API まとめクイズ

    Stream API 章で学んだ生成 / filter / map / reduce / collect / sorted / distinct / limit / skip / parallel を、メソッドチェーンの順序と中間操作・ターミナル操作の使い分けで総ざらいする 4 択クイズ。

  • ラムダ式の基本

    Java 8 から導入されたラムダ式の文法と関数型インターフェースの考え方を学び、Stream と組み合わせて配列の合計を一行で書く感覚を身につけよう。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • リスト順序付きで複数の値を扱うデータ構造
  • クラスデータと振る舞いを束ねる単位
  • オブジェクトキーと値のペアで構造を作るデータ
  • セット重複を持たない順序なし集合
  • フィールドクラスが持つデータ
  • 配列サイズ固定の同型データの集まり
  • instanceof「このオブジェクトの実際の型は何か」を確認 → 安全に変換
  • 戻り値呼び出し元への返答を表す点線矢印
main.java
学習モード
エディタを読み込んでいます

メモ

distinct で重複排除

⌘S で保存