ArrayIndex

Marlon

Mitglied
Hallo allerseits!

Ich erhalte eine

java.lang.ArrayIndexOutOfBoundsException: 10
at java.util.ArrayList.add(Unknown Source)

Fehlermeldung, wenn ich mittels arrayListInstance.add(object) ein Objekt einer Arrayliste hinzufügen möchte.
Hat jemand Tipps, wie ich dieses Problem umgehen kann?

P.S.: Angetriggert wird das ganze von einem KeyEvent (also eigener Thread). Ich vermute also dass das Problem darin liegt, dass Arraylisten nicht threadsafe sind und ich daher besser einen Vector verwenden sollte.
 

Marco13

Top Contributor
Ja, Vector könnte eine Lösung sein (oder eben Collections.synchronizedList(new ArrayList<E>()))

Wo werden noch Elemente zu dieser List hinzugefügt?
 

Marlon

Mitglied
Das Problem tritt immer dann auf, wenn gleichzeitig auf die Arrayliste zugegriffen wird, um ein Element zu entfernen.
Im Anhang die konkrete Fehlermeldung.

Umgesetzt wird übrigens ein Echtzeit Actionspiel, bei dem es auf Schnelligkeit und Performance ankommt.
Frage: Ist es sinnvoller eine Collections.synchronizedList(new ArrayList<E>()) oder eben einen Vector zu verwenden?
(Geispeichert werden zwischen 1000 und 50000 Objekten)

Java:
java.lang.ArrayIndexOutOfBoundsException: 10
at java.util.ArrayList.add(Unknown Source)
at N.P.C(MainController.java:1724)
at L.Y.é(DragableObject.java:536)
at skillsystem.H.D(Skill.java:346)
at skillsystem.H.D(Skill.java:354)
at skillsystem.H.D(Skill.java:354)
at skillsystem.H.L(Skill.java:232)
at X.g.F(HealthObject.java:766)
at A.F.F(RPGCharacter.java:1295)
at X.g.D(HealthObject.java:452)
at X.g.B(HealthObject.java:581)
at X.g.?(HealthObject.java:248)
at A.F.?(RPGCharacter.java:172)
at N.P.E(MainController.java:1216)
at N.P.?(MainController.java:1228)
at N.P.?(MainController.java:1004)
at applet.B.run(RPGBoard.java:403)
at java.lang.Thread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.util.ArrayList.fastRemove(Unknown Source)
at java.util.ArrayList.remove(Unknown Source)
at N.P.B(MainController.java:1728)
at L.Y.é(DragableObject.java:524)
at skillsystem.H.D(Skill.java:346)
at skillsystem.H.D(Skill.java:354)
at skillsystem.H.D(Skill.java:354)
at skillsystem.H.L(Skill.java:232)
at X.g.F(HealthObject.java:766)
at A.F.F(RPGCharacter.java:1295)
at X.g.D(HealthObject.java:452)
at X.g.B(HealthObject.java:581)
at X.g.E(HealthObject.java:734)
at X.g.K(HealthObject.java:827)
at L.Y.F(DragableObject.java:387)
at L.Y.?(DragableObject.java:403)
at L.n.?(DragableSkill.java:171)
at E.A.keyPressed(RPGKeyListener.java:86)
at java.awt.Component.processKeyEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Window.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
 

Marco13

Top Contributor
Interessant, obfuskiert. Ja, im einfachsten Fall ist das durch eine der beiden Möglichkeiten erledigt, aber beachte, dass z.B. ein Drüberiterieren dann ggf. auch synchronisiert werden muss.

Es ist fast egal, ob Vector oder sychronizedList, solange man nur
private List<X> list = ...
schreibt, und nicht
private Vector<X> list = ...
 

Marlon

Mitglied
Danke für die Hilfe!

Sorry, wenn ich jetzt nochmal nachhake, aber bevor ich das jetzt an mehreren Stellen falsch umsetze möchte ich doch lieber nochmal Rat einholen.

Du empfiehlst also folgende Deklaration zu verwenden? :
private List<X> dieListe=new List<X>;
bzw:
private List<X> dieListe=new Vector<X>;

Warum kann ich nicht gleich folgendes schreiben? :
private Vector<X> dieListe=new Vector<X>;

Ich iteriere über die Listen standartmässig mittels
for(int i=0;i<dieListe.size();i++) {}
Sollte ich das jetzt in einen synchronized Block schreiben?

