I can't find the cause of my java.util.ConcurrentModificationException - java

I have code that enters the for loop within my Main method.
for (List<Point2D> points : output) {
currentPath = pathDistance(points);
if (shortest == 0){
shortest = currentPath;
} else if (currentPath < shortest) {
best = points;
shortest = currentPath;
}
}
where pathDistance is defined as
public static Double pathDistance(List<Point2D> path){
double distance = 0;
int count = path.size()-1;
for (int i = 0; i < count; i++) {
distance = distance + path.get(i).distance(path.get(i+1));
}
distance = distance + path.get(0).distance(path.get(count));
return distance;
}
But I keep getting the error
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.SubList.checkForComodification(Unknown Source)
at java.util.SubList.size(Unknown Source)
at java.util.Collections$SynchronizedCollection.size(Unknown Source)
at TSMain.pathDistance(TSMain.java:76)
at TSMain.main(TSMain.java:203)
I know this is supposed to mean that I am altering an object while an iteration depends on it, but I can't for the life of me figure out where that might be happening. Any help would be appreciated.

Your stacktrace shows that somewhere in your code subList is passed to Collections.synchronizedCollection (directly or indirectly). Like this
Set<List<Point2D>> output = Collections.singleton(
Collections.synchronizedCollection(data.subList(start, end)));
However it does not copy data list. And points subList is still pointing to a range in data list. And original list is modified at the momet path.size() call occurs.
You can easily fix your problem by doing explicit list copy before passing it to pathDistance
for(List<Point2D> points : output){
List<Point2D> pointsCopy = new ArrayList<>(points)
currentPath = pathDistance(pointsCopy);
// rest of code using pointsCopy
}
I also should notice that it looks like there is a problem with synchronization in your code. Wrapping sublists in synchronized collection is a bad idea because original list could be modified in unsafe manner w/o proper synchronization.
You can learn more about list modification checking by looking into AbstractList#modCount sources.

Related

Avoiding ConcurrentModificationException in dijkstra's algorithm

For this program, I read in a excel file that lays out a map of towns, towns adjacent to them, and the distance between them, which looks like:
Bourke Nyngan 200
Brewarrina Walgett 134
Broken Hill Mildura 266
Broken Hill Wilcannia 195
Bungendore Queanbeyan 54
etc.
And that's working great. Everything seems perfect. I'm trying to make a program that if I give it two towns, it returns the shortest possible path between the two.
I can get my program to correctly read in the file, and set up everything, so I know that this issue isn't anything to do with the setup. To the best of my knowledge this part of my program works, as my program can get through it without throwing errors:
//returns the Vertex with the smallest distance from a list of Vertices
public static Vertex minDist(List<Vertex> Q){
int min = 2147483647;
Vertex closest = new Vertex("Closest");
for(Vertex v : Q){
if(v.distance < min){
closest = v;
}
}
return closest;
}
//used to relax (change the distance of a town)
public static void relax(Vertex u, Vertex v, int w){
if(v.distance > u.distance + w){
v.distance = u.distance + w;
v.predecessor = u;
}
}
public static void Dijkstra(Graph G, Vertex s){
Vertex u = new Vertex("not good");
List<Vertex> Q = V;
Vertex v = new Vertex("oh no");
//while Q is not empty:
while(!Q.isEmpty()){
//the vertex in Q that has the smallest distance (at first s with 0, then we relax and that changes things)
u = minDist(Q);
if(u.name.equals("Closest")){
//Q.remove(u);
return;
}
Q.remove(u);
S.add(u);
//for each edge e in u's adjacencyList:
for(Edge e : u.roadList){
if(e != null && !e.finish.name.equals(u.name) ){
v = e.finish;
relax(u,v,w(u,v)); //w(u,v) returns the distance between u and v
}
}
}
System.out.println("Q is null");
}
So I have that, and things look okay to me. I know it's a bit Frankenstein'ed together, but I got it to at least run without errors, because the ConcurrentModificationException gets thrown AFTER this method returns in my main method.
This is where my Dijkstra method gets called in my main method. I never reach the line in my code that prints "SHOULD REACH HERE" because the program throws the ConcurrentModificationException.
//if both towns exist and are unique, find the shortest route between them.
if(isTown(town1,V) && isTown(town2,V) && !town1.equals(town2)){
for(Vertex f : V){
if(f.name.equals(town2)){
destination = f;
}
}
System.out.println("Traveling...");
Graph G = new Graph(V,E);
for(Vertex s : V){
if(s.name.equals(town1)){
//////////////////DIJKSTRA STUFF GOES HERE///////////////////
initialize(G,s);
Dijkstra(G, s);
System.out.println("FINISHED DIJKSTRA");
//Print out the things in the vertex array S with their distances.
for(Vertex b : S){
System.out.println(b.name + " (" + b.distance + ")");
}
///////////////////////////////////////////////
}
}
System.out.println("SHOULD REACH HERE");
}
I have never seen a ConcurrentModificationException, my lab TA has never seen a ConcurrentModificationException, and even my professor has never seen a ConcurrentModificationException. Can I get some help with avoiding this? A person in a higher class said that he has only seen this happening when working with multiple threads, and I don't even know what that really means so I assume my program doesn't do that.
If I run the program with with town1 = Grafton and town2 = Bathurst, then the output should be:
First town: Grafton
Second town: Bathurst
Bathurst (820)
Lithgow (763)
Windsor (672)
Singleton (511)
Muswellbrook (463)
Tamworth (306)
Bendemeer (264)
Uralla (218)
Armidale (195)
Ebor (106)
Grafton
But is instead
First town: Grafton
Second town: Bathurst
Grafton (0)
Glen Innes (158)
Inverell (225)
Warialda (286)
Coffs Harbour (86)
I/O error: java.util.ConcurrentModificationException
You're getting this because you're removing from Q while iterating over it it. See Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop

