deep copying a graph structure - java

I have a graph class with Node's, where each Node can connect to others:
public class Node {
List<Node> connections;
}
I would like to make a deep copy of the entire graph. As a first attempt, I tried making a copy constructor like:
public Node(Node other) {
connections = new ArrayList<Node>();
for (Node n : other.connections) {
connections.add(new Node(n));
}
}
So deep copying a graph would just be:
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
for (Node n : nodes) {
g.nodes.add(new Node(n));
}
}
But that doesn't work as that destroys the connection relationship among the nodes. I am wondering if anyone has suggestions to do this in a simple way? Thanks.

The problem is that you need to copy the identities of the nodes, not just their values. Specifically, when you're copying some node, you need to deal with the identities of the nodes it refers to; that means that a copy constructor, or some other kind of purely local copying mechanism, can't do the job, because it only deals with one node at a time. I'm not sure that makes any sense, but I've typed it and my backspace key doesn't work.
Anyway, what you can do is pass around some other object which can tell which new node corresponds to which old node. If you wanted to be fancy (and who doesn't?) you could refer to this as a graph isomorphism. This can be something as simple as a map. As in this completely untested code:
// in Graph
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
Map<Node, Node> isomorphism = new IdentityHashMap<Node, Node>();
for (Node n : nodes) {
g.nodes.add(n.deepCopy(isomorphism));
}
return g;
}
// in Node
public Node deepCopy(Map<Node, Node> isomorphism) {
Node copy = isomorphism.get(this);
if (copy == null) {
copy = new Node();
isomorphism.put(this, copy);
for (Node connection: connections) {
copy.connections.add(connection.deepCopy(isomorphism));
}
}
return copy;
}
Sergii mentions using serialization; serialization actually does something pretty similar when it traverses an object graph.

Yep, deep copy in java ( not only in java) can be made using memory serialization/deserialization
like this
public static Object copy(Object orig) {
Object obj = null;
try {
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}

Kinda late input. But I had a similar problem but came to a different solution. But not shure if its bulletproof. So please feel free to comment so I can learn!
I have a Type called "Numbers" because I have no creativity naming stuff.
Each object of type "Numbers" has an internal list that can carry additional objects of type "Numbers" of which each has a list of additional "Numbers" of which each... and so on.
Basicaly you can make a tree structure similar to this:
I solved the deep copy problem by using a recursive copy-constructor inside the "Numbers" class.
Numbers class:
import java.util.ArrayList;
public class Numbers {
private ArrayList<Numbers> numbers = new ArrayList<>();
private int number;
public Numbers(int number) {
this.number = number;
}
public Numbers(Numbers numToCopy) {
this.number = numToCopy.getNumber();
ArrayList<Numbers> list = numToCopy.getNumbers();
for(int i = 0; i < list.size(); i++) {
Numbers n = new Numbers(list.get(i));
numbers.add(n);
}
}
public void addNumber(Numbers i) {
numbers.add(i);
}
public ArrayList<Numbers> getNumbers() {
return numbers;
}
public void setNumber(int i) {
this.number = i;
}
public int getNumber() {
return number;
}
public ArrayList<Numbers> getAllNumbers(ArrayList<Numbers> list) {
int size = numbers.size();
list.addAll(numbers);
for(int i = 0; i < size; i++) {
numbers.get(i).getAllNumbers(list);
}
return list;
}
}
Usage:
import java.util.ArrayList;
public class NumbersTest {
public NumbersTest() {
}
public static void main(String[] args) {
Numbers num0 = new Numbers(0);
Numbers num1 = new Numbers(1);
Numbers num2 = new Numbers(2);
Numbers num3 = new Numbers(3);
Numbers num4 = new Numbers(4);
Numbers num5 = new Numbers(5);
Numbers num6 = new Numbers(6);
num0.addNumber(num1);
num0.addNumber(num2);
num1.addNumber(num3);
num1.addNumber(num4);
num2.addNumber(num5);
num2.addNumber(num6);
num4.addNumber(num6);
//Deep copy here!
Numbers numCopy = new Numbers(num0);
//Change deep down in graph of original
num0.getNumbers().get(0).getNumbers().get(1).getNumbers().get(0).setNumber(799);
//Printout of copy to show it was NOT affected by change in original.
for(Numbers n : numCopy.getAllNumbers(new ArrayList<Numbers>())) {
System.out.println(n.getNumber());
}
}
}
Usage code shows that changing deep inside the "graph" of the original num0 object, does not change the copy made of it.
Theres two sixes (6) in the graph, and thats ok since they are on different branches.
Downside is if same number would repeat through one of the paths, like if there was a (1) somewhere under the first 1. It would then end up in an infinite loop.
Please do comment! :)

