3

私が選んだデータ構造設計は、実行するのが非常に厄介であることがわかっているので、それを実行する方法について専門家の意見を求めるのではなく、私がやろうとしていることに対してより自然なデータ構造を提案してくれることを願っています.以下のとおりであります。私はデータの行を読んでいます。各列は単一の変数です (動物、色、作物など - 45 個あります)。データの各行には、その列の変数の値があります。値や行数は事前にはわかりません。

Animal  Color   Crop    ...
-------------------------------------
cat     red     oat
cat     blue        hay
dog     blue        oat
bat     blue        corn
cat     red     corn
dog     gray        corn
...     ...     ...

読み終わったら、次のように、各変数、変数が取った各値、およびその変数がその値を取った回数をキャプチャする必要があります。

Animal [cat, 3][dog,2][bat, 1]...
Color [blue, 3][red,2][gray,1]...
Crop [corn,3][oat, 2][hay,1]...
...

私はいくつかのアプローチを試しましたが、私が得た最も近いものは、ハッシュマップのGUAVAマルチマップを使用することです:

Map<String, Integer> eqCnts = new HashMap<String, Integer>();
Multimap<String, Map> ed3Dcnt = HashMultimap.create();
for (int i = 0; i + 1 < header.length; i++) {
    System.out.format("Got a variable of %s\n", tmpStrKey = header[i]);
    ed3Dcnt.put(tmpStrKey, new HashMap<String, Integer>());
 }

必要なものを正確に作成したようですが、作業が非常に厄介で退屈であり、不思議な方法で動作します (たとえば、「ed3Dcnt.put()」が HashMap を挿入したにもかかわらず、対応する ".get()" は HashMap を返すのではなく、まったく新しい一連の問題を作成するコレクションを返します)。私はそれを十分に簡単に行うことができます。

よろしければ、データ構造設計のより良い選択についての提案はありますか? 明らかに優れた設計上の選択肢がない場合、.get() が返す Collection をどのように使用すればよいでしょうか? そのスロットに入れる単一の HashMap だけが必要な場合は?

どうもありがとう - エド

4

3 に答える 3

3

Map<String, Integer>Multisetに置き換えることで、奇妙さの一部を取り除くことができます。

マルチセット (またはバッグ)は、重複する要素を許可し、それらをカウントするセットです。りんご、梨、りんごをもう一度投げます。リンゴが 2 つとナシが 1 つあることを覚えています。基本的に、それはMap<String, Integer>あなたが今使った の下であなたが想像するものです。

Multiset<String> eqCounts = HashMultiset.create();

対応する「.get()」は HashMap ではなくコレクションを返します。

これは、汎用の「Multimap」インターフェイスを使用したためです。ドキュメントは言う:

ただし、Multimap インターフェースを直接使用することはめったにありません。ListMultimapまたはを使用することが多くSetMultimap、キーをそれぞれ List または Set にマップします。


したがって、元のデザインに固執するには:

  • 各列はMultiset<String>、値を保存してカウントする になります。
  • 次のように列を配置するMap<String, Multiset<String>>(キーはヘッダー、値は列) があります。

    Map<String, Multiset<String>> columns = Maps.newHashMap();
    for (int i = 0; i < headers.length; i++) {
        System.out.format("Got a variable of %s\n", headers[i]);
        columns.put(headers[i], HashMultiset.<String>create());
    }
    

行を読み取り、値が属する場所に値を入力します。

String[] values = line.split(" ");
for (int i = 0; i < headers.length; i++) {
    columns.get(headers[i]).add(values[i]);
}

そうは言っても、外側HashMapは冗長であり、全体がまだ改善される可能性があることがわかります (それでも十分だと思いますが)。さらに改善するには、次のことを試すことができます。

  1. Multisetの代わりに の配列を使用しHashMapます。結局、列の数は事前にわかっています。
  2. ジェネリック配列の作成に不安がある場合は、List.
  3. そしておそらく最高です: 次のColumnようなクラスを作成します:

    private static class Column {
        private final String header;
        private final Multiset<String> values;
    
        private Column(String header) {
            this.header = header;
            this.values = HashMultiset.create();
        }
    }
    

    String[]また、ヘッダーに aMap<String, Multiset<String>>を使用し、その値にa を使用する代わりに、 a を使用しColumn[]ます。配列を作成する代わりに、この配列を作成できますheaders

于 2013-06-16T09:49:22.237 に答える
1

最適なのは次のとおりです。

HashMap<String, HashMap<String, Integer>> map= new HashMap<String, HashMap<String, Integer>>();

ここで、ヘッダーの内部マップを追加するには:

for (int i = 0; i + 1 < header.length; i++) {
    System.out.format("Got a variable of %s\n", tmpStrKey = header[i]);
    map.put(tmpStrKey, new HashMap<String, Integer>());
}

内部マップの値をインクリメントするには、次のようにします。

//we are in some for loop
for ( ... ) {
    String columnKey = "animal"; //lets say we are here in the for loop
    for ( ... ) {
        String columnValue = "cat"; //assume we are here
        HashMap<String, Integer> innerMap = map.get(columnKey);

        //increment occurence
        Integer count = innerMap.get(columnValue);
        if (count == null) {
            count = 0;
        }
        innerMap.put(columnValue, ++count);
    }
}
于 2013-06-16T09:45:42.173 に答える