Eingräge einer List<String> in xml datei hinzufügen/entfernen?

will2k

Mitglied
Hallo,

Ich habe folgende GUI:

Der Benutzer gibt verschiedene Strings ein in eine editierbare JCombobox. Danach drückt er rechts davon den add Button damit der string der JCB geaddet wird. Der Benutzer kann auch in der Liste mit dem delete Button einen Eintrag löschen. Doch das alle ist nebensächlich... Wichtiger ist, dass beim drücken des Save Buttons ALLE String-Elemente in der JComboBox in einer xml datei gespeichert werden in einer List<String>.

Wenn aber jedesmal alle Strings die sich in der JCB befinden in die xml Datei geschrieben werden. Bekomme ich ja jedesmal redundante Elemente in der xml Datei so:

Inhalt der JCB:

String1
String2
String3


Inhalt der xml datei:

String1
String2
String3
String1
String2
String3
String1
String2
String3

Nun frage ich micht wie ich verhindern kann das jedesmal ALLE Strings aus der JCBox in der xml gespeichert werden...

1.) Gibt es eine jaxb Annotation z.B. die besagt ein Element in einer Liste darf nur einmal vorkommen?

oder

2.)Bevor ich jeden String in der JCB der xml Datei hinzufüge prüfe ich ob der String in der xml schon vorhanden ist. Bei 10 strings in der JCB und existieren 10 strings in der xml datei ergibt das 100 IF prüfungen was net so performant sein dürfte...

Weiß jemand hier eine vernüftige Lösung ?
 
Nun die Lösung ist ganz einfach: Nimm ein Set und keine List. In einem Set kann jedes Element nur einmal vorhanden sein.

Die JAXB Annotationen kannst du so weiter verwenden.
 
Nun die Lösung ist ganz einfach: Nimm ein Set und keine List. In einem Set kann jedes Element nur einmal vorhanden sein.

Die JAXB Annotationen kannst du so weiter verwenden.


ganz so einfach ists auch net :)

und anstatt List liste = new ArrayList() musst ich ne = new HashSet() nehmen und weiteres Problem ergibt sich dadurch:

Code:
for(int i = 0 ; i < settingsData.getSetMovieFormat().size() ; i++)
        {
        	getMovieFormatCB().addItem(settingsData.getSetMovieFormat().get(i));	        	
        }

Das HashSet/Set kennt keine get(i); methode, wie lese ich nun die Strings einzeln aus dem Set aus und einzeln in die JComboBox genannt movieFormatCB ? Es gibts zwar eine addAll() methode für die Set doch eine AddAllitem gibts für die JCBox leider nicht.

Wie könnt ich da jetzt am geschicktesten vorgehen?
 
Zuletzt bearbeitet:
Man sollte über Listen sowieso nicht mit einer normalen for-Schleife zugreifen. Das ist einfach zu langsam.

Besser ist es einen Iterator zu nehmen:
Java:
final Set s = new HashSet();
//elemente hinzufügen...
for(final Iterator it = s.iterator(); it.hasNext(); ){
    final String element = it.next();
    System.out.println(element);
}

Oder noch einfacher ab Java 1.5:
Java:
final Set<String> s = new HashSet<String>();
//elemente hinzufügen...
for(final String element : s){
    System.out.println(element);
}

Achso noch besser wäre für dich wohl ein TreeSet<String> denn dann sind die Elemente sogar alphabetisch sortiert, was immer ganz gut ist in soner Combobox.
 
Code:
SettingsData settingsData = new SettingsData();
			JAXBContext jc = JAXBContext.newInstance(SettingsData.class);			
			Marshaller m = jc.createMarshaller();
		    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);		    
		    OutputStream os = new FileOutputStream( "test.xml" );


Code:
  for(String  element : this.getMovieFormatCB().toString())
		     {		     
		    	 settingsData.getSetMovieFormat().add(element);		     
		     }

das hier ist code aus der saveXML-Methode. Ich will ja alle strings aus der JCombobox auslesen element für element, doch this.getMovieFormatCB() mag java nicht... alle Strings aus der JCB in ein String-Array lesen und dieses Array mit for zu durchlaufen element für element kanns ja net sein oder?


Code:
    m.marshal(settingsData, os);


Fehler bei this.getMovieFormatCB().toString() -->

Code:
Can only iterate over an array or an instance of java.lang.Iterable
 
Also nochmal zum Verständnis:
for(String element : list)
Liest sich als: Für alle String element in list

Bei der Combobox müßte es heißen:
Java:
JComboBox box = new JComboBox();
for(int i = 0; i < box.getItemCount(); i++){
    Object elem = box.getItemAt(i);
}

Hast du noch Probleme damit die API zu verwenden?

Ich würde dir außerdem statt:
settingsData.getSetMovieFormat().add(element);
empfehlen in settingsData direkt eine Methode addMovieFormat vorzustehen. Ist ein wenig schöner.

Besonders weil man in einem get eigentlich keine Modifizierbare Liste/Set zurückgeben sollte:
Java:
public Set getMovieFormats(){
    return Collections.unmodifiableSet(movieFormats);
}

Das verhindert daß versehentlich Elemente einfach so hinzugefügt werden können.
 
@zeja
Wie sollte man denn auf eine Liste zugreifen? Der Iterator ist langsamer als mit einer normalen for-Schleife. :)

Die performanteste Lösung die ich kenne um über eine Liste zu iterieren ist diese
Code:
for(int i=0, size = list.size(); i<size; i++){
   
}
Man sollte auch die Größe der Liste einer Variablen zuordnen, wie hier im Beispiel und nicht
Code:
for(int i=0; i<list.size(); i++)
da so bei jedem Schleifendurchlauf die Listengröße neu abgefragt wird.