Related

Bfs game Map implementation in java

I'm trying to implement a bfs algorithm in Java,but it doesn't work as it should be.
I've made a game map comprised of HexTile objects(custom objects,similar to matrix elements). Each HexTile includes one adjacency list containing references to the elements that it's connected to, one function that returns those elements and one function that computes the distance between two HexTiles. The bfs algorithm is excecuted in another class called unit(units are placed in HexTiles) and finds every unit available in a given range from the room(currentTile). It then creates an ArrayList with the given units.
class HexTile {
static final int MAX_NEIGHBOURS = 6;
private HexTile[] neighbours;
public HexTile[] getNeighbours() {
return this.neighbours;
}
public double distanceFromTarget(HexTile target) {
double distance = Math.sqrt(Math.pow((this.getRow() - target.getRow()), 2) + Math.pow((this.getCol() - target.getCol()), 2));
return distance;
}
}
class Unit {
private ArrayList<Unit> unitsWithinRange = new ArrayList<Unit>();
private void findUnitsWithinRange(HexTile currentTile, int attackRange) {
Queue<HexTile> queue = new LinkedList<>();
ArrayList<HexTile> visited = new ArrayList<HexTile>();
queue.add(currentTile);
visited.add(currentTile);
while (!queue.isEmpty()) {
HexTile aux = queue.poll();
for (HexTile auxNeigh : aux.getNeighbours()) {
if (auxNeigh != null && (!visited.contains(auxNeigh))) {
visited.add(auxNeigh);
queue.add(auxNeigh);
}
}
if (aux != null && (currentTile.distanceFromTarget(aux) <= attackRange)) {
Unit auxUnit = aux.getUnitOnTile();
this.unitsWithinRange.add(auxUnit);
}
}
queue.clear();
visited.clear();
}
}
What happens whenever findUnitsWithinRange is excecuted is that it return a list of units,but the units that are in range 1 are not included(direct neighbours to root).Sometimes the program crashes,because units need to be able to know if there are any nearby units,to excecute some other functions.Any advice would be appreciated!

Why does the last 2 elements gets repeated in the following implementation of double-ended list?

