Java中的多级地图
在Java中将值("o")保留在像这样的树结构中的最佳方法是什么:
What is the best way in Java to keep values ("o") in a tree structure like this:
obj1
/\
/ \
/ \
obj2 obj3
/\ /\
/ \ / \
/ \ / \
obj4 obj5 obj6 obj7
/\ /\ /\ /\
/ \ / \ / \ / \
o8 oN...
它看起来像一棵树,但是我不需要任意深度.我宁愿需要强大的数据类型和预定义的美观方法来处理最终结构.
It looks like a tree, but I don't need arbitrary depth. I rather need strong datatyping and predefined good looking methods for working with final structure.
我需要能够通过键获取某种值列表-就像在我的图片上一样.换句话说,结构应该不以任何方式变为平面.
I need to be able to get some kind of list of values by keys - exactly like on my picture. In other words, structure should not become planar in any way.
我需要 .get(obj3)
返回 {obj6,obj7},.get(obj1)-{obj2,obj3}
.
现在我为此使用Map,但是由于需要检查结构的每个级别,因此夸大此类地图是很丑的.看起来像这样(数据是地图):
For now I use Map for that, but inflating such map is ugly, because I need to check each level of the structure. Looks like that (data is the map):
if(data.get(somedouble) == null) {
Map<Integer, Data> inm = new TreeMap<>();
inm.put(someint, obj);
Map<Double, Map<Integer, Data>> m = new TreeMap<>();
m.put(somedouble2, inm);
data.put(somedouble, m);
}
else {
if(data.get(somedouble).get(somedouble2) == null) {
Map<Integer, Data> inm = new TreeMap<>();
inm.put(someint, obj);
data.get(somedouble).put(somedouble2, inm);
}
else
data.get(somedouble).get(somedouble2).put(someint, obj);
}
性能不是问题,但代码美观是关键.
Performance in not an issue, but code beauty is.
您可以使用您的特定密钥:
You can use your specific key:
class MyKey {
Double beta;
Double yaw;
int minute;
public int hashCode() {
/* Returns hash code from a combination of hash of the key members. */
}
@Override
public boolean equals(Object obj) {
/* Returns true if obj is a MyKey with same members. */
}
}
然后简单地:
data.put(myKey, obj);
这样,多级检查"全部隐藏在 MyKey.equals()
中.它可以使客户端代码保持整洁,并且关键的复杂性放在安全的位置.
This way the "multi-level checks" are all hidden in MyKey.equals()
. It keeps the client code clean and the key complexity is in a safe place.
如果最重要的是,您希望能够从双 beta
到您的对象建立一个映射,那么我仍然会像这样保持事物的平面.
If on top of this, you want to be able to have a map from your double beta
to your objects, then I would still keep the thing planar like that.
您真正想要的是为数据提供多个索引",例如在数据库中,因此您可以查询具有相同"beta"或"yaw"的对象.为此,最好的方法是使用多个Map(实际上是Multimap),每个索引"使用一个.
What you actually want is to have multiple "indexes" for your data, like in a database, so you can query for objects with same "beta" or "yaw". For that the best way is to use several Map (actually Multimap), one for each of your "indexes".
使用番石榴的Multimap:
ListMultimap<Double, Data> mapForBeta;
ListMultimap<Double, Data> mapForYaw;
您可以将所有多图和 Map< MyKey,Data>
放在特定的类中.实际上,最好的方法是继承 Map< MyKey,Data>
:
You can put all the multimap and the Map<MyKey, Data>
in your a specific class. Actually the best way would be to subclass Map<MyKey, Data>
:
public class MyMap extends HashMap<MyKey, Data> {
ListMultimap<Double, Data> mapForBeta;
ListMultimap<Double, Data> mapForYaw;
public Data put(MyKey key, Data value) {
super.put(key, value);
mapForBeta.add(key.beta, value);
mapForYaw.add(key.yaw, value);
};
public List<Data> getFromBeta(Double beta) {
return mapForBeta.get(beta);
}
public List<Data> getFromYaw(Double yaw) {
return mapForYaw.get(yaw);
}
}
具有更好解决方案的新
实际上,这让我开始思考,我意识到您的地图默认值确实存在问题,这就是为什么您的代码有点混乱的原因.
New Edit with better solution:
Actually, it got me thinking, and I realized that you are really having a problem with default values for your maps, and that's why your code is a bit messy.
您可以使用生成器来创建基础地图的默认地图来解决此问题:
You can solve this with a Default Map using a generator to create underlying maps:
public class DefaultMap<K, V> extends TreeMap<K, V> {
static abstract class Generator<V>{
abstract V create();
}
final Generator<V> generator;
DefaultMap(Generator<V> generator) {
this.generator = generator;
}
@Override
public V get(Object key) {
V val = super.get(key);
if (val == null) {
val = generator.create();
put((K)key, val);
}
return val;
}
}
现在,您可以使用实用程序树类来存储所有数据:
Now you can have your utility tree class to store all your data :
public class MyTree {
private final Map<Double, Map<Double, Map<Integer, Data>>> data;
public MyTree() {
data = new DefaultMap<>(new Generator<Map<Double, Map<Integer, Data>>>() {
@Override
Map<Double, Map<Integer, Data>> create() {
return new DefaultMap<>(new Generator<Map<Integer, Data>>() {
@Override
Map<Integer, Data> create() {
return new TreeMap<>();
}
});
}
});
}
void add(MyKey d, Data obj) {
data.get(d.beta).get(d.yaw).put(d.minute, obj);
}
}
现在,您可以使用data.get(beta).get(yaw)访问数据,并且没有意大利面条式代码来存储您的值.
Now you can access your data with data.get(beta).get(yaw) and you don't have spaghetti code to store your values.