Java 实现可导航图形的优雅方式?
这是一个设计问题。我正在努力为我面临的问题创建一个概念模型Java 实现可导航图形的优雅方式?,java,python,graph-databases,ognl,object-graph,Java,Python,Graph Databases,Ognl,Object Graph,这是一个设计问题。我正在努力为我面临的问题创建一个概念模型 我有一个许多对象的图表(这取决于你希望你的表现在哪里 如果您想要快速查询,并且不介意在添加对象时有一点额外的时间/内存,保留一个指向具有特定属性的对象的指针数组/列表可能是一个好主意(特别是如果您在设计时知道可能的属性)。然后,在添加新对象时,说: {name: A, attributes:{black, thin, invalid}, connections: {B,C}} 添加指向黑色列表、精简列表和无效列表的新指针。快速查询连
我有一个许多对象的图表(这取决于你希望你的表现在哪里 如果您想要快速查询,并且不介意在添加对象时有一点额外的时间/内存,保留一个指向具有特定属性的对象的指针数组/列表可能是一个好主意(特别是如果您在设计时知道可能的属性)。然后,在添加新对象时,说:
{name: A, attributes:{black, thin, invalid}, connections: {B,C}}
添加指向黑色
列表、精简
列表和无效
列表的新指针。快速查询连接可能需要保留一个指针列表/数组作为对象
类的成员。然后在创建对象时,为正确的对象添加指针
如果您不介意较慢的查询并且希望在添加对象时优化性能,则使用链表可能是一种更好的方法。您可以在所有对象之间循环,检查每个对象是否满足查询条件
在这种情况下,如果(正如您的问题所表明的)您希望执行多级查询(即a.connections[0]。connections[0]
,则保留连接的成员指针仍然是一个好主意。如果通过嵌套循环执行,将导致极低的性能。)
希望这会有所帮助,这实际上取决于您希望最频繁调用的查询类型。用Java表达这一点没有问题。只需定义表示节点集的类。假设有一组固定的属性,它可能会如下所示:
enum Attribute {
BLACK, WHITE, THIN, VALID /* etc. */ ;
}
class Node {
public final String name;
public final EnumSet<Attribute> attrs
= EnumSet.noneOf(Attribute.class);
public final NodeSet connections
= new NodeSet();
public Node(String name)
{
this.name = name;
}
// ... methods for adding attributes and connections
}
听起来你有一些对象模型,你想以不同的方式查询。一种解决方案是使用Java创建模型,然后使用脚本语言以不同的方式支持对该模型的查询。例如:我建议使用Java+Groovy 您可以为模型使用以下Java类
public class Node {
private String name;
private final Set<String> attributes = new HashSet<String>();
private final List<Node> connections = new ArrayList<Node>();
// getter / setter for all
}
公共类节点{
私有字符串名称;
私有最终集属性=新HashSet();
私有最终列表连接=新建ArrayList();
//所有人的接受者/接受者
}
然后,应使用正确填充的“connections”属性填充此类对象的列表
要支持不同类型的脚本,您需要做的是为脚本创建上下文,然后填充此上下文。上下文基本上是一个映射。映射的键成为脚本可用的变量。诀窍是填充此上下文以支持您的查询要求
例如,在groovy中,绑定是上下文(请参阅)。因此,如果您以以下方式填充它,您的查询需求将得到满足
上下文/绑定映射
1. <Node name(String), Node object instance(Node)>
2. <Attribute name(String), list of nodes having this attribute(List<Node>)>
1。
2.
当您计算一个名为“a.connections[0]”的脚本时,将在绑定中查找针对键“a”存储的对象。然后将访问返回的对象“connections”属性。因为这是一个列表,所以“[0]'在groovy中允许使用此语法。这将返回索引0处的对象。同样,为了支持查询要求,您需要填充上下文。我认为使用图形解决此问题是有意义的。您提到了使用图形数据库的可能性,我认为这将使您能够更好地关注您的问题一个简单的内存中的图形,如来自项目的,将是一个很好的开始 通过使用TinkerGraph,您可以访问名为(另请参阅)的查询语言这有助于回答您在文章中提出的问题。下面是中的一个Gremlin会话,它展示了如何构造您所展示的图,以及一些示例图遍历,这些示例图遍历可以得到您想要的答案……第一部分简单地构造了您的示例图:
gremlin> g = new TinkerGraph()
==>tinkergraph[vertices:0 edges:0]
gremlin> a = g.addVertex("A",['color':'black','width':'thin','status':'invalid'])
==>v[A]
gremlin> b = g.addVertex("B",['color':'white','width':'thin','status':'valid'])
==>v[B]
gremlin> c = g.addVertex("C",['color':'black','width':'thick','status':'invalid'])
==>v[C]
gremlin> a.addEdge('connection',b)
==>e[0][A-connection->B]
gremlin> a.addEdge('connection',c)
==>e[1][A-connection->C]
gremlin> b.addEdge('connection',a)
==>e[2][B-connection->A]
gremlin> c.addEdge('connection',a)
==>e[3][C-connection->A]
gremlin> c.addEdge('connection',b)
==>e[4][C-connection->B]
现在查询:
// black - yields [A,C]
gremlin> g.V.has('color','black')
==>v[A]
==>v[C]
// black.thick - yields C
gremlin> g.V.has('width','thick')
==>v[C]
// A.connections[0].connections[0] - yields A
gremlin> a.out.out[0]
==>v[A]
// black[0].connections[0] - yields B
gremlin> g.V.has('color','black')[0].out[0]
==>v[B]
如果您不熟悉堆栈,这种方法确实会引入一些学习曲线,但我认为您会发现,图形适合作为许多问题的解决方案,并且对TinkerPop堆栈有一些经验通常会对您遇到的其他场景有所帮助。是否有任何标准的、众所周知的数据结构可以组合要提供您需要的功能?@Thorbjørnravandersen,如果我知道我不会问您。:)我正在考虑类似JQuery的功能,它允许通过ID(名称)或属性导航DOM元素
$('forma[href~=“value”]”)
我觉得你应该写几个例子,详细说明你需要做的各种事情。这样做的原因是,在我看来,您尚未全面了解这项计划要解决的问题。
class NodeSet extends LinkedHashSet<Node> {
/**
* Filters out nodes with at least one of the attributes.
*/
public NodeSet with(Attribute... as) {
NodeSet out = new NodeSet();
for(Node n : this) {
for(a : as)
if (n.attrs.contains(a)) {
out.add(n);
break;
}
}
return out;
}
/**
* Returns all nodes connected to this set.
*/
public NodeSet connections() {
NodeSet out = new NodeSet();
for(Node n : this)
out.addAll(n.connections);
return out;
}
/**
* Returns the first node in the set.
*/
public Node first() {
return iterator().next();
}
}
all.with(BLACK).first().connections()
public class Node {
private String name;
private final Set<String> attributes = new HashSet<String>();
private final List<Node> connections = new ArrayList<Node>();
// getter / setter for all
}
1. <Node name(String), Node object instance(Node)>
2. <Attribute name(String), list of nodes having this attribute(List<Node>)>
gremlin> g = new TinkerGraph()
==>tinkergraph[vertices:0 edges:0]
gremlin> a = g.addVertex("A",['color':'black','width':'thin','status':'invalid'])
==>v[A]
gremlin> b = g.addVertex("B",['color':'white','width':'thin','status':'valid'])
==>v[B]
gremlin> c = g.addVertex("C",['color':'black','width':'thick','status':'invalid'])
==>v[C]
gremlin> a.addEdge('connection',b)
==>e[0][A-connection->B]
gremlin> a.addEdge('connection',c)
==>e[1][A-connection->C]
gremlin> b.addEdge('connection',a)
==>e[2][B-connection->A]
gremlin> c.addEdge('connection',a)
==>e[3][C-connection->A]
gremlin> c.addEdge('connection',b)
==>e[4][C-connection->B]
// black - yields [A,C]
gremlin> g.V.has('color','black')
==>v[A]
==>v[C]
// black.thick - yields C
gremlin> g.V.has('width','thick')
==>v[C]
// A.connections[0].connections[0] - yields A
gremlin> a.out.out[0]
==>v[A]
// black[0].connections[0] - yields B
gremlin> g.V.has('color','black')[0].out[0]
==>v[B]