This is the Main class with main method for generating a double-ended list, remove and display its elements.
public class Main {
Link first, last;
public static void main(String args[]) {
Main ob = new Main();
Link arr[] = {
new Link(1), new Link(2), new Link(3)
};
int len = 3;
for(int i=0;i<len;i++)
ob.insertFirst(arr[i]);
System.out.print("Data in the list: ");
while(ob.first!=null)
System.out.print(ob.removeAndReturn()+", ");
for(int i=0;i<len;i++)
ob.insertLast(arr[i]);
System.out.print("\nData in the list: ");
while(ob.first!=null)
System.out.print(ob.removeAndReturn()+", ");
}
void insertFirst(Link arg) {
if(isEmpty())
last = arg;
arg.next = first;
first = arg;
}
// This removeAndReturn() method returns the Object data the link is holding and removes that Link from the list
Object removeAndReturn() {
Object ret = null;
try {
ret = first.data;
if(first.next==null)
last = null;
first = first.next;
}catch(NullPointerException NPe) {
System.out.println("You are referring to a null.\nLinked List is empty.");
}
return ret;
}
void insertLast(Link arg) {
if(isEmpty())
first = arg;
else
last.next = arg;
last = arg;
}
boolean isEmpty() {
return first==null;
}
}
class Link {
Object data;
Link next;
Link(Object data) {
this.data = data;
}
}
When executing, it gives the following output:
Data in the list: 3, 2, 1,
Data in the list: 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, ... {truncated}
Here the last two elements gets repeated in the output. I tried nullifying both the Link variables first and last before calling ob.insertLast(arr[i]) but it gives the same output.
Update:
private keywords are removed from the complete method signature for methods in the Main class other than main(String args[]) method and rmF() method is changed to removeAndReturn().
The main problem in your code lies in the fact that you're using the exact same nodes (the ones within the arr) to fill your list with head and tail insertions.
In fact, once you perform your first head insertion, those nodes have been linked to each other like this:
(3) => (2) => (1) => null
So, when you're performing your second insertion, you have that node 1 points to node 2, node 2 points to node 3, and theoretically node 3 should point to null since it's supposed to be the last element. However, node 3's next field is still pointing to node 2 from the previous insertion. This creates a loop where node 2 and node 3 keep pointing at each other; thus yielding the infinite loop you're experiencing.
(1) => (2) => <= (3)
To fix your problem you could either reset the next field of your nodes before re-using them (poor solution) or work with the actual data you need to structure rather than the nodes. In fact, the user of your class shouldn't be bothered with the details of your implementation and should only care about the info to be stored/represented (in your case int numbers).
This is a possible solution to your problem:
public class Main {
Link first, last;
public static void main(String args[]) {
Main ob = new Main();
//Array of int not of links
int[] arr = {1, 2, 3};
int len = 3;
for (int i = 0; i < len; i++)
ob.insertFirst(arr[i]);
System.out.print("Data in the list: ");
while (ob.first != null)
System.out.print(ob.removeAndReturn() + ", ");
for (int i = 0; i < len; i++)
ob.insertLast(arr[i]);
System.out.print("\nData in the list: ");
while (ob.first != null)
System.out.print(ob.removeAndReturn() + ", ");
}
//------ CORRECTION ------
//The method should receive the info the user needs to store.
//It will then be up to you to represent it as a Link or whatever
//internal structure you're going to use tomorrow. Don't bind the
//user to your internal implementation.
//------------------------
void insertFirst(int arg) {
//Generating a new node (or link) based on the given info
Link l = new Link(arg);
if (isEmpty())
last = l;
l.next = first;
first = l;
}
// This removeAndReturn() method returns the Object data the link is holding and removes that Link from the list
Object removeAndReturn() {
Object ret = null;
try {
ret = first.data;
if (first.next == null)
last = null;
first = first.next;
} catch (NullPointerException NPe) {
System.out.println("You are referring to a null.\nLinked List is empty.");
}
return ret;
}
//-------- CORRECTION --------
//same explanation given above
//----------------------------
void insertLast(int arg) {
//Generating a new node (or link) based on the given info
Link l = new Link(arg);
if (isEmpty())
first = l;
else
last.next = l;
last = l;
}
boolean isEmpty() {
return first == null;
}
}
Lastly, Do not capture RuntimeException. These are unchecked exceptions, not checked. You should investigate on their origin rather than simply catching them. What you've written is a bad practice.
https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
As #JayC667 has already said, you could improve some designing and naming of your class, methods and variables. There are some conventions, especially when talking about data structures. For example:
Your class is called Main but it describes a List, a name like MyList would have been better.
Your utility class, Link, could have been placed within MyList as a static nested class and probably named Node (it's a better fit).
Some of your methods' names were a bit too cryptic. Self-explanatory names will better help the users of your class.
Avoid accessing the internal state of another object from outside (list.first != null). Methods should be your way to go to interrogate an object's state.
Using generic types could have been a better implementation than just Object as generics provide: strict checks at compile time, avoid casting a more type safety, the ability to re-use the same code for different data types.
https://docs.oracle.com/javase/tutorial/java/generics/why.html
Here is a link to an implementation with the suggestions made above:
https://www.jdoodle.com/iembed/v0/s7C

Create an Open Hashtable

I'm completely new and right now I'm writing a bid of code for university. I want to create an open hashtable and I wrote this peace of code:
public class AuDOpenHashTable extends AuDHashTable {
private LinkedList<Contact>[] table;
public AuDOpenHashTable(int capacity) {
super(capacity);
this.table = new LinkedList[capacity];
}
#Override
public void insert(Contact c) {
int position = hash(c.email);
if (table[position] == null) {
table[position] = new LinkedList<>();
}
table[position].add(c);
}
#Override
public void remove(Contact c) throws NoSuchElementException{
int position = hash(c.email);
if(table[position] != null){
table[position].remove();
}
else{
throw new NoSuchElementException();
}
}
#Override
public Contact getContact(String email)throws NoSuchElementException{
int position = hash(email);
table[position].getContact(email);
if(table[position] != null){
return table[position].get(position);
}
else{
throw new NoSuchElementException();
}
}
}
public abstract class AuDHashTable {
protected int capacity;
public AuDHashTable(int capacity){
this.capacity = capacity;
}
public abstract void insert(Contact c);
public abstract void remove(Contact c);
public abstract Contact getContact(String email);
protected int hash(String s){
int hash = 0;
for(int i = 0; i < s.length(); i++){
hash += s.charAt(i);
}
hash = hash % capacity;
return hash;
}
public static void main(String[] args) {
AuDClosedHashTable hashtabelle = new AuDClosedHashTable(3);
Contact eins = new Contact("hans.peter#web.de");
Contact zwei = new Contact("selina.meier#gmail.com");
Contact drei = new Contact("alexander.bauer#gmx.de");
hashtabelle.insert(eins);
hashtabelle.insert(zwei);
hashtabelle.insert(drei);
System.out.println(hashtabelle.isFull());
System.out.println(hashtabelle.getIndexOf("hans.peter#web.de"));
System.out.println(hashtabelle.getIndexOf("selina.meier#gmail.com"));
System.out.println(hashtabelle.getIndexOf("alexander.bauer#gmx.de"));
hashtabelle.remove(drei);
System.out.println(hashtabelle.isFull());
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
System.out.println(hashtabelle.getContact("hans.peter#web.de"));
System.out.println(hashtabelle.getContact("alexander.bauer#gmx.de"));
AuDOpenHashTable hashtabelle = new AuDOpenHashTable(3);
Contact eins = new Contact("hans.peter#web.de");
Contact zwei = new Contact("selina.meier#gmail.com");
Contact drei = new Contact("alexander.bauer#gmx.de");
hashtabelle.insert(eins);
hashtabelle.insert(zwei);
hashtabelle.insert(drei);
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
hashtabelle.remove(zwei);
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
}
}
So, my problem is in the "getContact()" method. If i want to display an account on a certain position and it is the ONLY account on that position, then everything works fine. But, if want to display an account in which the head differs the tail, so there are two accounts, it only give me one account(mostly not the correct one). For these examples the code works very well, but if i decide to pick other names, sometimes it does also not work. But not to make it complicated I wanted to hear your suggestions on how I can improve the "getContact" method. Thanks in prehand.
The hash function will tell you which bucket an item can be in, but you still need to check all the items within the bucket for equality. getContact should iterate over the LinkedList and check the email against each contact, then only return the contact with the matching email. Same for the remove method.
Different keys can have the same hash code. This is detected at insertion usually in which case there's usually a rehash, some algorithm to produce another hash code, that results in another possibly free has code. If not free it is again rehashed. If this continues a lot then the table was possibly allocated to small and a bigger table should be used.
When retrieving the information, you should compare the data at the index with the key searched. If not matching, rehash ( same algorith as insert ) and try again. Until you find it or end up in an empty index, in which case the key was not there.

Backtracking using loops in java?

Here's the gist of my code and it's function. It's a pick-where-you-go game to choose your path. For example, if you choose path a at the start, you get to choose between path d and e, and if you chose d you can move to f and g and so on.
I want to add backtracking. For instance, if I choose a in the beginning and go all the way to f, I want to be able to go back to d and have the choice between f and g again, or go all the way back to the starting point and choose b.
My initial thought was to use something to tell the code to go back to a certain line of code when I need to backtrack, but there's no goto in java to my understanding. I have an inkling to use loops. (I'm thinking while loops in particular.) I cannot figure out HOW to structure the loops to backtrack.
Here's my code:
public class PathGame {
public static void main (String[] args) {
String name = JOptionPane.showInputDialog("Hello! Welcome to my paths! What is your name, adventurer?");
JOptionPane.showMessageDialog(null, "Well then " + name + ", here's how this works...some generic instructions");
String startingChoice = JOptionPane.showInputDialog("Choose your path, a, b, or c.");
if (startingChoice.equals("a")){
String aChoice = JOptionPane.showInputDialog("Choose path d or path e");
if (aChoice.equals("d")) {
String dExamineChoice = JOptionPane.showInputDialog("path f or g?");
if (dExamineChoice.equals("f")) {
JOptionPane.showMessageDialog(null, name + "...!");
}
else if (dExamineChoice.equals("g")) {
JOptionPane.showMessageDialog(null, "Stuff g");
}
}
else if (aChoice.equals("e")) {
JOptionPane.showMessageDialog(null, "Stuff e");
}
else if (aChoice.equals("goBack")) {
///Backtrack back to start
}
}
else if (startingChoice.equals("b")) {
String bChoice = JOptionPane.showInputDialog("Path h or i?");
if (bChoice.equals("h")) {
String hChoice = JOptionPane.showInputDialog("Path j, k, or l?");
if (hChoice.equals("j")) {
String jExamine = JOptionPane.showInputDialog("m or n?");
if (jExamine.equals("m")) {
JOptionPane.showMessageDialog(null,"Stuff m");
}
else if (jExamine.equals("n")) {
JOptionPane.showMessageDialog(null,"Stuff n");
}
}
else if (hChoice.equals("k")) {
JOptionPane.showMessageDialog(null,"Stuff k");
}
else if (hChoice.equals("l")) {
JOptionPane.showMessageDialog(null,"Stuff l");
}
}
else if (bChoice.equals("i")) {
JOptionPane.showMessageDialog(null,"Stuff i");
}
}
}
}
Backtracking can be achieved with recursion. However, since you wanted the iterative approach. You can apply the same concept using a stack. Every time you visit a new square, push the current state into the stack. When you need to backtrack (for example you are in a dead end), pop out from the stack.
If your intention is to create something like a maze runner, you may want to record the visited squares.
And yes, you should be using a while-loop to do that.
Make an option back for each starting choice, such that when the user selects that option startingChoice is set to the letter you want to go back to.
There are two ways you could do this.
The extendable way would be to use a graph data structure. You could use something like JGraphT or TinkerPop. But that's assuming you want something really fancy. A graph would let you treat the whole path selection (traversal) very generically. It would let you design all sorts of paths and backtracking would simply be keeping track of where you came from.
The faster way would be to use a Stack data structure. Everytime you make a choice add that choice to your stack. So your current position is always stored at the top of the stack. When you backtrack just pop off the top of the stack and try again. For example:
public static void main(String []){
Stack<String> myPath = new Stack<>();
while(detinationNotReached){
myPath = goSomewhere(myPath);
}
}
public Stack goSomewhere(Stack<String> myPath){
String currentPosition = myPath.peek();
String choice = getChoice(currentPosition);
switch(choice){
case "a":
myPath.push("a");
break;
... //Other choices
case "back":
myPath.pop(); // This effectively backtracks.
break;
}
return myPath
}
Here's an example of how you store your possibilities in a graph data structure (i.e. a half-edge data structure):
class Edge {
public final Node end;
public Edge(Node end) { this.end = end; }
}
class Node {
public final int id;
public final List<Edge> edges;
public Node(int id, List<Edge> edges) { this.id = id; this.edges = Collections.unmodifiableList(edges); }
}
class Graph {
private final ArrayList<ArrayList<Edge>> halfEdges = new ArrayList<>();
private final ArrayList<Node> nodes = new ArrayList<>();
public Node addNode() {
ArrayList<Edge> edges = new ArrayList<>();
Node node = new Node(nodes.size(), edges);
halfEdges.add(edges);
nodes.add(node);
return node;
}
public Edge addEdge(Node from, Node to) {
assert nodes.contains(from) && nodes.contains(to);
Edge edge = new Edge(to);
halfEdges.get(from.id).add(edge);
return edge;
}
}
You'd use it like this:
class Game
{
public static void main (String[] args)
{
Graph graph = new Graph();
Node root = graph.addNode();
Node a = graph.addNode();
graph.addEdge(root, a);
Node b = graph.addNode();
graph.addEdge(root, b);
Node c = graph.addNode();
graph.addEdge(root, c);
Node d = graph.addNode();
graph.addEdge(a, d);
Node e = graph.addNode();
graph.addEdge(a, e);
// ...
// main loop
ArrayList<Node> path = new ArrayList<>();
StringBuilder builder = new StringBuilder();
while(true) {
if(path.isEmpty())
path.add(root);
Node pos = path.get(path.size() - 1);
builder.setLength(0);
builder.append("At ").append(pos.id).append(", choices: back");
for(Edge out : pos.edges)
builder.append(", ").append(out.end.id);
System.out.println(builder.toString());
// handle input: add chosen node to path or remove last entry if "back"
// ...
}
}
}
You can add any kind of data to a Node or an Edge, like an actual name for the choice to display (instead of id) etc...