Danke nochmal!
 

Marco13

Top Contributor
Du empfiehlst also folgende Deklaration zu verwenden? :
private List<X> dieListe=new List<X>;
bzw:
private List<X> dieListe=new Vector<X>;

Warum kann ich nicht gleich folgendes schreiben? :
private Vector<X> dieListe=new Vector<X>;

private List<X> dieListe= Collections.synchronizedList(new ArrayList<X>());
bzw
private List<X> dieListe=new Vector<X>();

Dass man das in beiden Fällen nur als "List<X>" deklariert, hat den Vorteil der höheren Flexibilität. Das macht man ganz allgemein, wird als "gegen das Interface programmieren" bezeichnet. Auch bei Methoden sollte immer dastehen
public List<X> doSomething(List<Y> list) { ... }
statt
public ArrayList<X> doSomething(ArrayList<Y> list) { ... }

Der Grund dafür ist klar: Wenn du jetzt schon überall in deinem Programm "ArrayList" verwendet hast, steht dir einiges an Arbeit bevor. Wenn überall nur "List" steht, änderst du die eine Zeile, in der die Liste erstellt wird, von
private List<X> dieListe=new ArrayList<X>();
in
private List<X> dieListe=new Vector<X>();
und am Rest des Programms ändert sich nichts.

Abgesehen von der synchronisation, die vielleicht noch an anderen Stellen (aber unabhängig davon) notwendig ist:
Ich iteriere über die Listen standartmässig mittels
for(int i=0;i<dieListe.size();i++) {}
Sollte ich das jetzt in einen synchronized Block schreiben?
Das solltest du, FALLS während dieser Iteration ein anderer Thread die Liste ändern kann (dann fliegt nämlich sonst eine ConcurrentModificationException). Du solltest genau überlegen, welcher Thread mir welcher Liste was macht - da kann man leicht den Überblick verlieren...
 

Andi_CH

Top Contributor
Du empfiehlst also folgende Deklaration zu verwenden? :
private List<X> dieListe=new List<X>;

Das kann gar nicht gehen - List ist nur ein Interface und kann nicht instanziert werden

Java:
       	List<Integer> meineListe = new Vector<Integer>();

Ist einer der richtigen Wege.

Man verwendet bei der Referenzdeklaration üblicherweise das Interface und kann so später ganz einfach (auf nur einer Zeile) den Datentp wechseln, wenn sich das aufdrängt.
Der neue Typ muss einfach das Interface List implementieren.
 

Marlon

Mitglied
Gekapselt ist bei mir natürlich alles, deswegen sollte ja eigentlich:
private Vector<X> dieListe = new Vector<X>;
kein Problem darstellen?
Aber ich denke ich werde mich trotzdem für Variante
private List<X> die Liste = Collections.synchronizedList(new ArrayList<X>());
entscheiden.

for(int i=0;i<dieListe.size();i++)
verwende ich leider ziemlich häufig. Folglich muss ich dann einige synchronized Blöcke hinzufügen.
Das implizite "synchronized" bei Vektoren bezieht sich also auf die internen Operationen wie .add und .remove.
Vielen Dank!
 

Marco13

Top Contributor
for(int i=0;i<dieListe.size();i++)
verwende ich leider ziemlich häufig. Folglich muss ich dann einige synchronized Blöcke hinzufügen.
Das implizite "synchronized" bei Vektoren bezieht sich also auf die internen Operationen wie .add und .remove.

Bevor du das jetzt an 10000 Stellen änderst, nochmal: Überleg' dir genau, welcher Thread da was macht. Vielleicht würde es schon reichen, z.B. an der einen oder anderen Stelle eine Kopie der Liste zu erstellen und zu verwenden. Nicht blind-unüberlegt jeden Foren-Hinweis umsetzen ;) IMHO passieren die größten Programmiersünden, indem man versucht, Fehler zu beheben, deren Ursache man nicht verstanden hat.
 

Andi_CH

Top Contributor
Kennst du diese Art von Loop schon?

Java:
       	List<Integer> meineListe = new Vector<Integer>();
       	meineListe.add(4);
       	meineListe.add(2);
       	for (Integer i : meineListe) {
       		System.out.println(i);
       	}
 

Marco13

