我选择的数据结构设计证明执行起来非常尴尬,所以我不希望你的专家意见如何执行它,我希望你能为我想要的东西建议一个更自然的数据结构做,如下。我在读数据行。每列都是一个变量(Animal,Color,Crop,... - 其中有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,而是返回一个Collection,它会创建一组全新的问题。)请注意,我想对值进行排序,从最高到最低,但我认为我可以很容易地做到这一点。
如果您愿意,建议更好地选择数据结构设计?如果没有明显更好的设计选择,我如何使用.get()返回的Collection,当我想要的只是我放在那个插槽中的单个HashMap时?
非常感谢 - Ed
答案 0 :(得分:3)
您可以将Map<String, Integer>
替换为Multiset来消除一些奇怪现象。
A multiset (or a bag)是一个允许重复元素的集合 - 并对它们进行计数。你又扔了一个苹果,一个梨子和一个苹果。它记得它有两个苹果和一个梨。基本上,这是您在刚刚使用的Map<String, Integer>
下所想象的。
Multiset<String> eqCounts = HashMultiset.create();
相应的“.get()”不会返回HashMap,而是返回一个 集合
这是因为您使用了通用的“Multimap”界面。文档说:
但您很少直接使用Multimap界面;更经常的 您将使用
ListMultimap
或SetMultimap
,它们将键映射到List或a 分别设置。
所以,坚持你原来的设计:
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
是多余的,整个事情仍然可以改进(尽管它已经足够好了,我认为)。要进一步改进,您可以尝试以下方法:
Multiset
而不是HashMap
的数组。毕竟,你事先知道列数。List.
可能是最好的:像这样创建一个类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[]
标题和Map<String, Multiset<String>>
作为其值,而是使用Column[]
。您可以创建此数组来代替创建headers
数组。
答案 1 :(得分: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);
}
}
答案 2 :(得分:1)
1)多图中的地图通常称为基数图。为了从值集合中创建基数映射,我通常使用来自Apache Commons Collections的CollectionUtils.getCardinalityMap,虽然这不是一般化的,因此您需要一个不安全(但已知是安全的)强制转换。如果你想使用Guava构建地图我认为你应该首先将变量的值放在Set<String>
中(以获取唯一值的集合),然后对每个值使用Iterables.frequency()来获取计数。 (编辑:甚至更简单:使用ImmutableMultiset.copyOf(collection)
将基数地图设为Multiset
)无论如何,生成的基数地图是Map<String, Integer
,例如您已经在使用。
2)我不明白你为什么需要Multimap。毕竟你想将每个变量映射到基数图,所以我使用Map<String, Map<String, Integer>>
。
编辑:或者如果您决定使用Multiset作为基数地图,请使用Map<String, Multiset<String>>
。