4

私たちのウェブアプリがUIからより限定的に実行できるものの一括操作バージョンを作成する必要があります。必要な操作は、オブジェクトをカテゴリに割り当てることです。カテゴリには複数のオブジェクトを含めることができますが、特定のオブジェクトは1つのカテゴリにのみ含めることができます。

タスクのワークフローは次のとおりです。

1)ブラウザを使用して、次の形式のファイルがアップロードされます。

# ObjectID, CategoryID
Oid1, Cid1
Oid2, Cid1
Oid3, Cid2
Oid4, Cid2
[etc.]

ファイルにはおそらく数十から数百行が含まれますが、間違いなく数千行になる可能性があります。

理想的な世界では、特定のオブジェクトIDはファイル内で1回だけ発生します(オブジェクトは1つのカテゴリにしか割り当てることができないという事実を反映しています)が、ファイルは制御外で作成されるため、実際に真であり、処理される保証はありません。その可能性に対処する必要があります。

2)サーバーはファイルを受信し、解析し、前処理して、次のようなページを表示します。

723 objects to be assigned to 126 categories
142 objects not found
 42 categories not found

Do you want to continue?

[Yes]     [No]

3)ユーザーがYesボタンをクリックすると、サーバーが実際に作業を行います。

手順(2)と(3)の両方でファイルを解析したくないので、(2)の一部として、リクエスト間で存続し、データの有用な表現を保持するコンテナを構築する必要があります。 「プレビュー」ページに入力するデータを簡単に提供して、実際の作業を効率的に実行できるようにします。(明らかにセッションはありますが、通常、メモリ内のセッション状態はほとんど保持されません。)

既存のものがあります

assignObjectsToCategory(Set<ObjectId> objectIds, CategoryId categoryId)

UIを介して割り当てが行われるときに使用される関数。単純な割り当てに加えて他の多くのビジネスロジックを実行し、この一括割り当てが実行されるときに同じビジネスロジックを実行する必要があるため、一括操作でもこのAPIを使用することが非常に望ましいです。

最初は、ファイルが特定のオブジェクトに対して「違法に」複数のカテゴリを指定した場合、ファイルが関連付けられたカテゴリの1つにオブジェクトを不意に割り当てても問題ありませんでした。

そのため、最初は、ステップ(2)でファイルを調べながら、ビルドしてクロスリクエストコンテナーに入れ Map<CategoryId, Set<ObjectId>>(具体的にはHashMap、すばやく検索して挿入するために)、作業を行うときだと思っていました。マップ上で繰り返すだけで、それぞれCategoryIdに関連付けられたものを引き出して、Set<ObjectId>に渡すことができますassignObjectsToCategory()

ただし、重複ObjectIdの処理方法に関する要件が変更されました。そして、それらは次のように処理されます。

  • がファイルObjectIdに複数回出現し、すべての時間が同じに関連付けられているCategoryId場合は、オブジェクトをそのカテゴリに割り当てます。
  • がファイルObjectIdに複数回表示され、異なるに関連付けられてCategoryIdいる場合は、エラーと見なし、「プレビュー」ページにそのことを記載してください。

これは、ファイルから読み取ったばかりのファイルがすでにに関連付けられていることMap<CategoryId, Set<ObjectId>>を検出するための適切な方法を提供しないため、私の戦略を台無しにしているようです。ObjectIdCategoryId

だから私の質問は、これらの重複を最も効率的に検出して追跡する方法ObjectIdですか?

頭に浮かんだのは、「順方向」と「逆方向」の両方のマップを使用することです。

public CrossRequestContainer
{
    ...

    Map<CategoryId, Set<ObjectId>> objectsByCategory;  // HashMap
    Map<ObjectId, List<CategoryId>> categoriesByObject; // HashMap
    Set<ObjectId> illegalDuplicates;

    ...
}

次に、各(ObjectId, CategoryId)ペアが読み込まれると、両方のマップに配置されます。ファイルが完全に読み込まれると、次のことができます。

for (Map.Entry<ObjectId, List<CategoryId>> entry : categoriesByObject.entrySet()) {
    List<CategoryId> categories = entry.getValue();
    if (categories.size() > 1) {
        ObjectId object = entry.getKey();
        if (!all_categories_are_equal(categories)) {
            illegalDuplicates.add(object);
            // Since this is an "illegal" duplicate I need to remove it
            // from every category that it appeared with in the file.
            for (CategoryId category : categories) {
                objectsByCategory.get(category).remove(object);
            }
        }
    }
}

このループが終了objectsByCategoryすると、「違法な」重複は含まれなくなり、illegalDuplicates必要に応じて報告されるすべての「違法な」重複が含まれます。次に、繰り返して、各カテゴリobjectsByCategoryのを取得し、呼び出して割り当てを行うことができます。Set<ObjectId>assignObjectsToCategory()

しかし、これはうまくいくと思いますが、特に入力ファイルが巨大な場合は、データを2回保存することを心配しています。また、効率性について何かが欠けているのではないかと心配しています。これは非常にゆっくりと進みます。

ダブルメモリを使用しないが、それでも迅速に実行できるこれを行う方法はありますか?ダブルメモリを使用しても、予想よりも実行速度が大幅に低下するものがありませんか?

4

1 に答える 1

1

あなたが与えた制約を考えると、はるかに少ないメモリを使用してこれを行う方法はありません。

ただし、考えられる最適化の1つは、複数のカテゴリにリストされているオブジェクトのカテゴリのリストのみを維持することです。それ以外の場合は、オブジェクトをカテゴリにマップします。

Map<CategoryId, Set<ObjectId>> objectsByCategory;  // HashMap
Map<ObjectId, CategoryId> categoryByObject; // HashMap
Map<ObjectId, Set<CategoryId>> illegalDuplicates;  // HashMap

はい、これによりさらに別のコンテナが追加されますが、(うまくいけば)いくつかのエントリのみが含まれます。また、categoryByObjectマップのメモリ要件が削減されます(エントリごとに1つのリストオーバーヘッドが削減されます)。

もちろん、ロジックはもう少し複雑です。重複が最初に検出された場合、オブジェクトはcategoryByObjectマップから削除され、illegalDuplicatesマップに追加される必要があります。カテゴリByObjectマップにオブジェクトを追加する前に、まずillegalDuplicatesマップを確認する必要があります。

最後に、他の2つのマップを作成した後、別のループでobjectsByCategoryマップを作成しても、パフォーマンスが低下することはおそらくなく、コードが少し単純化されます。

于 2011-04-28T03:00:21.230 に答える