Java:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


public class ListIterationPerformance
{

  /**
   * @param args
   */
  public static void main(String[] args)
  {
    List<String> list1 = new ArrayList<String>();
    
    fillList(list1);
    
    long start, stop;
    
    start = System.currentTimeMillis();
    for(String s : list1){
      
    }
    stop = System.currentTimeMillis();
    System.out.println(stop-start);
    
    start = System.currentTimeMillis();
    for(Iterator<String> it = list1.iterator(); it.hasNext();){
      it.next();
    }
    stop = System.currentTimeMillis();
    System.out.println(stop-start);
    
    
    
    start = System.currentTimeMillis();
    for(int i = 0, size = list1.size(); i<size; i++){
      list1.get(i);
    }
    stop = System.currentTimeMillis();
    System.out.println(stop-start);
  }
  
  private static void fillList(List<String> list){
    
    for(int i = 0; i < 1000000; i++){
      list.add("test"+i);
    }
    
  }

}



Bei einem Set kann man natürlich nur mit einem Iterator auf eine Liste zugreifen. Wobei man die altmodische Variante nehmen sollte und nicht die seit Java 1.5.

MFG

Sascha
 
Zuletzt bearbeitet von einem Moderator:
Achja das war wieder die Sache die in Java keinen Sinn macht: Ein Iterator ist langsamer als ein direkter Zugriff. Für mich sollte nen Iterator immer den schnellsten Zugriff bieten...

Aber einen Fehler hast du gemacht in deinem Performance-Test: Der Iterator ist nur schneller als das for-each weil du die Zuweisung an einen String herausgelassen hast, der ja im for-each gemacht wird.

Korrekter ist es also so:
Java:
package de.tutorials;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListIterationPerformance {

	public static void main(String[] args) {
		List<String> list1 = new ArrayList<String>();

		fillList(list1);

		long start, stop;

		start = System.currentTimeMillis();
		forEachLoop(list1);
		stop = System.currentTimeMillis();
		System.out.println("For each:\t" + (stop - start));

		start = System.currentTimeMillis();
		forIteratorLoop(list1);
		stop = System.currentTimeMillis();
		System.out.println("For iterator:\t" + (stop - start));

		start = System.currentTimeMillis();
		forSizeLoop(list1);
		stop = System.currentTimeMillis();
		System.out.println("For size:\t" + (stop - start));
	}

	private static void forSizeLoop(List<String> list1) {
		for (int i = 0, size = list1.size(); i < size; i++) {
			String s = list1.get(i);
		}
	}

	private static void forIteratorLoop(List<String> list1) {
		for (Iterator<String> it = list1.iterator(); it.hasNext();) {
			String s = it.next();
		}
	}

	private static void forEachLoop(List<String> list1) {
		for (String s : list1) {

		}
	}

	private static void fillList(List<String> list) {
		for (int i = 0; i < 1000000; i++) {
			list.add(Integer.toString(i));
		}
	}

}

Schaut man ins class-File:
Code:
// Method descriptor #22 (Ljava/util/List;)V
  // Signature: (Ljava/util/List<Ljava/lang/String;>;)V
  // Stack: 1, Locals: 3
  private static void forIteratorLoop(java.util.List list1);
     0  aload_0 [list1]
     1  invokeinterface java.util.List.iterator() : java.util.Iterator [74] [nargs: 1]
     6  astore_1 [it]
     7  goto 20
    10  aload_1 [it]
    11  invokeinterface java.util.Iterator.next() : java.lang.Object [78] [nargs: 1]
    16  checkcast java.lang.String [69]
    19  astore_2
    20  aload_1 [it]
    21  invokeinterface java.util.Iterator.hasNext() : boolean [84] [nargs: 1]
    26  ifne 10
    29  return

Code:
// Method descriptor #22 (Ljava/util/List;)V
  // Signature: (Ljava/util/List<Ljava/lang/String;>;)V
  // Stack: 1, Locals: 3
  private static void forEachLoop(java.util.List list1);
     0  aload_0 [list1]
     1  invokeinterface java.util.List.iterator() : java.util.Iterator [74] [nargs: 1]
     6  astore_2
     7  goto 20
    10  aload_2
    11  invokeinterface java.util.Iterator.next() : java.lang.Object [78] [nargs: 1]
    16  checkcast java.lang.String [69]
    19  astore_1
    20  aload_2
    21  invokeinterface java.util.Iterator.hasNext() : boolean [84] [nargs: 1]
    26  ifne 10
    29  return

Sieht man dass der generierte Teil für for-each und for-iterator nun auch (nahezu) gleich sind, so wie es sein sollte.

Ausgabe:
Code:
For each:	55
For iterator:	55
For size:	19

So kann man auch schön erkennen warum man keinen Vector nehmen sollte wenn man diesen nicht benötigt:
Code:
For each:	143
For iterator:	144
For size:	72

also ein gaaanzes Stück langsamer.
 
Also bei mir sind die Zeiten gleich geblieben.

Aber das ist ja erstmal egal. Ich wollte nur deutlich machen, dass die Methode ohne Iterator immer noch die schnellste ist.

MFG

Sascha

EDIT:
Dass intern ein Iterator verwendet wird, weiß ich. Nach nochmal mehrmaligem ausführen, muss ich sagen, dass sich der Iterator schon der for-each-Schleife genähert hat. Aber ich hab ja schon erwähnt worauf ich hinaus wollte. :)
 
Zurück