Top Contributor
Auch dort muss man synchronisieren (sogar noch "dringender" als beim anderen - das andere funktioniert "meistens", weil ja jedes mal .size() aufgerufen und direkt zugegriffen wird. Den foreach haut's früher mit einer ConcurrentModificationException raus. (Und das ist nichts schlechtes, im Gegenteil, das ist das "Fail Fast"-Konzept was in der API Doc beschrieben ist))
 

Marlon

Mitglied
...passieren die größten Programmiersünden, indem man versucht, Fehler zu beheben, deren Ursache man nicht verstanden hat.
Da ist was Wahres dran.
Bei all den Threads, die ich geschrieben habe, habe ich mir genauestens Gedanken gemacht, welche Methoden bzw. Bloecke synchronisiert werden sollten und welche nicht und da scheint es auch so gut wie keine Probleme zu geben.
Das Problem ist allerdings, dass der MouseListener einen eigenen Thread verwendet und die Logik der Klicks demnach sofort ausgefuehrt wird und an wirklich vielen Stellen meines Programms zu Fehlern fuehren kann.

Ich habe mir nun noch ein anderes System ueberlegt.
Ich koennte die MouseEvents abfangen und gewisse Flags fuer Mausposition und Art des Klicks speichern.
In der Lebensschleife der Run-Methode wuerde dann auf die Flags reagiert werden und somit eine geordnete Abarbeitung aller Events erfolgen.
 

Marco13

Top Contributor
Ohne den Kontext genau zu kennen: Sowas wie "SwingUtilities.invokeLater" kann man auch "umgekehrt" machen:
Java:
class Hauptklasse
{
    private List<Runnable> tasks = ...

    public void addTask(Runnable r) { tasks.add(r); }

    public void methodeDieImHauptThreadLäuft()
    {
        while (true)
        {
            while (tasks.size() > 0) tasks.remove(0).run();

            ...
        }
    }

}

(Auch da muss man noch ein bißchen rum-synchronisieren, aber SEHR lokal im Vergleich zu anderen Lösungen)

Dann kann man z.B. sowas machen wie
Java:
void mousePressed(...)
{
    hauptklasse.addTask(new Runnable()
    {
        public void run() {
            machWasMitEinerListe();
        }
    });
}

Also Aufgaben (Runnables) in die Hauptklasse übergeben, die DORT dann (alle vom gleichen Thread, nacheinander) ausgeführt werden.
 

Samuel72

Aktives Mitglied
Was ich schon mal (recht erfolgreich) ausprobiert habe:
Die LinkedList selber basteln,
also statt [c]LinkedList<MeinObjekt>[/c] in der Klasse MeinObjekt die Klassenvariablen [c]MeinObjekt prev, next[/c]
Dann muss man halt beim Hinzufügen bzw. Entfernen von Objekten jeweils die Links so umhängen, dass ein zweiter Thread, der gerade gleichzeitig drüberläuft, nicht ins Leere läuft.
 

truesoul

Top Contributor
Warum nicht einfach :
Java:
 CopyOnWriteArrayList
verwenden?

Bei Vector kann es trotz das sie synchronized ist eine ConcurrentModificationException schmeißen.
Schwachpunkt ist dort der Iterator.

Vector (Java 2 Platform SE v1.4.2)

Oder die bisherige Variante verwenden und sowas wie:
Java:
List<Byte> syncList = Collections.synchronizedList( new LinkedList<String>() ); 
synchronized ( syncList ) 
{ 
  Iterator iter = syncList.iterator(); 
}
machen.
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Ja, die CopyOnWriteArrayList hätte man schonmal erwähnen sollen ... aber auch, dass sie nicht in allen Fällen die beste Wahl ist (wenn es um "viele" Elemente geht und die Liste "oft" geändert wird ist sie natürlich nicht so gut...)
 

Marlon

Mitglied
Vielen Dank für die vielen Tipps! :)

Ich werde es erstmal so versuchen, dass ich eine Liste von MouseEvents bereitstelle:
Vector<MouseEvent> mouseEventList=new Vector<MouseEvent>();

In diese Liste werden die gesamten MouseEvents geschmissen und in der run() Methode des Hauptprogramms abgearbeitet:

Java:
public void run() {
   ...
   for(int i=0;i<mouseEventList.size();i++) {
      mouseListener.executeMouseEvent(mouseEventList.get(i));
   }
}
 

Neue Themen


Oben