2015-04-02 125 views
1

我有一个位置POJO,它存储从JSON文件中解析出来的位置对象,我想将其映射到一个图形。图中每个节点的位置对应于它的id字段,其中id =“1”是开始节点,id =“10”是目标节点。如何迭代对象列表并分配子对象?

为了解决这个问题,我调整了一个Node类以包含诸如setWeight(),addChildLocation()等方法,但我不确定如何从我的位置列表创建图形。我知道如何通过硬编码位置的,并呼吁addChildren,通过执行以下操作来创建图形,但不知道如何从已有的位置对象的列表创建:

Node LocationOne= new Node("LocationOne", 170); 
    LocationOne.addChildNode(LocationTwo, 105); 

我对这个问题的看法,是否应该使用循环来遍历列表中的位置,并将每个位置添加为前一个的子项。

基本上,我想知道如何遍历位置对象的列表,并在每个顺序位置调用addChild?

下面是我使用的位置对对象表示映射位置类:

public class Location { 

    private Location[] location; 

    private int id; 

    private String description; 

    private String weight; 

    private String name; 

    private Exit[] exit; 

    private boolean visited = false; 
    private boolean goalLocation; 
    private int approximateDistanceFromGoal = 0; 
    private Location parent; 

    private Map<Location, Integer> children = new HashMap<Location, Integer>(); 




    public Location() { 
     super(); 
    } 

    public Location(String name){ 
     this.name = name; 
    } 

    public Location(String name, int goalDistance){ 
     this.name = name; 
     this.approximateDistanceFromGoal = goalDistance; 
    } 

    public Location[] children(){ 
     return (Location[]) children.keySet().toArray(new Location[children.size()]); 
    } 

    public int getDistance(Location loc){ 
     if(children.get(loc) == null) System.out.println(this.name + ": " + loc.getName()); 
     return children.get(loc); 
    } 


    public int getChildLocationCount(){ 
     return children.size(); 
    } 

    public void addChildLocation(Location child, int distance){ 
     children.put(child, distance); 
    } 

    public boolean isLeaf(){ 
     if (children.size() > 0){ 
      return false; 
     }else{ 
      return true; 
     } 
    } 


    public void removeChild(Location child){ 
     children.remove(child); 
    } 


    public Location[] getLocation() { 
     return location; 
    } 

    public void setLocation(Location[] location) { 
     this.location = location; 
    } 


    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getDescription() 
    { 
     return description; 
    } 

    public void setDescription (String description) 
    { 
     this.description = description; 
    } 


    public String getWeight() { 
     return weight; 
    } 

    public void setWeight(String weight) { 
     this.weight = weight; 
    } 

    public String getName() 
    { 
     return name; 
    } 

    public void setName (String name) 
    { 
     this.name = name; 
    } 

    public Exit[] getExit() { 
     return exit; 
    } 

    public void setExit(Exit[] exit) { 
     this.exit = exit; 
    } 


    public boolean isVisited() { 
     return visited; 
    } 

    public void setVisited(boolean visited) { 
     this.visited = visited; 
    } 

    public boolean isGoalLocation() { 
     return goalLocation; 
    } 

    public void setGoalLocation(boolean goalLocation) { 
     this.goalLocation = goalLocation; 
    } 

    public int getApproximateDistanceFromGoal() { 
     return approximateDistanceFromGoal; 
    } 

    public void setApproximateDistanceFromGoal(int approximateDistanceFromGoal) { 
     this.approximateDistanceFromGoal = approximateDistanceFromGoal; 
    } 

    public Location getParent() { 
     return parent; 
    } 

    public void setParent(Location parent) { 
     this.parent = parent; 
    } 

    @Override 
    public String toString() { 
     return "Location [location=" + Arrays.toString(location) + ", id=" + id 
       + ", description=" + description + ", weight=" + weight 
       + ", name=" + name + ", exit=" + Arrays.toString(exit) 
       +"]"; 
    } 


} 
+0

我会创建一个HashTable,键是位置和值是连接到键的所有位置的列表。 – Kunal 2015-04-02 00:58:38

+0

我不会构建自己管理节点的工具,而是我可能会使用图形数据库。结帐http://neo4j.com或http://thinkaurelius.github.io/titan/。 – 2015-04-02 01:03:41

+0

@Kinsley我已经在Location类中创建了一个映射,并且使用了一个方法addChild来获取位置名称和int ID,我的问题是如何将addChild调用到对象列表中的每个后续位置对象以创建图形。 – 2015-04-02 01:23:08

回答

3

如果你想建立任何形式的图形,然后认识到,图上2个基本要素组成:

  1. ë - >边缘
  2. V> - 顶点(AKA节点)

Key属性:

  • 顶点可以有任意数量的边。
  • 边缘只能在单个的 节点之间(在我的实现中,我修改了这个用于保留书籍)。

实际上,您如何访问这些将取决于功能和性能之间的权衡。另外,如果图表节点不包含您的某些信息,它们将毫无价值。因此,我们添加

Map<String,Object> properties 

因此,我们可以存储像一些数据:

new Vertext().set("age",10); 

我们也使用命名的边缘,所以你可以做这样的事情:

Graph g = new Graph(); 
g.addVertex("Doctor",new DrVertex("Dr. Smith")); 
g.addVertex("Doctor",new DrVertex("Dr. Cooper")); 
List<Vertex> doctors = g.get("Doctor"); 
assertTrue("Dr. Smith",doctors.get(0)); 
assertTrue("Dr. Cooper",doctors.get(1)); 

我有放在一起我将如何实现一个通用图的基本结构。但有些人指出,像Neo这样的图形数据库非常复杂。

以下是代码。如果你想模拟你的位置使用它。

/** 
* @author Christian Bongiorno 
*/ 
public class Graph { 

    private class Vertex { 
     Map<String,Object> properties; 
     private Map<String,Edge> edges; 

     public Graph addVertex(String edgeName, Vertex v) { 
      Edge e = edges.get(edgeName); 
      if(e == null) { 
       e = new Edge(this); 
       edges.put(edgeName,e); 
      } 

      e.addVertex(v); 
      return Graph.this; 
     } 

     public Graph addVertex(Vertex v) { 
      return addVertex("anonymous",v); 
     } 

    } 

    private static class Edge { 
     Map<String,Object> properties; 
     Vertex in; 
     Collection<Vertex> out; 

     private Edge(Vertex in) { 
      this.in = in; 
     } 


     private void addVertex(Vertex v) { 
      out.add(v); 
     } 
    } 

} 

你可能会考虑将这一问题codereview.stackexchange.com

编辑

想象一下,你这样的代码:

  • 位置 - >顶点
  • 位置[] - >全部连接ed位置(顶点)其中我在位置[i]是边缘

由于您没有声明边缘,您的代码将很难处理。边缘的一个属性是“重量”,但在这里您将它作为位置的属性。

至于实现“拜访” - 这是一个调用状态不是图宽状态。但是,通过使其成为地点的一部分,您现在有一个国家管理问题;想象一下,试图再次“找到”什么?您必须重置每个节点上的“visited”属性或丢弃并重新创建整个图。非常低效且容易出错。

如果你实现DFS,这很容易递归或者甚至是一个尾循环,你可以通过dfs方法调用传递“visited”状态。例如:

public Location find(Integer locationId) { 
    Location result = null; 
    for(Location l : locations) { 
     // this hashset represents the visited state which only matters for this method call 
     result = dfs(new HashSet(),l,locationId); 
     if(result != null) 
      break; 
    } 
    return result; 
} 

注意,这下一个方法是私人

private Location dfs(Set<Location> visitedAlready,Location current, Integer id){ 
    if(current.id == id) 
     return current; 
    visitedAlready.add(current); // instead of your boolean 
    Location result = null; 
    for(Location l : current.locations) { 
     result = dfs(visitedAlready,l,id); 
     if(result != null) 
     break; 
    } 
    return result; 
} 

这是一个粗略的提纲。如果您通过Java chat与我联系,我会给予更多的意见,并最终做出更加流畅和可重复使用的内容。我没有声明上述DFS的作品。但它很接近

+0

我尝试了第二种解决方案,实现了通过ID查找的方法,但是我在每个循环中获取了一个空指针,当它遍历位置时,任何想法为什么?因为在调用'find'之前打印内容时,位置显示为已填充。这是使用两种方法更新的Location类,http://hastebin.com/aricokicov.java – 2015-04-07 14:10:07

+0

从这里开始:https://coderpad.io/6JQZ9QXX然后我可以看到你在做什么。你没有驱动程序代码,所以我不知道你是如何执行这个代码,所以我添加了一个main。至于你的NPE:只有一个解释:你还没有初始化位置。我会聊天闲逛。如果你找到我,我们可以谈更多。此代码中的任何地方都有问题。我会引导你,但我们需要更紧密的互动。 – 2015-04-08 20:51:07

+0

所以我进一步调试了这两个方法,发现dfs()中的for循环导致了空指针。为了证明这一点,我给find()一个参数'1',即第一个返回正确位置的位置。但是,如果我给find()一个参数'2'例如我得到一个NPE,任何想法为什么这是?我在考虑它在dfs()方法逻辑中的缺陷? – 2015-04-09 15:10:07

1

我不知道,我完全理解,但做“退出”数组中的名称将成为子节点?不确定Exit对象上有什么方法,或者如果这是远程最有效的解决方案,那么我不能在不幸的时候检查语法。

这个想法是,该方法需要一个节点,找到子节点,然后在该节点上有效递归地“向下”层次上调用该方法。

ArrarList<Location> allNodes 

// Create children for the specified node 
public void createChildNodes(node n){ 
    // Fetch all available exits 
    for(Exit e : n.getExit()){ 
     // Iterate through all possible nodes 
     for(tempNode : allNodes) { 
      // Check if the 'exit' node matches one of the available nodes 
      if(tempNode.getName().equals(e.getName()){ 
       // Match found, add it 
       n.addChildNode(tempNode, tempNode.getDistance)); 

       // Check for children nodes of the temp node 
       createChildNodes(tempNode); 
      } 
     } 
    } 
} 
+0

我在想层次而不是图。 – Kazagha 2015-04-02 03:08:15

+0

只要每个位置知道应该附加哪些孩子来替换关于退出的代码,就可以以类似的方式对位置进行同样的处理。 – Kazagha 2015-04-03 07:28:31

+0

我不确定重量,但它在创建时/之后设置在每个节点上,不应将该信息存储在“位置”对象内。 – Kazagha 2015-04-03 07:32:58