Java TSP 2-opt swap

This is my first year with programming at university and we have just started using java. I've already wrote a bunch of codes for calculating "shortest" path through all points, but it has one problem. Sometimes the path will be overlapping each other. I have been looking for 2-opt swap, but have no clue on how to implement this to my code. Would be awesome with help. Here is my code for calculating distances between points ( cities ):
public void calculate(){
Point current = null;
current = points.get(0);
Point nearestPoint = null;
ArrayList<Point> remainingPoints = new ArrayList<Point>(points);
remainingPoints.remove(current);
lines.clear();
while(!remainingPoints.isEmpty()){
double minimumDistance = -1;
for (int i = 0; i < remainingPoints.size(); i ++){
if (minimumDistance == - 1 || current.distance(remainingPoints.get(i)) < minimumDistance){
minimumDistance = current.distance(remainingPoints.get(i));
nearestPoint = remainingPoints.get(i);
}
}
lines.add(new Point[] { current, nearestPoint });
remainingPoints.remove(current);
current = nearestPoint;
}
lines.add(new Point[] { points.get(0), current });
}
What does it do? Well it is quiet basic. It starts with the first point, then will find the nearest point. This will be saved in an array called lines. This will continue like this until no points are left. Line-array will then be sorted by distances so we can draw lines between them. My question is how can i prevent overlapping? See the links bellow for better description:
I dont want this
I want this

Maze generating stackoverflow error

I am working on my maze game and I've almost got the generating of the maze. The only problem I have is that when I try to run my init method, it gives me a stack overflow error. I think this has to do with the corStack getting too large, but I can't find the cause of this problem. Here's the code:
The maze generating:
public void carveMaze(Stack<Integer> corStack, int currentX, int currentY)
{
ArrayList<Integer> corList = getNeighbours(currentX, currentY);
Random randomGen = new Random();
while (checkForUnvisited()) {enter code here
if (corList.size() > 0) {
boolean goodNumber = false;
int newY = 0;
int newX = 0;
int index = 0;
index = randomGen.nextInt(corList.size());
if (index % 2 != 0) {
newY = corList.get(index);
goodNumber = true;
}
goodNumber = false;
while (!goodNumber) {
index = randomGen.nextInt(corList.size());
if (index % 2 == 0) {
newX = corList.get(index);
goodNumber = true;
}
}
corStack.push(currentY);
corStack.push(currentX);
if (newX > currentX) {
maze[newY][newX - 1].setStatus(Cell.WEG);
maze[newY][newX - 1].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newX < currentX) {
maze[newY][newX + 1].setStatus(Cell.WEG);
maze[newY][newX + 1].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newY > currentY) {
maze[newY - 1][newX].setStatus(Cell.WEG);
maze[newY - 1][newX].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newY < currentY) {
maze[newY + 1][newX].setStatus(Cell.WEG);
maze[newY + 1][newX].setVisited(true);
maze[newY][newX].setVisited(true);
}
maze[currentY][currentX].setVisited(true);
currentX = newX;
currentY = newY;
carveMaze(corStack, currentX, currentY);
} else {
if (!corStack.isEmpty()) {
currentX = (int) corStack.pop();
currentY = (int) corStack.pop();
carveMaze(corStack, currentX, currentY);
}
}
}
}
The cell Class:
package javaapplication23;
public class Cell {
public static final int SPELER = 2;
public static final int WEG = 0;
public static final int MUUR = 1;
public static final int BAZOOKA = 3;
private int status;
private boolean visited;
Cell(int status, boolean visited)
{
this.status = status;
this.visited = visited;
}
public int getStatus()
{
return status;
}
public boolean getVisited()
{
return visited;
}
public void setStatus(int status)
{
this.status = status;
}
public void setVisited(boolean visited)
{
this.visited = visited;
}
}
I double checked everything but I can't find the cause of the problem. The problem starts when I put the else{if(!corstack.isempty)} section after the if(corList.size() > 0) statement so I know it's in there somewhere.
stacktrace:
Exception in thread "main" java.lang.StackOverflowError
at java.util.ArrayList.grow(ArrayList.java:239)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:220)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:212)
at java.util.ArrayList.add(ArrayList.java:443)
at javaapplication23.MazeManager.getNeighbours(MazeManager.java:154)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:55)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
You are calling carveMaze() within carveMaze() and this causes the StackOverflowError. Everytime the method is called, its variables take up space in the stack, which are garbage collected when the method completes. In your case, before the method completes, you are calling it again and therefore your stack gets filled-up more before it had time to empty-up. Your stack gets full ("overflown") thus the Error. If you want to call carveMaze() continuously, you'd be better off with a loop of some sort.
Before you go to any trouble changing the structure of your code, you need to be sure there is no faulty logic within your algorithm implementation. This is partly just stating the obvious, but it's worth mentioning:
Try your code with minimally sized mazes and see if they complete without an overflow. If they consistently work, then your logic might be sound. If they don't, then there's faulty logic in finding an exit condition for your recursive loop (and that will guarantee an overflow every time).
Try to pinpoint (using your debugger) why these minimal mazes are not finding a termination condition (ie. the case where the recursive calls will stop happening). This can be difficult to do, but is made easier if you are working with minimal reproducible examples - ie. very small mazes.
Sometimes it can also help to put some conditional blocks into your source solely to allow you to set a breakpoint that can be triggered only when a condition is met (ie. allowing you to break when a list is under a certain size for example).
Once you get it working for some mazes, Unit Testing each part of your code is a great way to ensure that special circumstances ("corner cases") aren't causing your code to derail on seemingly arbitrary occasions.
Once small examples work successfully for many inputs, you can start creating larger mazes. If the stack overflow is only happening on very large mazes then its possibly just an issue with the size of the stack.
At that point, if this appears to definitely be the problem, try increasing your java stack size:
java -Xss4m Test
for more info look here: How to increase the Java stack size?
If increasing the stack size solves the problem then all may be good: you can either work around your issues by increasing the stack size every time you run your code, or change the code to remove the recursion, but at least you'll know the logic does find a termination condition eventually given enough memory resources to work with.
The general approach to removing recursion is to use a loop and your own Stack data structure rather than the virtual machine's stack. Further info here:
Way to go from recursion to iteration
I think the most important point is that debugging code is easier to do with a debugger than just from reading it, which makes you, the writer of the code, the person in the best position to fix it.
Besides, debugging your code is usually more fun than writing questions asking other people to help.
Hope these ideas help you solve your problem.
Looks like your program is calling carveMaze() recursively without any base conditions and thus you are getting StackOverFlow Issue.Can you identify a case when this function should stop calling itself.Please find an example here
I found it :) the fault was with the getting of random new neighbour. I inserted the coordinates like x, y, x, y, x, y etc. in the neighbour list and then chose random coordinates. instead of getting the x and following y, i chose a random x with random y :0
thanks all!

