6

I've just ported all my arrays to ArrayList (due to my great lack of knowledge in Java I didn't know basic Array type does not have any ".add" option) in my little program and everything seems fine... except that from time to time an exception is thrown, but it contradicts itself:

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 17, Size: 21
    at java.util.ArrayList.rangeCheck(ArrayList.java:604)
    at java.util.ArrayList.get(ArrayList.java:382)
    at guay.Puntitos.AumentarTamano(Puntitos.java:346)
    at guay.Guay$MiMouse.mouseMoved(Guay.java:226)
    at java.awt.Component.processMouseMotionEvent(Component.java:6550)
    at java.awt.Component.processEvent(Component.java:6274)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Window.processEvent(Window.java:2016)
    at java.awt.Component.dispatchEventImpl(Component.java:4861)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Window.dispatchEventImpl(Window.java:2713)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:707)
    at java.awt.EventQueue.access$000(EventQueue.java:101)
    at java.awt.EventQueue$3.run(EventQueue.java:666)
    at java.awt.EventQueue$3.run(EventQueue.java:664)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:680)
    at java.awt.EventQueue$4.run(EventQueue.java:678)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:677)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

The block of code that Java point me to is this:

for (int i = 1; i < elipsasCol.size(); i++) {
  if (elipsasCol.get(i) != null && elipsasCol.get(i).contains(mouse)) {
     // This line                    
     double modulo = Math.sqrt(Math.pow(mouse.x - elipsasCol.get(i).getCenterX(), 2) 
                             + Math.pow(mouse.y - elipsasCol.get(i).getCenterY(), 2));
  }
}

The error isn't causing any trouble to the performance of the program. However, I would appreciate that someone can explain me what is the cause of this exception.

Thank you!

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
LosTChG
  • 77
  • 2
  • 7
  • 1
    you might wanna start the loop at 0 unless it's a special value... – ratchet freak May 06 '12 at 21:58
  • is there a specific reason as to why your loop starts @1 rather than 0? – Lyuben Todorov May 06 '12 at 21:59
  • 2
    Is there a reason why you don't use [for-each loop](http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html)? – amit May 06 '12 at 21:59
  • 2
    That Index:17, Size:21 is awfully suspicious. The source code for ArrayList is at http://www.docjar.com/html/api/java/util/ArrayList.java.html. The line numbers completely agree with what you have shown in your stack trace, but it does not appear possible from that code that that message _could_ ever appear. I will be interested to see the ultimate answer here. Do you have other threads adding to the list here? – Ray Toal May 06 '12 at 22:01
  • 2
    I have a feeling this is a threading issue. As @RayToal has pointed out the message should be impossible; unless, you are adding to the list between the time it is throwing the exception and it creates the message. – Krrose27 May 06 '12 at 22:04
  • @amit I just prefer this type of loop, but if there is any benefit in using for-each loop I will consider using it... After all, I'm learning Java ;) – LosTChG May 06 '12 at 22:10
  • @user1204548 well the way you are doing it doesn't create an iterator. So you won't receive a concurrent modification issue. Can you temporarily switch it to a foreach loop and see if you get a concurrent modification issue? – Krrose27 May 06 '12 at 22:12
  • @RayToal Nope, just the active rendering loop thread – LosTChG May 06 '12 at 22:12
  • Try to create a copy of `elipsasCol` before iterating. And see if exception still appear. – Mikita Belahlazau May 06 '12 at 22:22

1 Answers1

11

This is going to be a concurrent modification issue. It is the only way you can get that error.

The reason you do not receive a concurrent modification error is because the way you are doing the looping through doesn't create an iterator and as such no chance is provided for a concurrent modification error to be thrown.

I would suggest syncing on your arraylist or using something such as a CopyOnWriteArrayList.

Edit: Sorry CopyOnWrite will not work for this specific issue. You need to switch to a foreach loop for it to be an option.

In response to your comment below:

Synchronizing:

synchronized(elipsasCol){
for (int i = 1; i < elipsasCol.size(); i++) {
  if (elipsasCol.get(i) != null && elipsasCol.get(i).contains(mouse)) {
    // This line                    
    double modulo = Math.sqrt(Math.pow(mouse.x - elipsasCol.get(i).getCenterX(), 2)
                            + Math.pow(mouse.y - elipsasCol.get(i).getCenterY(), 2));
  }
}

and then add a similar synchronized(elipsasCol){} around anywhere else you touch elipsasCol.

or

for (T obj : elipsasCol) {
  if (obj != null && obj.contains(mouse)) {
    // This line                    
    double modulo = Math.sqrt(Math.pow(mouse.x - obj.getCenterX(), 2)
                            + Math.pow(mouse.y - obj.getCenterY(), 2));
  }
}

which will most likely cause a concurrentmodification error to be thrown. At which point you can switch your ArrayList to a CopyOnWriteArrayList or synchronize around it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Krrose27
  • 1,026
  • 8
  • 13
  • 2
    Will it work without for each loop? As I understand from javadoc `CopyOnWriteArrayList` helps when new iterator is created (we use for each loop). Is it correct? – Mikita Belahlazau May 06 '12 at 22:25
  • 1
    @NikitaBeloglazov you are correct. He needs to switch to a for each loop and then he can use a copy on write to avoid the concurrent modification issue he will get. I've edited my answer to reflect this. – Krrose27 May 06 '12 at 22:28
  • Wowowow, this surpass my amateurish java level, time to study ;) – LosTChG May 06 '12 at 22:39
  • @user1204548 I've added examples for you. – Krrose27 May 06 '12 at 22:47
  • Brilliant! You've solved it. I didn't use your method, but you pointed me to the true problem. I just misunderstood the use of ArrayList's .add and .set, I was updating elipsasCol by destroying and creating a new elipsasCol and then using .add to refill it with new data while I had another method using elipsasCol, so silly... Anyway, I would take more care on syncing things... – LosTChG May 06 '12 at 23:22