I have a Location POJO that stores Location objects parsed in from a JSON file, which I want to map to a graph. Each node's location in the graph corresponds to it's id field, where id="1" is the start node and id="10" is the goal node.
To solve this I adapted a Node class to include methods such as setWeight()
, addChildLocation()
etc , But I'm not sure how to create the graph from my list of locations. I know how to create the graph by hard coding the location's and calling addChildren, by doing the following, but not sure how to create it from a list of already available Location objects:
Node LocationOne= new Node("LocationOne", 170);
LocationOne.addChildNode(LocationTwo, 105);
My thoughts on this problem, are that a for..each
loop should be used to loop through the locations in the list, and add each location as a child of the previous.
Basically, I'm wondering how the list of Location object's can be iterated through, and addChild be called on each sequential location?
Below is the Location class I'm using to map the Location's to object representations:
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)
+"]";
}
}
If you want to build a graph of any kind, then recognize That a graph is composed of on 2 basic elements:
- E -> edge
- V -> Vertex (A.K.A node)
Key properties:
- Vertices can have any number of edges.
- Edges only go between single nodes (in my implementation I modified this for book keeping sake).
Practically speaking, how you access these will depend on a trade off between functionality and performance. In addition, graph nodes are worthless if they don't contain some of your information. So, we add
Map<String,Object> properties
So we can store some data like:
new Vertext().set("age",10);
We also use named edges so you can do things like:
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));
I have put together the basic structure for how I would implement a generic graph. But some have pointed out that there are graph DB's out there like Neo which are very sophisticated.
Below is the code. Use it if you'd like to model your locations.
/**
* @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);
}
}
}
You might consider moving this question to codereview.stackexchange.com
Edited
Imagine you code like this:
- Location -> Vertex
- Location[] -> All connected Locations (Vertices) where i in location[i] is the Edge
Your code will be difficult to work with because you don't have declared edges. One of the properties of an edge is "weight" but here you have it as location's property.
As for implementing "visited" -- this is a call-state not a graph wide state. But, by making it part of the Location you now have a state management problem; Imagine trying to "find" something again? You would have to reset the 'visited' property on every node or throw away and create the whole graph again. Very inefficient and error prone.
if you implement DFS, which is really easy recursively or even with a tail loop, you pass the "visited" state along with the dfs method call. Example:
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;
}
Note that this next method is private.
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;
}
This is a rough outline. If you reach out to me in Java chat I will give more input and eventually something more slick and reusable. I make no claim the above DFS works. But it's close
I am not sure that I completely understand but do the names in the 'Exits' array become children nodes? Not sure what methods are on the Exit object or if this is even remotely the most efficient solution plus I can't check the syntax at the moment unfortunately.
The idea is that the method takes a node, finds the child node and then invokes the method on that node effectively recursively 'walking' down the hierarchy.
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);
}
}
}
}
来源:https://stackoverflow.com/questions/29403187/how-to-iterate-through-a-list-of-objects-and-assign-children