Java | ネストforのリファクタリング(安全設計・例外・ログ対応)

Java Java
スポンサーリンク

では次に、
「ネスト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化(安全+簡潔)

ネスト構造を flatMapfilter で置き換えると、
**「見通し+保守性+安全性」**が大幅アップします。

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:例外階層・ログ設計のベストプラクティス

区分ログレベル例外種別対応方針
入力値エラーWARNValidationExceptionユーザー通知+スキップ
業務ロジックERRORBusinessException処理中断+アラート
システム障害FATALSystemException管理者通知+リトライ検討
想定外エラーERRORExceptionログ出力+継続処理

📘 まとめ

ステップ内容メリット
① try-catch導入安全性UP処理継続・障害特定
② メソッド分割可読性UP責務の明確化
③ Stream化保守性UPフラット構造・並列対応
④ テンプレート化再利用性UPコード統一
⑤ ログ・例外方針統一運用性UP監視・保守容易

Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました