サンプルコード
Java のサンプルコード一覧
Java で「○○するには」を解決するコードサンプル。73 件。
他の言語から探す
2つの日付の差を求めるには
日付単位の差は `ChronoUnit.DAYS.between` が読みやすい。時刻を含む差は `Duration.between` で取るが、`toHours` / `toDays` は切り捨てて整数を返すため、36 時間 30 分は 36 と表示される。端数まで見たいときは `toMinutes` のように細かい単位へ落としてから割る。
配列を条件で絞り込むには
`Stream` の `filter` は中間操作なので、`toList` のような終端操作を呼んで初めて評価される。`Stream.toList` は Java 16 で追加されたメソッドで、それ以前は `collect(Collectors.toList())` を使う。
環境変数を削除するには
Java には自プロセスの環境変数を削除する標準 API がなく、`System.getenv()` が返すマップは変更不可で `remove` すると `UnsupportedOperationException` になる。子プロセスから外したいときは `ProcessBuilder.environment()` が返す環境のコピーに対して `remove` を呼ぶ。この変更は `start()` で起動するその子プロセスにだけ反映される。
QRコードを生成するには
ZXing では `QRCodeWriter` で `BitMatrix` を作り、`MatrixToImageWriter` で PNG に書き出す。誤り訂正レベルの既定は L なので、ほかの言語と条件をそろえるなら `EncodeHintType.ERROR_CORRECTION` で明示する。
ディレクトリ内のファイル一覧を取得するには
`Files.list` が返す `Stream` はディレクトリを開いたままにするので、`try-with-resources` で閉じる。閉じ忘れるとファイルハンドルが残り、多数のディレクトリを走査したときに枯渇する。
ネストしたJSONから値を取り出すには
Jackson の `path` は、キーが無いときに `null` ではなく `MissingNode` を返すため、途中で切れても連鎖を書き続けられる。`asInt` / `asText` に既定値を渡せばそのまま倒せる。`get` だと `null` が返り、次の呼び出しで `NullPointerException` になる。
乱数のシードを固定して同じ結果を再現するには
`new Random(seed)` で同じシードから同じ数列が得られる。`nextInt(1, 101)` のように下限と上限を渡せるのは Java 17 以降で、上限は含まれない。
HTTPリクエストにヘッダーを付けるには
HttpRequest.Builder の header を並べるとリクエスト単位でヘッダーが付く。なお Java の HttpClient は Host や Content-Length など一部のヘッダーを制限しており、自分で設定しようとすると IllegalArgumentException になる。
配列からランダムに1つ選ぶには
標準には 1 要素を選ぶメソッドが無いため、`ThreadLocalRandom` の `nextInt(size)` で添字を作って `get` に渡す。`nextInt` は引数が 0 以下だと `IllegalArgumentException` を投げるので、空のリストでは添字の範囲外より前に失敗する。
ランダムな文字列を生成するには
`SecureRandom` の `nextInt(bound)` で文字集合の添字を選ぶ。`SecureRandom` は暗号用途向けの乱数で、`java.util.Random` はトークンに向かない。`SecureRandom` はスレッドセーフなので、インスタンスを `static final` で 1 つ持って使い回してよい。
環境変数を設定するには
Java には自プロセスの環境変数を変更する標準 API が無く、System.getenv() が返すマップは変更不可である。書き換えられるのは ProcessBuilder.environment() が返す「親の環境のコピー」だけで、これは start() で起動する子プロセスにのみ反映される。自分の JVM に値を渡したいなら起動時に -D でシステムプロパティを使う。
UUIDを生成するには
`java.util.UUID.randomUUID()` が標準でバージョン 4 の UUID を返す。ハイフンなしにするには `toString().replace("-", "")` とする。`UUID.fromString` は不正な文字列を渡すと `IllegalArgumentException` になるので、外部から受け取った値を戻すときは例外を考慮する。
範囲を指定して乱数を得るには
`ThreadLocalRandom.current().nextInt(min, max)` は上限を含まないので、`max + 1` を渡す。暗号用途には `SecureRandom` を使う。Java 17 以降は `SecureRandom` でも同じ `nextInt(min, max)` の形で範囲を指定できる。
配列をランダムに並べ替えるには
`Collections.shuffle` はリストを直接並べ替える。`List.of` が返すリストは変更できないため、そのまま渡すと `UnsupportedOperationException` になる。`new ArrayList<>(List.of(...))` のようにコピーしてから渡す。
.env ファイルを読み込むには
dotenv-java は読み込んだ値を `Dotenv` オブジェクト経由で返し、`System.getenv` には流し込まない(読み込み後の `System.getenv("PORT")` は `null` だった)。したがって取得は `dotenv.get()` を使う。同名の OS の環境変数があればそちらが優先され、`PORT=1111` を付けて起動すると 1111 が出力された。`Dotenv.load()` は .env が無いと `DotenvException` で止まるので、落としたくない場合は `Dotenv.configure().ignoreIfMissing().load()` を使う。
文字列のSHA-256ハッシュ値を求めるには
`MessageDigest.getInstance("SHA-256")` で作ったオブジェクトの `digest` に、文字コードを明示したバイト列を渡す。結果は `byte[]` なので、Java 17 以降の `HexFormat.of().formatHex` で 16 進数の文字列にする。アルゴリズム名を `SHA-2` のように誤ると `NoSuchAlgorithmException` になる。
文字列が含まれるか調べるには
`String.contains` が部分文字列の判定で、既定では大文字小文字を区別する。標準の `String` には `containsIgnoreCase` が無く、それは Apache Commons Lang の `StringUtils` にあるメソッドなので、依存を足したくなければ片方を `toLowerCase` で畳んで比較する。位置が決まっているなら `regionMatches` の第1引数に `true` を渡して部分一致を無視比較できる。
日付をJSON文字列にするには
Jackson の標準の `ObjectMapper` は `java.time` の型を扱えず `InvalidDefinitionException` になるため、`jackson-datatype-jsr310` を足して `JavaTimeModule` を登録する。さらに `WRITE_DATES_AS_TIMESTAMPS` を無効にしないと、日時が文字列ではなく秒数の数値で出力される。
日付文字列を日付型にするには
`java.time` の `parse` は ISO 8601 が既定なので、`LocalDate.parse` や `OffsetDateTime.parse` は書式指定なしで読める。独自書式は `DateTimeFormatter.ofPattern` を渡す。パターンの年は `yyyy` ではなく `uuuu` を選ぶと、紀元をまたぐ入力で意図しない解釈になるのを避けられる。失敗は `DateTimeParseException` になる。
配列から重複を除くには
`LinkedHashSet` は挿入順を保つ集合なので、これを経由すると出現順のまま重複が消える。順序が不要なら `HashSet` で十分だが、その場合は並びが保証されない。
正規表現の特殊文字をエスケープするには
`Pattern.quote` は文字列を `\Q…\E` で囲み、中身をそのままの文字として扱わせる。文字列に `\E` が含まれていても、`\Qx\E\\E\Qy\E` のように囲み直して正しく扱う。`replaceAll` の置換文字列は `$` と `\` が特別扱いされ、存在しないグループを `$1` で参照すると `IndexOutOfBoundsException` になるので、`Matcher.quoteReplacement` を通す。
文字列の前後の空白を除去するには
`String.strip` は `Character.isWhitespace` の定義で前後の空白を落とすため、全角スペース(U+3000)も除去できる。古くからある `trim` は U+0020 以下の文字しか対象にせず全角スペースが残るので、日本語を扱うなら `strip` を選ぶ。片側だけなら `stripLeading` / `stripTrailing` を使う。
ホームディレクトリのパスを取得するには
`System.getProperty("user.home")` は環境変数 `HOME` を見ず、passwd のホームを返す。`HOME=/tmp/other` を付けて実行しても `/home/taro` のままだった。差し替えたいときは `-Duser.home=/tmp/other` のようにシステムプロパティで渡す。`Files.exists(Path.of("~"))` は `~` を展開せず `false` になる。
HTTP POSTでJSONを送るには
Java 11 以降の `java.net.http.HttpClient` で POST を送れる。`BodyPublishers.ofString` に本文の文字列を渡し、`Content-Type` を明示する。この例だけ JSON をテキストブロック(Java 15 以降)で直接書いているのは、Java の標準ライブラリに JSON シリアライザが無いためである。値が固定のうちはこれで足りるが、動的な値を埋めるなら Jackson などに任せる。手で連結すると引用符や改行のエスケープで壊れる。実行すると `201` に続いて、レスポンス本文の JSON が表示される。
ファイルを読み込むには
Files.readString は Java 11 以降で使え、ファイル全体を UTF-8 の String として読み込む。UTF-8 として正しくないバイトがあると置換文字に置き換えずに MalformedInputException を投げるため、文字化けに気づかないまま処理が進むことはない。Java 10 以前では Files.readAllBytes の結果を new String(bytes, StandardCharsets.UTF_8) で文字列にする。
整数を2進数や16進数の文字列にするには
`Integer.toBinaryString` `toOctalString` `toHexString` で文字列にし、`Integer.parseInt(text, radix)` で戻す。`toString(n, radix)` は負の数を `-101` のように符号付きで返すが、`toBinaryString` は 32 ビットの 2 の補数を返す。`parseInt` は `0x` を読めないので、接頭辞付きなら `Integer.decode` を使う。
JSONをパースするには
JDK の標準ライブラリには JSON パーサが含まれないため、Jackson や Gson を依存に追加する必要がある。Jackson の ObjectMapper は readValue に渡した型へマッピングし、record にも対応する。JSON 側に未知のキーがあると既定で UnrecognizedPropertyException になるので、必要なら FAIL_ON_UNKNOWN_PROPERTIES を無効にする。逆にキーが欠けていても例外にはならず、int のフィールドは 0 のまま入る。
オブジェクトをJSON文字列にするには
JDK の標準ライブラリに JSON は含まれないため、Jackson(`jackson-databind`)や Gson を依存に追加する。`writeValueAsString` が 1 行の JSON を返し、`writerWithDefaultPrettyPrinter()` を挟むと整形される。整形の書式は `"name" : "山田"` のようにコロンの前にも空白が入り、短い配列は `[ "api", "cli" ]` と 1 行に収まる。`record` はコンポーネント名がそのままキーになる。
JSONをファイルに保存するには
Jackson の writeValueAsString で先に文字列を作り、Files.writeString で一時ファイルに書いてから Files.move で置き換える。Files.writeString は既定で UTF-8 を使い、BOM も付けない。writerWithDefaultPrettyPrinter の整形はキーの後ろが " : " になり、数値の配列を 1 行にまとめるので、ほかの言語で整形したファイルとは見た目が揃わない。JDK に JSON ライブラリは含まれないため、Jackson(例は 2.21 で確認)を依存に加える。
バイト列を16進数の文字列にするには
`HexFormat` の `formatHex` と `parseHex` で往復できる。出力は既定で小文字で、`withUpperCase()` を付けると大文字になる。不正な文字は `IllegalArgumentException` になる。`HexFormat` は Java 17 以降で使える。
配列を文字列に連結するには
String.join は CharSequence の並びだけを受け取るため、数値のリストは String::valueOf で写してから Collectors.joining に渡す。joining は前後の飾り文字も同時に指定できるので、括弧付きの出力もひと息で作れる。
文字列を分割するには
`String.split` の第1引数は文字列ではなく正規表現として解釈される。ドットやパイプなどのメタ文字をそのまま渡すと意図しない分割になるため、リテラルとして分けたいときは `Pattern.quote` で囲むかエスケープする。空白区切りは `split("\\s+")` のように正規表現を渡すと連続空白をまとめられる。
文字列をUTF-8のバイト列にするには
`getBytes(StandardCharsets.UTF_8)` で文字コードを明示してバイト列にし、戻すときも同じ文字コードを `new String` に渡す。引数なしの `getBytes()` は実行環境の既定の文字コードに結果が左右されるため避ける。`String.length()` は UTF-16 の単位数で、絵文字は 2 と数えられる。`HexFormat` は Java 17 以降で使える。
文字列をBase64にするには
java.util.Base64 が標準で入っており、getEncoder が標準、getUrlEncoder が URL セーフ版で、withoutPadding を付けるとパディングが消える。文字列をバイト列にするときは StandardCharsets.UTF_8 を明示し、実行環境の既定の文字コードに結果が左右されないようにする。
ファイルの文字コードを変換するには
Files.readString と Files.writeString に Charset を渡して変換する。Windows 由来のファイルは "Shift_JIS" ではなく "windows-31j" を指定する。前者は丸数字などの機種依存文字を含むファイルで MalformedInputException になる。
HTMLの特殊文字をエスケープするには
標準ライブラリには HTML をエスケープする汎用のメソッドが無いため、char ごとに switch で置き換える関数を書く。StringBuilder に書き出す方式なので、& を先に処理する順序を意識しなくてよい。
HTTP GETリクエストを送るには
Java 11 で java.net.http.HttpClient が標準に入ったので、GET を送るだけなら外部ライブラリは要らない。JSON を構造体へ写す段になって初めて Jackson などが必要になる、という切り分けで考えると依存を最小にできる。
文字列をURLエンコードするには
URLEncoder.encode は文字コードを必ず明示する。引数が文字列だけの旧メソッドは非推奨で、javac の -Xlint:deprecation で警告が出る。出力はフォーム形式でスペースが + になるため、%20 が必要なときは置き換える。
日付を書式付きの文字列にするには
DateTimeFormatter のパターンは大文字と小文字で意味が別で、MM が月、mm が分である。定型の ISO 形式なら ISO_LOCAL_DATE などの定数が用意されているので、自前のパターンを書かずに済ませる。
文字列の長さを取得するには
String.length は UTF-16 の char 単位数を返すので、絵文字などの補助文字は 2 と数えられる。コードポイントの数は codePointCount で求める。バイト数は文字コードを明示して getBytes から取り、引数なしの getBytes は実行環境の既定の文字コードに依存するため避ける。
ファイルが存在するか調べるには
Files.isRegularFile は存在しない場合も属性を読めない場合も false を返す。Files.exists はディレクトリでも true になるため、ファイルとして開く前提なら isRegularFile を選ぶ。存在しないと確かめられたときだけ true になる Files.notExists もあり、権限不足で調べられないときは exists も notExists も false になる。
UNIXタイムスタンプを日付に変換するには
Instant.ofEpochSecond は秒から UTC 上の瞬間を作り、表示したい地域は atZone で付ける。ミリ秒は ofEpochMilli で渡す。秒の引数にミリ秒の値を渡しても例外にはならず、遠い未来の日付になる。
配列に要素が含まれるか調べるには
List の contains は equals で比較する。配列は Arrays.asList で包めば同じ書き方で探せるが、int[] のようなプリミティブ配列は要素が int ではなく int[] 1 個として扱われ、常に false になる。List.of が返す不変リストは null を許さないので、contains(null) は NullPointerException を投げる。
現在時刻を取得するには
Instant は常に UTC 上の瞬間を表し、タイムゾーンの概念を持たない。地域時刻として表示したいなら ZonedDateTime を使う。可変で扱いにくい java.util.Date ではなく、Java 8 以降は java.time パッケージを使うのが標準である。
ディレクトリを作成するには
Files.createDirectory は親が無いと NoSuchFileException、既にあると FileAlreadyExistsException になる。準備処理には、親までまとめて作り、既にディレクトリがあれば何もしない Files.createDirectories を使う。
環境変数を読むには
System.getenv は未設定の環境変数に対して null を返すため、そのまま使うと NullPointerException の原因になる。null チェックで既定値を補うか、Optional でラップして扱う。
配列をソートするには
配列は Arrays.sort、List は Collections.sort か list.sort でその場で昇順に並べ替える。降順や独自基準にするときは Comparator を渡す。プリミティブの int[] は Comparator を使えないため、降順にするなら Integer[] にするか後で反転する。
数値をゼロ埋めするには
String.format の %05d は、幅 5 に足りない分を符号の後ろのゼロで埋める。0 フラグは数値の変換にしか使えず、%05s のように文字列に付けると FormatFlagsConversionMismatchException が投げられる。小数の %f は既定ロケールの小数点を使うため、Locale.ROOT などを明示して出力を固定する。
数値を指定した範囲に収めるには
Math.clamp は Java 21 で追加されたメソッドで、javac --release 20 でコンパイルすると cannot find symbol になる。long の値と int の範囲を渡す版は int を返すので、キャストせずに int の変数で受け取れる。下限が上限より大きいと IllegalArgumentException になる。
文字列を置換するには
replace は正規表現を使わないリテラルの全置換である。replaceAll と replaceFirst は第 1 引数を正規表現として扱うため、"a.b.c".replaceAll(".", "-") は ----- になり、記号を文字どおりに探すときは Pattern.quote で囲む。置換後の文字列でも $ と \ は特別な意味を持ち、"$100" をそのまま渡すと IndexOutOfBoundsException(No group 1)で止まるので、Matcher.quoteReplacement でエスケープする。
数値を3桁区切りにするには
String.format の %,d と %,.2f は、渡したロケールの区切り文字で 3 桁ごとに区切る。ロケールを省略すると実行環境の既定ロケールが使われ、ドイツ語の環境では 1.234.567 になるので、出力先に合わせて明示する。NumberFormat.getInstance が返す書式は小数部を既定で 3 桁まで出し、桁数は setMaximumFractionDigits で変えられる。
整数の割り算で商と余りを求めるには
整数どうしの / は商を 0 の側へ切り捨て、% の余りは割られる数と同じ符号になる。Python と同じく商を小さい側へ切り下げたいときは、Math.floorDiv と Math.floorMod を使う。整数を 0 で割ると ArithmeticException が投げられるが、どちらかが double なら例外にはならず、17.0 / 0 は Infinity になる。
小数を指定桁で丸めるには
Math.round は long を返すので、100 倍して丸めてから 100.0 で割り戻すと小数に戻る。ちょうど 0.5 のときは +∞ の側へ寄せるため、Math.round(-2.5) は -2 になる。桁数と丸め方を指定したいときは BigDecimal の setScale を使うが、new BigDecimal(1.005) は 2 進数の誤差を含んだ 1.00499… から作られるので、BigDecimal.valueOf か文字列から作る。String.format はロケールを渡さないと実行環境によって小数点が , になるので、Locale を明示する。
環境変数を真偽値として読むには
Boolean.parseBoolean は例外を投げず、大文字小文字を無視した "true" 以外をすべて false にするため、yes や 1、タイプミスの ture が黙って false になる。自前で判定するときは、実行環境のロケールで結果が変わらないよう toLowerCase に Locale.ROOT を渡す。
パスからファイル名を取り出すには
getFileName は最後の要素を Path で返すので、文字列として使うときは toString を呼ぶ。ルートの / では null が返り、そのまま toString を呼ぶと NullPointerException になる。拡張子を除いた名前を返すメソッドは標準 API に無いため、最後のドットより前を切り出す。条件を dot > 0 にしておくと、.bashrc のようにドットで始まる名前が空文字にならない。
ファイルに書き込むには
Files.writeString は Java 11 以降で使え、オプションを渡さなければ UTF-8 で新規作成または上書きする。オプションを渡すとこの既定の動作は適用されないので、StandardOpenOption.APPEND だけを渡すと、ファイルが無いときに NoSuchFileException になる。追記では CREATE と APPEND を一緒に渡す。
HTTPでファイルをダウンロードするには
HttpClient は既定でリダイレクトを追わない(Redirect.NEVER)ので、followRedirects(HttpClient.Redirect.NORMAL) を指定しないと、GitHub の raw リンクのような 302 で止まって空のファイルができるだけになる。BodyHandlers.ofFile はステータスコードに関係なく本文を書き出すため、一時ファイルで受けてから 200 かどうかを確かめ、Files.move で本来の名前に置き換える。
正規表現で大文字小文字を区別せずに一致させるには
Pattern.compile の第 2 引数に Pattern.CASE_INSENSITIVE を渡すか、パターンの先頭に (?i) と書く。CASE_INSENSITIVE だけでは ASCII の範囲しか対象にならないため、非 ASCII の文字も無視したいときは Pattern.UNICODE_CASE を併せて指定する。
配列の最大値と最小値を求めるには
List なら Collections.max と Collections.min、int[] のようなプリミティブの配列なら Arrays.stream で IntStream にしてから max と min を呼ぶ。IntStream の max は空の可能性を表す OptionalInt を返すので、orElse で空のときの値を決めておく。Collections.max は空のリストを渡すと NoSuchElementException を投げる。
親ディレクトリのパスを取得するには
getParent は最後の要素を取り除いたパスを返すが、ディレクトリ部分が無い report.csv やルートでは null を返すため、戻り値にそのままメソッドを呼ぶと NullPointerException になる。相対パスでも親を確実に得たいときは、先に toAbsolutePath で絶対パスにしてから getParent を呼ぶ。
相対パスを絶対パスにするには
toAbsolutePath はカレントディレクトリを前に付けるだけで .. を残すので、normalize を続けて呼んで取り除く。シンボリックリンクまで解決した実体のパスは toRealPath で得られるが、こちらはファイルが実在しないと例外になる。
ファイルの拡張子を取り出すには
Java の標準 API には拡張子を返すメソッドが無いため、ファイル名の最後のドットで切り出す。ディレクトリ名にドットを含むパス(v1.2/README)を誤判定しないよう、先に getFileName でファイル名だけにしてから探す。
パスを連結するには
Path.of は可変長引数で要素をまとめてつなぎ、既存の Path に足していくときは resolve を使う。resolve は引数が絶対パスならそれをそのまま返すので、外から受け取った名前をつなぐときは、結果が基準ディレクトリの下に収まっているかを確かめる。
日付に日数を足すには
LocalDate は不変オブジェクトで、plusDays や minusDays は新しい LocalDate を返す。戻り値を受け取らずに d.plusDays(30); と書いても d は変わらないため、結果は必ず変数に代入する。plusMonths は存在しない日付を月末に丸める。
ファイルをコピーするには
Files.copy の既定は「宛先があれば例外」で、上書きしたいときだけ REPLACE_EXISTING を明示する設計になっている。更新時刻などの属性も引き継ぎたい場合は COPY_ATTRIBUTES を併せて渡す。
文字列を数値に変換するには
Integer.parseInt と Double.parseDouble が基本形で、変換できない文字列には NumberFormatException を投げる。parseInt は int を返し valueOf は Integer を返すという違いがあるため、null を扱いたいかどうかで選び分ける。基数を指定する第 2 引数もある。
2つのJSONをマージするには
Jackson の ObjectNode を使い、両方がオブジェクトのときだけ再帰して統合する。set はノードの参照をそのまま持たせるため、元の JSON を後から変更しても影響が出ないよう deepCopy を挟んでいる。
未実行
環境変数の一覧を取得するには
System.getenv() は変更できない Map<String, String> を返す。順序は不定なので TreeMap に移して名前順にし、絞り込みは keySet のストリームで行う。
正規表現に一致する部分をすべて取り出すには
Matcher は find を呼ぶたびに次の一致へ進む状態を持つので、while で回して 1 件ずつ取り出す。Java 9 以降は results で Stream<MatchResult> にでき、同じ処理を 1 行で書ける。
HTTPリクエストにタイムアウトを設定するには
接続までの上限は HttpClient.Builder の connectTimeout、応答を受け取るまでの上限は HttpRequest.Builder の timeout に分かれている。どちらも既定では設定されないため、明示しなければ待ち続ける。接続時の時間切れは HttpTimeoutException を継承した HttpConnectTimeoutException になるので、まとめて捕まえられる。
未実行
正規表現で部分文字列を取り出すには
Matcher は find か matches を呼んで一致を確定させてから group を呼ぶ。順番を逆にすると IllegalStateException になる。(?<name>...) で付けた名前は group(String) で引け、番号のずれに強くなる。
文字列が正規表現に一致するか調べるには
Matcher.matches は文字列全体の一致、find は部分一致を判定する。String.matches も全体一致なので、部分一致のつもりで使うと常に false になる。同じパターンを使い回すなら、Pattern.compile の結果を定数として保持するとコンパイルが 1 度で済む。
正規表現で置換するには
String.replaceAll は正規表現として解釈し、replaceFirst は最初の 1 件だけを置き換える。名前が似ている replace は正規表現ではなく文字列そのものを探すので、メタ文字をそのまま扱いたいときはこちらを使う。置換文字列の中では $1 で後方参照する。