Avoiding duplicate entries in an array

I am writing a method that adds Vertex objects to an array. I need to check if the vertex I am adding already exists in the array. I am not sure where I am going wrong. Here is my method:
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=1; i<vertices.length; i++) {
if(vertices[i] != vertices[i-1]){
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
}
}
Vertex class:
public class Vertex {
public String name;
public int graphIndex; // index of adjacency matrix position of node in graph
public Vertex (String s) {
name = s;
graphIndex = -1; // invalid position by default
}
public String toString() {
return name;
}
}
The class that contains the addVertex() method:
public class Graph {
private int maxVertices;
private Vertex[] vertices; // array of nodes
private int[][] edges; // adjacency matrix
int activeVertices;
public Graph(int maxSize) {
maxVertices = maxSize;
vertices = new Vertex[maxVertices];
activeVertices = 0;
}
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=1; i<vertices.length; i++) {
if(vertices[i] != vertices[i-1]){
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
}
}
public void addEdge(Vertex v1, Vertex v2, int w) {
edges[v1.graphIndex][v2.graphIndex] = w;
edges[v2.graphIndex][v1.graphIndex] = w;
}
public Graph minimumSpanningTree() {
Graph mst = new Graph(maxVertices); // create new graph
int[] set = new int[activeVertices];
for (int i=0; i<activeVertices; i++) { // copy nodes to graph
mst.addVertex(vertices[i]);
set[i]=i; // assign each node to its own set
}
PriorityQueue q = new PriorityQueue(maxVertices*maxVertices); // create priority queue
for (int i=0; i<activeVertices; i++) { // copy edges to queue
for (int j=0; j<activeVertices; j++) {
if (edges[i][j]!=0) {
q.enqueue(new Edge(vertices[i],vertices[j],edges[i][j]));
}
}
}
while (!q.isEmpty()) { // iterate over all edges in priority order
Edge e = q.dequeue(); // consider next edge
if (set[e.source.graphIndex]!=set[e.destination.graphIndex]) { // skip edges not connecting different sets
mst.addEdge(e.source, e.destination, e.weight); // add edge to MST
System.out.println("adding "+e);
int setToMerge=set[e.destination.graphIndex]; // rename nodes from "other" set
for (int i=0; i<activeVertices; i++) {
if (set[i]==setToMerge) { // find nodes from "other" set
set[i]=set[e.source.graphIndex]; // reassign nodes
}
}
}
}
return mst;
}
public void print() {
System.out.format(" ");
for (int i=0; i<activeVertices; i++) {
System.out.format("%3s ", vertices[i].name);
}
System.out.format("\n");
for (int j=0; j<activeVertices; j++) {
System.out.format("%3s ", vertices[j].name);
for (int i=0; i<activeVertices; i++) {
System.out.format("%3d ", edges[i][j]);
}
System.out.format("\n");
}
}
}
First, you should be using equals instead of ==. You should write a proper equals method in your Vertex class (use Google to find plenty of tutorials on how to do this).
For example, if you wanted two Vertex objects to be considered equal only when their names were the same, then your equals method would look something like this:
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(obj instanceof Vertex) {
Vertex otherVertex = (Vertex) obj;
if(this.name.equals(otherVertex.name)) {
return true;
}
}
return false;
}
If you wanted to compare graphIndex as well, then you would need to check that in the equals method as well.
Assuming you have a proper equals method in your Vertex class, the simplest solution would be to use the ArrayUtils.contains method, from the Apache Commons library (Apache Commons has TONS of useful methods, which can save you a lot of time. You should check it out). This method takes in an array and an Object, and checks if the array contains the object or not.
You're always checking vertices[1] against vertices[0] and adding based on the result. You're not checking for v, and not actually looking in the whole array.
If an == check (identity, not equivalence) is really what you want, then:
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=0; i<vertices.length; i++) {
if(vertices[i] == v){
// Already have it
return;
}
}
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
If you want equivalence instead, replace
if(vertices[i] == v){
with
if(v.equals(vertices[i])){
Side note: Based on your having an activeVertices variable, I suspect you may be better off with ArrayList<Vertex> rather than Vertex[]. That would also give you the contains method (which uses equals), which may be able to replace your loop (if you want an equals, not ==, check).
Whenever you write a value class, i.e. a class that represents a value or quantity of something, you should always override the following methods for your class:
equals(Object o);
hashCode();
Not all classes are value classes. Some represent system resources and others represent actions or processes, but whenever a class is written as an abstraction for a collection of data you should always consider writing the above methods.
The reason is straightforward. Whereas Java primitives have only value, Java reference types (which include all the instances of classes you write yourself) have both value and location. This confers the properties of both equality and identity to reference types and they are very different.
By default, the equals() method in the Object class performs an identity comparison and NOT an equality comparison ... and it's a good thing too. Because any subclass of Object can have vastly different notions of "how instances can be considered equal" there is no straightforward way that Object could have a superclass method that would test equality for any arbitrary Java object. In contrast, it is always straightforward to test for identity. If any two references indicate the same location, then their objects are identical. This exemplifies the different notions of equality and identity.
You need to be able to test whether your Vertex instances are equal and not whether they are identical. For this reason, you really do need to override the equals(Object o) method. If you also override hashCode() (which you should), then you may be able to store your vertices in a HashSet, which would guarantee that no two vertices would be equal.

Categories

Resources