では次に、
「ネストforのリファクタリング(安全設計・例外・ログ対応)」
を、実務レベルで分かりやすく整理します。
目的
ネストfor(forの入れ子)は便利ですが、
実務コードでは「例外」「ログ」「早期return」などを適切に組み合わせないと
障害の原因や性能問題になりやすいです。
以下の例では、
- ✅ ネストforのリファクタリング手順
- ✅ 安全な例外設計とログ方針
- ✅ Stream API化による改善例
を段階的に解説します。
⚠️ Before:ありがちなネストfor(危険版)
for (User user : userList) {
for (Order order : orderList) {
if (user.getId().equals(order.getUserId())) {
System.out.println(user.getName() + "の注文:" + order.getItem());
}
}
}
Java問題点:
| 問題 | 内容 |
|---|---|
| 💥 例外発生時にループが止まる | NullPointerExceptionなどで全処理中断 |
| 🌀 ログがない | どのユーザーで落ちたか不明 |
| ⏱️ パフォーマンス低下 | ネストループで O(n²) の計算量 |
| 👀 可読性が低い | ロジックが深く見づらい |
✅ Step1:安全化(例外+ログ対応)
まずは try-catch と SLF4J ロギングを導入します。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderReporter {
private static final Logger log = LoggerFactory.getLogger(OrderReporter.class);
public void printOrders(List<User> users, List<Order> orders) {
for (User user : users) {
for (Order order : orders) {
try {
if (user.getId().equals(order.getUserId())) {
log.info("{}の注文:{}", user.getName(), order.getItem());
}
} catch (Exception e) {
log.error("ユーザー{}の処理中にエラー: {}", user.getName(), e.getMessage(), e);
}
}
}
}
}
Java💡ポイント
| 対策 | 内容 |
|---|---|
| try-catchを内側に限定 | エラーが起きても他ユーザー処理を続行 |
| ログレベルを分ける | INFO:正常、ERROR:異常 |
| eを第3引数に渡す | スタックトレースも記録できる |
✅ Step2:ネストの分離(メソッド化)
ロジックを分けると見通しが良くなります。
private void processUser(User user, List<Order> orders) {
for (Order order : orders) {
if (user.getId().equals(order.getUserId())) {
log.info("{}の注文:{}", user.getName(), order.getItem());
}
}
}
public void printOrders(List<User> users, List<Order> orders) {
for (User user : users) {
try {
processUser(user, orders);
} catch (Exception e) {
log.error("ユーザー{}の処理失敗: {}", user.getName(), e.getMessage(), e);
}
}
}
Java🧩 改善効果
- ネストが浅くなる(for-for → for+メソッド)
- 例外処理の粒度を明確化
- ログメッセージの責務を分離
✅ Step3:Stream API化(安全+簡潔)
ネスト構造を flatMap や filter で置き換えると、
**「見通し+保守性+安全性」**が大幅アップします。
users.stream()
.flatMap(user -> orders.stream()
.filter(order -> user.getId().equals(order.getUserId()))
.map(order -> Map.entry(user, order))
)
.forEach(entry -> {
User user = entry.getKey();
Order order = entry.getValue();
try {
log.info("{}の注文:{}", user.getName(), order.getItem());
} catch (Exception e) {
log.error("ユーザー{}の処理中にエラー: {}", user.getName(), e.getMessage(), e);
}
});
Java💡Stream化のメリット
| 項目 | 内容 |
|---|---|
| ネスト消滅 | flatMap でフラットに処理 |
| ロギングが明確 | forEach 内で統一管理 |
| 並列化対応 | .parallelStream() に変更で高速化可 |
| エラー分離 | try-catchごとにログ出力可能 |
✅ Step4:安全設計テンプレート(再利用可)
実務で使いまわせる「安全ループ」テンプレートです。
/**
* 共通の安全な2重ループテンプレート
*/
public <A, B> void safeNestedLoop(
List<A> listA,
List<B> listB,
BiConsumer<A, B> processor,
String contextName) {
for (A a : listA) {
for (B b : listB) {
try {
processor.accept(a, b);
} catch (Exception e) {
log.warn("{}処理中にエラー: {}", contextName, e.getMessage(), e);
}
}
}
}
Java利用例:
safeNestedLoop(users, orders,
(user, order) -> {
if (user.getId().equals(order.getUserId())) {
log.info("{}の注文:{}", user.getName(), order.getItem());
}
},
"注文出力"
);
Java✅ Step5:例外階層・ログ設計のベストプラクティス
| 区分 | ログレベル | 例外種別 | 対応方針 |
|---|---|---|---|
| 入力値エラー | WARN | ValidationException | ユーザー通知+スキップ |
| 業務ロジック | ERROR | BusinessException | 処理中断+アラート |
| システム障害 | FATAL | SystemException | 管理者通知+リトライ検討 |
| 想定外エラー | ERROR | Exception | ログ出力+継続処理 |
📘 まとめ
| ステップ | 内容 | メリット |
|---|---|---|
| ① try-catch導入 | 安全性UP | 処理継続・障害特定 |
| ② メソッド分割 | 可読性UP | 責務の明確化 |
| ③ Stream化 | 保守性UP | フラット構造・並列対応 |
| ④ テンプレート化 | 再利用性UP | コード統一 |
| ⑤ ログ・例外方針統一 | 運用性UP | 監視・保守容易 |