InvocationTargetException while trying to add variables to a new object

I am trying to send two variables from one sketch to another, using the oscP5 library for processing.
The message I am sending is created like this:
OscMessage myMessage = new OscMessage("/test");
myMessage.add(title);
myMessage.add("Zeit");
oscP5.send(myMessage, remoteLocation);
In the second sketch, I receive the data like that:
void oscEvent(OscMessage theOscMessage) {
if(theOscMessage.checkAddrPattern("/test")) {
String title = theOscMessage.get(0).stringValue();
String layoutType = theOscMessage.get(1).stringValue();
addToQueue(title, layoutType);
}
}
And here my simplified addToQueue function:
void addToQueue(String title, String layoutType) {
if(!existsInQueues(title)) {
upcomingHeadlines.add(new Headline(title, printAxis, scrollSpeed, layoutType));
}
}
Every time I start the sketches, I get the error:
ERROR # OscP5 ERROR. an error occured while forwarding an OscMessage to a method in your program. please check your code for any possible errors that might occur in the method where incoming OscMessages are parsed e.g. check for casting errors, possible nullpointers, array overflows ... .
method in charge : oscEvent java.lang.reflect.InvocationTargetException
I have been able to track the problem down to the layoutType-Variable. If I change
String layoutType = theOscMessage.get(1).stringValue();
to
String layoutType = "Zeit";
no error occurs.
That is quite confusing, because both versions should have the same result.
The error message does not help me in any way.
Edit
I have compared the two possible variables like that:
String layoutType = theOscMessage.get(1).stringValue();
String layoutTypeB = "Zeit";
if(layoutType.equals(layoutTypeB)) println("Same String!");
Since gets printed to the console, both have to be the same … I really do not know where to search for an error anymore.
Edit 2
I have wrapped my second sketch in try {...} catch(Exception ex) {ex.printStackTrace();} like that:
void oscEvent(OscMessage theOscMessage) {
try {
if(theOscMessage.checkAddrPattern("/test")) {
if(debug && debugFeed) println("Received message from other sketch.");
String title = theOscMessage.get(0).stringValue();
String layoutTypeO = (String)theOscMessage.get(1).stringValue();
String layoutType = "Zeit";
if(debug && debugTemp) {
if(layoutType.equals(layoutTypeO)) println("IS DOCH GLEICH!");
}
if(debug && debugFeed) println("Parsed Information.");
if(debug && debugFeed) println("-----");
addToQueue(title, layoutTypeO);
}
} catch(Exception ex) {ex.printStackTrace();}
}
That gives me this error as result:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at printer$Headline.useLayout(printer.java:260)
at printer$Headline.<init>(printer.java:188)
at printer.addToQueue(printer.java:407)
at printer.oscEvent(printer.java:395)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at oscP5.OscP5.invoke(Unknown Source)
at oscP5.OscP5.callMethod(Unknown Source)
at oscP5.OscP5.process(Unknown Source)
at oscP5.OscNetManager.process(Unknown Source)
at netP5.AbstractUdpServer.run(Unknown Source)
at java.lang.Thread.run(Thread.java:744)
Edit 4
Constructor for my Headline-Class:
class Headline {
//Define Variables
Layout layout;
String title, lastHeadline;
float yPos, speed;
float transparency = 255;
boolean fullyPrinted = false;
int boundingBoxHeight;
// Initialize Class Function
Headline(String t, float y, float s, String lay) {
title = t;
yPos = y;
speed = s;
layout = useLayout(lay);
boundingBoxHeight = calculateTextHeight(title);
}
You might want to know about useLayout() too, so here it is:
Layout useLayout(String name) {
ArrayList layoutVariants = new ArrayList<Layout>();
int existingLayouts = layouts.size();
Layout chosenLayout;
for(int i = 0; i < existingLayouts; i++) {
Layout currentLayout = (Layout)layouts.get(i);
if(currentLayout.layoutType == name) {
layoutVariants.add(currentLayout);
}
}
if(layoutVariants != null) {
int rand = (int)(Math.random() * layoutVariants.size());
chosenLayout = (Layout)layoutVariants.get(rand);
} else {
chosenLayout = (Layout)layouts.get((int)(Math.random() * existingLayouts));
}
return chosenLayout;
}
There are two problems with your code, and both of them are in your useLayout method.
The first problem is that you are not comparing Stringss correctly on this line:
if(currentLayout.layoutType == name) {
name is a String, and I assume currentLayout.layoutType is too. Two Strings that are equal but not the same will not compare equal under ==. As a result of this, your layoutVariants list will quite probably be empty at the end of the for loop.
This line should read:
if(currentLayout.layoutType.equals(name)) {
See also this question.
The second problem is that you don't correctly handle the case that the layoutVariants list is empty. The problem is on this line:
if(layoutVariants != null) {
layoutVariants will never be null, so the else branch of this if statement will never execute. Because layoutVariants.size() will be zero, rand will always be zero. Trying to get the element at index 0 in an empty ArrayList will give you precisely the IndexOutOfBoundsException you are seeing.
I imagine you want the else block to execute if the layout name given isn't recognised, in other words, if the layoutVariants list is empty, rather than null. In that case, change this line to
if(!layoutVariants.isEmpty()) {
Note the ! (not-operator) before layoutVariants. You want the code under the if statement to run if the layoutVariants element is not empty.
EDIT in response to your comments: a null ArrayList is very much not the same as an empty one. null is a special value meaning that the variable doesn't have an object of a given type.
Let's try a real-world analogy: a shopping bag. If you have an empty bag, or no bag at all, then you have no shopping either way. However, you can put things into an empty bag, and count how many items it contains, for example. If you don't have a bag, then it doesn't make sense to put an item in it, as there's no bag to put the item into. null represents the case where you don't have a bag.
Similarly, a String is a collection of characters, and the collection of characters can exist even if it doesn't contain any characters.
isEmpty() can be used for any collection, and, if you're using Java 6 or later, Strings as well. Off the top of my head I can't name any other classes that have an isEmpty method. You'll just have to consult the documentation for these classes to find out.
I've not worked with Processing much, but I am aware that Processing is built on Java, so I would expect any standard Java method to work. Also, I wouldn't worry about 'clearing' a variable: the JVM is generally very good at clearing up after you. There's certainly nothing I can see wrong with your code in this respect.
EDIT 2 in response to your further comment: ArrayList arr; declares a variable of type ArrayList. However, the variable arr is uninitialized: it does not have a value (not even null) and it is an error to try to read the value of this variable before you have assigned a value to it:
ArrayList arr;
System.out.println(arr); // compiler error: arr might not have been initialised.
Assign null and the code then compiles:
ArrayList arr = null;
System.out.println(arr); // prints 'null'.
It's not often you need to declare a variable and not give it a name, but one common case is where you want to assign different values to the same variable on both sides of an if statement. The following code doesn't compile:
int y = getMeSomeInteger(); // assume this function exists
if (y == 4) {
int x = 2;
} else {
int x = 5;
}
System.out.println(x); // compiler error: cannot find symbol x
The reason it doesn't compile is that each variable x is only available within the braces { and } that contain it. At the bottom, neither variable x is available and so you get a compiler error.
We need to declare x further up. We could instead write the following;
int y = getMeSomeInteger(); // assume this function exists
int x = 0;
if (y == 4) {
x = 2;
} else {
x = 5;
}
System.out.println(x);
This code compiles and runs, but the value 0 initially assigned to x is never used. There isn't a lot of point in doing this, and we can get rid of this unused value by declaring the variable but not immediately giving it a value.
int y = getMeSomeInteger(); // assume this function exists
int x;
if (y == 4) {
x = 2;
} else {
x = 5;
}
System.out.println(x);

LinkedList: java.lang.OutOfMemoryError: Java heap space

I'm trying to make a graph implementation for an assignment, which has Graph(GraphImp) objects and Node(NodeImp) objects.
Node objects contain a reference to their Graph, x & y co-ordinates and a name.
The Graph object contains a linked list of its Nodes.
The problem occurs when I try to add a Node into the middle of the List of Nodes (Appending to the end works fine). The program runs out of heap space. I'm not sure why this is occurring though, since the complexity of inserting to a LinkedList should be O(1), and Java (I believe) uses pointers, rather that the objects themselves. I've also tried an arraylist
Making the heap larger is not an option in this instance, and (as far as I understand) should not be the source of the problem.
Thanks in advance.
Here is the error:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.LinkedList.addBefore(LinkedList.java:795)
at java.util.LinkedList.add(LinkedList.java:361)
at pt.graph.GraphImp.addNode(GraphImp.java:79)
at pt.graph.NodeImp.<init>(NodeImp.java:25)
at pt.graph.Graphs.newNode(Solution.java:68)
Here is the Code:
class Graphs
{
static Node newNode(Graph g, double xpos, double ypos, String name) throws InvalidGraphException,InvalidLabelException
{
if(g==null || !(g instanceof GraphImp)){ //Checking validity of inputs
throw new InvalidGraphException();
}
if(name==null){
throw new InvalidLabelException();
}
NodeImp[] existNodes = ((GraphImp)g).getNodes(); //Get all Nodes already present in the Graph
for(int i=0;i<existNodes.length;i++){
if(existNodes[i].getXPos() == xpos && existNodes[i].getYPos() == ypos){ //If node already present at this position, throw InvalidLabelException()
throw new InvalidLabelException();
}
}
Node n = new NodeImp((GraphImp)g, xpos, ypos, name); //If all inputs are valid, create new node
return n;
}
}
class NodeImp extends Node //Node Class
{
private Object flags = null;
private GraphImp g = null;
private double xpos = 0.0;
private double ypos = 0.0;
private String name = "";
NodeImp(GraphImp g, double xpos, double ypos, String name){
this.g = g;
this.xpos = xpos;
this.ypos = ypos;
this.name = name;
g.addNode(this); // Add Node to the Graph
}
}
class GraphImp extends Graph
{
private LinkedList<NodeImp> nodes = new LinkedList<NodeImp>(); //LinkedList of all Nodes in the Graph
GraphImp(){
}
NodeImp[] getNodes(){ //Returns an array of all Nodes
NodeImp[] nArr = new NodeImp[nodes.size()];
return nodes.toArray(nArr);
}
int countNodes(){ //Returns number of Nodes
return nodes.size();
}
void addNode(NodeImp n){ //Add a Node to the LinkedList in order
boolean added = false;
for(int i = 0;i<nodes.size();i++){
if(n.compareTo(nodes.get(i))<=0 ){
nodes.add(i,n); //fails here
}
}
if(!added){
nodes.add(n);
}
return;
}
}
The problem is that you are not exiting your loop after inserting the new node in the middle of the list. Your code will try to insert the same node an infinite number of times, hence the OOM.
Try this:
for(int i = 0;i<nodes.size();i++){
if(n.compareTo(nodes.get(i))<=0 ){
nodes.add(i,n);
added = true;
break;
}
}
As an aside, your insertion is pretty inefficient. Since you know the list is already sorted you could use a binary search to find the insertion point rather than an O(n) scan of the list. Your current implementation is O(n^2) to insert n items, but it could be O(n log n).
It's hard to diagnose the exact cause of your OOM without the whole program, but here's one observation:
getNodes()
is pretty inefficient. You toArray the LinkedList simply to traverse it and look for a particular instance. Why not just use .contains() properly? No need to copy all of the elements then. Or just do what you were doing before but do it on the List instead of an array copy:
for(NodeImp n : existingNodes){
if(n.getXPos() == xpos && n.getYPos() == ypos){
throw new InvalidLabelException();
}
}
My guess is that the 'old' approach of adding to the end was likely to hit an OOM as well but for some heisenbug reason it hasn't manifested itself. Have you run with a profiler?

Categories

Resources