# JList verursacht Absturz



## jf (3. Jan 2011)

Hallo Leute, ich habe ein Problem mit der JList-Komponente.

Ich habe einen Wrapper geschrieben, welcher von JScrollPane erbt und über Komposition eine JList beinhaltet. Über den Konstruktor wird dann die JList meiner Komponente hinzugefügt und der ViewportView gesetzt, sowie das ListModel gewählt (DefaultListModel).
Dann gibt es da noch so ein paar praktische Methoden wie addItem(Object obj), addItem(int index, Object obj), removeItem(int index) und clear() sowie ein paar weitere Hilfsmethoden. Außerdem ist noch ein DefaultListCellRenderer implementiert, welcher dafür sorgt, das Tabulatorsprünge in der JList dargestellt werden.
PS: ich kann gerne den Quelltext hier veröffentlichen, wenn dies erforderlich sein sollte - sind aber um die 60 Zeilen, weshalb ich erst einmal darauf verzichte.

Das Problem ist, dass ich in der Listbox sehr viele Daten darstellen will. In der Regel geht dies recht flott, aber manchmal dauert es auch ungewöhnlich lange bzw. die Anwendung stirbt ganz weg (der Client-Bereich des Fensters wird dann komplett schwarz).
Das Problem ist nicht reproduzierbar, es passiert einfach hin- und wieder.
Wenn ich die Zeile, welche die Listen-Einträge einfügt, auskommentiere, dann passiert das nicht.

Was könnte der Grund für dieses Problem sein?
Gibt es Beschränkungen für eine JList?
Kennt jemand das Problem und hat Abhilfe dafür?

Vielen Dank für eure Hilfe!


----------



## Marco13 (3. Jan 2011)

Ein KSKB wäre da wohl nicht verkehrt...


----------



## Gast2 (4. Jan 2011)

Oder zumindest ein bissl von deinem code bzw. ne fehlermeldung


----------



## jf (4. Jan 2011)

Ja, gerne gebe ich euch den Code - es macht aber nur Sinn, wenn ich die ganze Klasse veröffentliche.
Da dies ein paar Zeilen mehr sind, wollte ich euch damit nicht von Anfang an vor den Kopf stoßen. :noe:

Ich habe gleich noch eine static Main mit eingebaut, damit ihr es sofort ausführen könnt:


```
import java.awt.Component;
import java.awt.Font;
import java.util.Vector;

import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


@SuppressWarnings("serial")
public class ListView extends JScrollPane {

	// Just for testing purposes ////////////
	public static void main(String[] args) {
		javax.swing.JFrame win = new javax.swing.JFrame();
		ListView myComponentWrapper = new ListView();
		
		win.add(myComponentWrapper);
		win.setSize(400, 300);
		win.setVisible(true);
		
		myComponentWrapper.addItem("Test-Eintrag!");

		for(long i=0; i<10000; i++) {
			myComponentWrapper.addItem("Test-Eintrag:\t" + i);
		}
	}
	/////////////////////////////////////////
	
	
	private JList listComponent = null;


	// Constructor
	public ListView() {
		listComponent = new JList();
		listComponent.setCellRenderer(new TabRenderer());
		super.add(listComponent);
		
		DefaultListModel listModel = new DefaultListModel();
		listComponent.setModel(listModel);

		this.setViewportView(listComponent);
	}


	// methods
	public DefaultListModel getModel() {
		return (DefaultListModel) listComponent.getModel();
	}

	public void setIndex(int index) {
		listComponent.setSelectedIndex(index);
		listComponent.scrollRectToVisible(getViewportBorderBounds());
	}
	
	public void addItem(Object obj) {
		((DefaultListModel) listComponent.getModel()).addElement(obj);
	}
	
	public void addItem(int index, Object obj) {
		((DefaultListModel) listComponent.getModel()).add(index, obj);
	}
	

	public void removeItem(int index) {
		((DefaultListModel) listComponent.getModel()).removeElementAt(index);
	}
	
	public void clear() {
		try {
			((DefaultListModel) listComponent.getModel()).removeAllElements();
		} catch(ClassCastException cce) {
			//
		}
	}

	public void setListData(Vector<?> listData) {
		listComponent.setListData(listData);
	}
	
	public void setListData(Object[] listData) {
		listComponent.setListData(listData);
	}

}


@SuppressWarnings("serial")
class TabRenderer extends DefaultListCellRenderer {
    JTextArea tarea = new JTextArea();	// JTextArea supports tabs - JTextField not!
    
     public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
         super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
         tarea.setText(super.getText());
         tarea.setFont(super.getFont());
         tarea.setBackground(super.getBackground());
         tarea.setForeground(super.getForeground());
         tarea.setBorder(super.getBorder());
         tarea.setTabSize(2);
         return tarea;
     }
}
```

Spielt bitte mal etwas mit der Schleife in der static Main herum - evtl. liegt hier bereits der Hund begraben... ???:L


----------



## hansmueller (4. Jan 2011)

Hallo,

ich habe mit deinem Code etwas herumgespielt und dabei ein paar interessante Effekte erzielt.
Schlau werde ich daraus aber auch nicht.

Ich kann dir nur folgende Lösungsansätze vorschlagen:
1. Nach jedem .addItem die Liste neu zeichen lassen (mit invokeAndWait)
2. Die Liste unsichtbar setzen, befüllen, wieder sichtbar machen und neu zeichnen lassen.
3. Die Klasse TabRenderer überarbeiten.

Der Knackpunkt sind die Anzahl der Einträge. (Habe sie mal auf 1000000 erhöht)
Beim Lösungsansatz 1 braucht es sehr lange bis sich die Liste generiert. (Bei 1000000 Einträgen eindeutig zu lange. Bei ein paar 1000 jedoch noch zu gebrauchen.)
Beim Lösungsansatz 2 haut es mir nach der "neu zeichnen"-Meldung eine "java.lang.OutOfMemoryError: Java heap space"-Fehlermeldung.

Ach ja, hier noch der Code, der den OutOfMemoryError liefert.
Der TabRenderer ist jetzt nur so schnell dahingeklatscht, da ist noch keine Markierfunktion oder ähnliches implementiert. Da gibt es aber ein paar Beispiele im Netz bzw. hier im Forum.


```
import java.awt.Component;
import java.awt.Font;
import java.util.Vector;

import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class ListView extends JScrollPane
{
	static ListView myComponentWrapper;
	static long i;
	
	// Just for testing purposes ////////////
	public static void main(String[] args)
	{
		javax.swing.JFrame win = new javax.swing.JFrame();
		myComponentWrapper = new ListView();

		win.add(myComponentWrapper);
		win.setSize(400, 300);
		win.setVisible(true);

		try
		{
			SwingUtilities.invokeAndWait(new Runnable()
			{					
				@Override
				public void run()
				{
					myComponentWrapper.setVisible(false);	
					System.out.println("unsichtbar");
				}
			});
		}
		catch (Exception e)
		{				
			e.printStackTrace();
		}
		
		
		
		myComponentWrapper.addItem("Test-Eintrag!");
		
		for (i = 0; i < 1000000; i++)
		{
			myComponentWrapper.addItem("Test-Eintrag:\t" + i);
			
			if(i % 1000 == 0)
			{
				System.out.println(i);
			}
			
		}
		
		try
		{
			SwingUtilities.invokeAndWait(new Runnable()
			{					
				@Override
				public void run()
				{
					myComponentWrapper.setVisible(true);
					System.out.println("wieder sichtbar");
				}
			});
		}
		catch (Exception e)
		{				
			e.printStackTrace();
		}
		
		
		try
		{
			SwingUtilities.invokeAndWait(new Runnable()
			{					
				@Override
				public void run()
				{	
					System.out.println("neu zeichnen");
					myComponentWrapper.repaint();
					myComponentWrapper.updateUI();
					
					
				}
			});
		}
		catch (Exception e)
		{				
			e.printStackTrace();			
		}
		
		
		
	}
	// ///////////////////////////////////////

	private JList listComponent = null;

	// Constructor
	public ListView()
	{
		listComponent = new JList();
		listComponent.setCellRenderer(new TabRenderer());
		super.add(listComponent);

		DefaultListModel listModel = new DefaultListModel();
		listComponent.setModel(listModel);

		this.setViewportView(listComponent);
	}

	// methods
	public DefaultListModel getModel()
	{
		return (DefaultListModel) listComponent.getModel();
	}

	public void setIndex(int index)
	{
		listComponent.setSelectedIndex(index);
		listComponent.scrollRectToVisible(getViewportBorderBounds());
	}

	public void addItem(Object obj)
	{
		((DefaultListModel) listComponent.getModel()).addElement(obj);
	}

	public void addItem(int index, Object obj)
	{
		((DefaultListModel) listComponent.getModel()).add(index, obj);
	}

	public void removeItem(int index)
	{
		((DefaultListModel) listComponent.getModel()).removeElementAt(index);
	}

	public void clear()
	{
		try
		{
			((DefaultListModel) listComponent.getModel()).removeAllElements();
		}
		catch (ClassCastException cce)
		{
			//
		}
	}

	public void setListData(Vector<?> listData)
	{
		listComponent.setListData(listData);
	}

	public void setListData(Object[] listData)
	{
		listComponent.setListData(listData);
	}

}

@SuppressWarnings("serial")
class TabRenderer extends JTextArea implements ListCellRenderer
{
	// JTextArea supports tabs - JTextField not!

	public Component getListCellRendererComponent(JList list, Object value,
			int index, boolean isSelected, boolean cellHasFocus)
	{
		//super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
		this.setText(value.toString());
		this.setFont(list.getFont());
		this.setBackground(list.getBackground());
		this.setForeground(list.getForeground());
		this.setBorder(list.getBorder());
		this.setTabSize(2);
		return this;
	}
}
```

Vielleicht hilft es ja irgendwie weiter. Wenn es z. B. nur 100000 Einträge sind, dann funktioniert es auf meinem Rechner.

MfG
hansmueller


----------



## jf (4. Jan 2011)

Vielen Dank für deine Mühe!!!



hansmueller hat gesagt.:


> ich habe mit deinem Code etwas herumgespielt und dabei ein paar interessante Effekte erzielt.
> Schlau werde ich daraus aber auch nicht.


Da geht es dir leider wie mir... ;(



> Ich kann dir nur folgende Lösungsansätze vorschlagen:
> 1. Nach jedem .addItem die Liste neu zeichen lassen (mit invokeAndWait)
> 2. Die Liste unsichtbar setzen, befüllen, wieder sichtbar machen und neu zeichnen lassen.
> 3. Die Klasse TabRenderer überarbeiten.


Es sind leider sehr viele Einträge, welche ich darstellen muss: die 100000 werden locker gesprengt. Ein Million sind aber auch ab- und zu drin!
=> Von daher fällt Lösung 1 leider raus. Lösung 2 bringt den Out of Memory-Fehler.
Bleibt also nur noch Lösung 3 - hier habe ich aber leider keine Ahnung, was ich machen soll...
Denn TabRenderer-Code habe ich von einer netten Person aus diesem Forum, ich habe ihn nicht selbst geschrieben.

=> Hat hier evtl. jemand etwas mehr Einblick als ich und kann den CellRenderer so hinbiegen, dass er etwas resourcenschonender umgeht? Wäre echt toll!


----------



## Marco13 (5. Jan 2011)

Ich dachte auch erst an den CellRenderer, aber ... da gibt's IMHO nicht viel zu holen. Was der im Moment macht, ist ja sehr minimalistisch. Wenn es nur um die Leerzeichen für die Tabs und nicht die Ausrichtung geht, könnte man wohl auch sowas machen wie

```
class TabRenderer extends DefaultListCellRenderer {
     public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){
         super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
         String text = getText();
         while (text.contains("\t")) text = text.replace("\t", "  ");
         setText(text);
         return this;
     }
}
```
aber das dürfte nichts bringen - bzw. nichts mit dem Problem zu tun haben: Den CellRenderer gibt es nur EIN mal. Dort geht es also nicht um Megabytes, sondern um ein paar bytes. Und er zeichnet höchstens 50 Elemente nacheinander - dort geht es also nicht um Sekunden, sondern nur ein paar Mikrosekunden.

Was genau dauert denn so lange? Das Einfügen? Klar, das dauert eine Weile bei 1 Million Elementen. Das könnte man ggf. mit einem eigenen ListModel etwas schneller machen. Das DefaultListModel delegiert an einen Vector, und feuert bei jedem Hinzufügen einen Event. Man könnte ein eigenes ListModel machen, das an eine ArrayList delegiert, und eine Methode anbietet wie "addManyElements(List)", oder "addElementWithoutThrowingEvents(Object)", oder gleich "setDelegate(List)". 

Es kann aber auch gut sein, dass das kritischste Problem (nämlich der Bildschirm, der schwarz bleibt) damit zusammenhängt, dass die Methoden auf einem falschen Thread aufgerufen werden. Zumindest bei mir war es so, dass das Programm (selbst bei nur 10 Elementen) NICHTS angezeigt hat - bis ich den Inhalt der Main in SwingUtilities.invokeLater eingewickelt habe. 

Also ganz speziell in bezug auf dein Problem: Bist du sicher, dass ALLE Methoden auf der Liste NUR vom Event-Dispatch-Thread aufgerufen werden?


----------



## hansmueller (5. Jan 2011)

Hallo,

@Marco13: Ich glaube nicht, daß es an dem DefaultListModel liegt. Wenn man nämlich in meinem Code die try/catch-Blöcke mit dem "wieder sichtbar" und "neu zeichnen" auskommentiert und statt dessen die Zeile 

```
System.out.println("Anzahl Einträge: " + myComponentWrapper.getModel().getSize());
```
einfügt und das ganze dann ausführen läßt, wird die korrekte Anzahl der Einträge ausgegeben. D. h. mit dem DefaultListModel müßte eigendlich alles in Ordnung sein. Es braucht zwar eine Weile um befüllt zu werden, aber daß erklärt nicht den OutOfMemoryError.

Es könnte sein, daß das Zeichnen der Liste das eigendliche Problem ist.
Wenn man in meinem Code alle try/catch-Blöcke auskommentiert und die Zeilen 

```
win.add(myComponentWrapper);
win.setSize(400, 300);
win.setVisible(true);
```
erst nach der Schleife zum Befüllen des Listmodels einfügt, kann man sehen, das zwar das Listmodel alle Einträge hat, aber das Programm dann beim Versuch das Fenster mit seinen Komponenten zu zeichnen einen OutOfMemoryError wirft.


```
...
994000
995000
996000
997000
998000
999000
Anzahl Einträge: 1000001
Fenster sichtbar machen
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at javax.swing.text.GapContent.remove(GapContent.java:133)
	at javax.swing.text.AbstractDocument.handleRemove(AbstractDocument.java:601)
	at javax.swing.text.AbstractDocument.remove(AbstractDocument.java:576)
	at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:652)
	at javax.swing.text.JTextComponent.setText(JTextComponent.java:1693)
	at TabRenderer.getListCellRendererComponent(ListView.java:183)
	at javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1344)
	at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1294)
	at javax.swing.plaf.basic.BasicListUI.getPreferredSize(BasicListUI.java:561)
	at javax.swing.JComponent.getPreferredSize(JComponent.java:1634)
	at javax.swing.ScrollPaneLayout.layoutContainer(ScrollPaneLayout.java:769)
	at java.awt.Container.layout(Container.java:1421)
	at java.awt.Container.doLayout(Container.java:1410)
	at java.awt.Container.validateTree(Container.java:1507)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validate(Container.java:1480)
	at java.awt.Window.show(Window.java:861)
	at java.awt.Component.show(Component.java:1563)
	at java.awt.Component.setVisible(Component.java:1515)
	at java.awt.Window.setVisible(Window.java:842)
	at ListView.main(ListView.java:103)
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
	at javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1325)
	at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1294)
	at javax.swing.plaf.basic.BasicListUI.getPreferredSize(BasicListUI.java:561)
	at javax.swing.JComponent.getPreferredSize(JComponent.java:1634)
	at javax.swing.ScrollPaneLayout.layoutContainer(ScrollPaneLayout.java:769)
	at java.awt.Container.layout(Container.java:1421)
	at java.awt.Container.doLayout(Container.java:1410)
	at java.awt.Container.validateTree(Container.java:1507)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validateTree(Container.java:1513)
	at java.awt.Container.validate(Container.java:1480)
	at java.awt.Window.dispatchEventImpl(Window.java:2476)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
```

Ich habe leider keine Ahnung, für was der GapContent eigendlich gut sein soll.

Man könnte evtl. versuchen, dem Programm beim Starten mehr Speicher zuzuweisen.

MfG
hansmueller


----------



## jf (5. Jan 2011)

Hallo Marco, vielen Dank für deinen Beitrag!



Marco13 hat gesagt.:


> Wenn es nur um die Leerzeichen für die Tabs und nicht die Ausrichtung geht


Allerdings ging es mir gerade eben um die Ausrichtung (damit man keinen Augenkrebs bekommt!).



> Was genau dauert denn so lange? Das Einfügen? Klar, das dauert eine Weile bei 1 Million Elementen.


Die Geschwindigkeit an sich ist erst einmal zweitrangig. Mir geht es vor allem darum, dass es stabil läuft. Mir ist aufgefallen, dass das Anzeigen gewisser Daten manchmal recht flott geht und ein ander mal wiederum recht lange dauert - ich dachte mir, dies hat evtl. auch etwas mit dem Problem zu tun, bin mir da aber nicht sicher.
Den OutOfMemory-Fehler, welchen Herr Hansmueller gefunden hat, wegzubekommen wäre jetzt erst einmal mein größtes Anliegen.



> Das könnte man ggf. mit einem eigenen ListModel etwas schneller machen. Das DefaultListModel delegiert an einen Vector, und feuert bei jedem Hinzufügen einen Event.


Bedeutet dies, dass nach jedem Hinzufügen die Liste neu gerendert wird?



> Man könnte ein eigenes ListModel machen, das an eine ArrayList delegiert, und eine Methode anbietet wie "addManyElements(List)", oder "addElementWithoutThrowingEvents(Object)", oder gleich "setDelegate(List)".


Das Problem ist, dass ich verschiedene Arten von Daten in einer ArrayList habe und nur selten alle auf einmal anzeigen will. Ich gehe also die Liste durch und zeige nur die Elemente an, welche vom Nutzer ausgewählt wurden. Die Liste komplett an das Listmodel zu übergeben geht also nur, wenn ich mir zuvor eine Liste der gewünschten Elemente zusammenstelle - damit wäre der Geschwindigkeitsvorteil auch wieder dahin.
Ein "addElementWithoutThrowingEvents(Object)" wäre natürlich sinnvoll und der nächste logische Schritt, sobald das OutOfMemory-Problem gelöst ist. Wahrscheinlich werde dazu aber hier im Forum noch mal ein paar Fragen stellen müssen...



> Es kann aber auch gut sein, dass das kritischste Problem (nämlich der Bildschirm, der schwarz bleibt) damit zusammenhängt, dass die Methoden auf einem falschen Thread aufgerufen werden. Zumindest bei mir war es so, dass das Programm (selbst bei nur 10 Elementen) NICHTS angezeigt hat - bis ich den Inhalt der Main in SwingUtilities.invokeLater eingewickelt habe.


Mein Programm ist momentan nicht Thread-optimiert. Es läuft also in nur einem Thread.
Auf die Liste greife ich nur in der Methode updateList() mit folgender Code-Zeile zu:
this.listView.addItem( item.toString() );

Gruß, jf


----------



## jf (5. Jan 2011)

hansmueller hat gesagt.:


> Man könnte evtl. versuchen, dem Programm beim Starten mehr Speicher zuzuweisen.


Ja, evtl. ist die Liste so lang, dass einfach der Puffer für die Grafikanzeige nicht mehr ausreicht.
Aber wie kann ich das über mein Programm selber realisieren?


----------



## Marco13 (5. Jan 2011)

jf hat gesagt.:


> Bedeutet dies, dass nach jedem Hinzufügen die Liste neu gerendert wird?


Es werden alle Listener benachrichtigt - also zumindest schonmal die JList, und was die daraufhin macht, müßte man sich genauer ansehen...



> Mein Programm ist momentan nicht Thread-optimiert. Es läuft also in nur einem Thread.
> Auf die Liste greife ich nur in der Methode updateList() mit folgender Code-Zeile zu:
> this.listView.addItem( item.toString() );



Und wer ruft wann die Methode updateList() auf? Wenn das z.B. durch einen Buttonklick passiert, ist alles OK* - aber wenn es auf direktem Weg aus der main gemacht wird, nicht...

* OK ist das eigentlich nicht, weil durch das Füllen der Liste das GUI relativ lange blockiert werden kann - eigentlich sollte man das in einen eigenen Thread auslagern, der dann das Einfügen wieder auf dem EDT macht, aber ... das müßte man erst genauer analysieren.

Wenn es nur um den OutOfMemoryError geht: Die Anwendung mit
java -Xmx1000m NameDerAnwendung
starten um 1000MB Speicher zuzusichern. Wenn du von "verschiedenen Daten" redest, und andeutest, dass 1 Million Elemente nur ein Teil der ständig im Speicher liegenden Daten sind, kann es eben sein, dass der standardmäßig zugesicherte Speicher nicht reicht :bahnhof:


----------



## Gast2 (5. Jan 2011)

Vor allem kommt es auch auf die größe der Objekte an wenn du darin z.B. Bilder hälst und die Objekte ständig im Speicher rumhängen kann es schnell zu einem OutOfMemory kommen.


----------



## hansmueller (5. Jan 2011)

Hab es mal schnell getestet.

Mit 





			
				Marco13 hat gesagt.:
			
		

> -Xmx1000m


 funktioniert mein Code (Lösungsansatz 2). 
Braucht zwar ein paar Sekunden, bis die Liste dargestellt wird, aber es gibt keinen OutOfMemoryError mehr.

Es wäre interessant zu wissen, was der Speicherfresser ist und wie man das evtl. optimieren kann. Oder ob man das Zeichnen der Liste irgendwie beschleunigen kann.

MfG
hansmueller


----------



## hansmueller (5. Jan 2011)

Habe grade was interessantes herausgefunden.

Wenn man die Zeile 
	
	
	
	





```
listComponent.setCellRenderer(new TabRenderer());
```
 auskommentiert, funktioniert es ohne zusätzlichen Speicher und ist auch um einiges schneller.
Es scheint also doch irgendwie mit den ListCellRenderer zusammenzuhängen. 
Vermutlich braucht die Klasse JTextArea irgenwas, was besonderst viel Speicher frißt.

@jf: Vielleicht solltest du doch überlegen, ob du die Ausrichtung nicht auch ohne den Tab hinbekommst und den Standardrenderer benutzt.

MfG
hansmueller


----------



## Gast2 (5. Jan 2011)

Es liegt einfach an der Anzahl items die er rendern muss.
Wenn man an den items noch eine 0 hinzufügt sieht man auf der Konsole dass er die items schon längst hinzugefügt hat aber das rendern ewig brauch.

2 Sachen kosten die Performance zum Einen der selber geschrieben CellRenderer ohne ihn werden die items auch ziemlich "zügig" angezeigt (Einfach Zeile auskommentieren).

2tens werden alle Items IMMER gerendert um so mehr Items um so langsamer. Bei SWT gibts ein flag bei Listen SWT.Virtual dadurch werden nur die items gerendert die im sichtbaren Bereich sind, sowas braucht man hier auch. Keine Ahnung ob es sowas in Swing gibt. Vielleicht bei swingx oder so.


```
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;


public class ListTest {

	private static int items = 100000;
	
	public static void main(String[] args) {
		final JFrame frame = new JFrame("Test JListe");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final ListPanel listPanel = new ListPanel();
		final JButton button = new JButton("Add Persons");
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				frame.add(listPanel, BorderLayout.CENTER);
				frame.add(button, BorderLayout.SOUTH);
				frame.pack();
				frame.setVisible(true);
				
			}
		});
		
		button.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				Date now = new Date();
				for(int i = 0; i < items; i++){
					Person person = new Person();
					person.setFirstname(""+i);
					person.setLastname("\t" + (i*i));
					listPanel.addPerson(person);
				}
				Date after = new Date();
				System.out.println(after.getTime() - now.getTime());
			}
		});
	}
	
	public static class ListPanel extends JPanel{
		private JList jList;
		
		public ListPanel(){
			jList = new JList(new DefaultListModel());
			jList.setCellRenderer(new TabRenderer());
			add(new JScrollPane(jList), BorderLayout.CENTER);
		}
		
		public void addPerson(Person person){
			((DefaultListModel)jList.getModel()).addElement(person);
		}
	}
	
	public static class Person{
		private String firstname;
		private String lastname;
		public void setFirstname(String firstname) {
			this.firstname = firstname;
		}
		public String getFirstname() {
			return firstname;
		}
		public void setLastname(String lastname) {
			this.lastname = lastname;
		}
		public String getLastname() {
			return lastname;
		}
		
		@Override
		public String toString() {
			return firstname + " " + lastname;
		}
	}
	
	public static class TabRenderer extends JTextArea implements ListCellRenderer
	{
	    // JTextArea supports tabs - JTextField not!
	 
	    public Component getListCellRendererComponent(JList list, Object value,
	            int index, boolean isSelected, boolean cellHasFocus)
	    {
	        //super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
	        this.setText(value.toString());
	        this.setFont(list.getFont());
	        this.setBackground(list.getBackground());
	        this.setForeground(list.getForeground());
	        this.setBorder(list.getBorder());
	        this.setTabSize(2);
	        return this;
	    }
	}

	
}
```


Wie man schön an den Zeiten sieht dauert das hinzufügen nicht lange nur das rendern der items...

EDIT: 
Könnte noch ein 3tes Problem sein wie Marco schon gesagt hat, es wird bei jedem add ein Event geworfen und ich denke mal stark nach jedem event wird neu gerendert und ein runnable in den EDT gelegt darum ist das hinzufügen sehr schnell fertig. Aber der EDT muss danach alle Events abarbeiten und darum dauert es sehr lange.

EDIT EDIT:
Ich denke bei der Datenmenge ist eventuell auch eine JTable ohne Linien besser machst einfach 2 Spalten ohne Header und gut ist.


----------



## Marco13 (5. Jan 2011)

hansmueller hat gesagt.:


> Habe grade was interessantes herausgefunden.
> 
> Wenn man die Zeile
> 
> ...



Dass es schneller geht, ist klar: Ein JLabel hat intern kein "Document", und braucht viele Sachen ja nicht zu machen (z.B. Tabs berücksichtigen  ). Aber auf den Speicher sollte es eigentlich kaum einfluß haben. Wie gesagt, den Renderer gibt es eigentlich nur genau EIN mal ???:L :bahnhof:


----------



## Gast2 (5. Jan 2011)

Marco13 hat gesagt.:


> Dass es schneller geht, ist klar: Ein JLabel hat intern kein "Document", und braucht viele Sachen ja nicht zu machen (z.B. Tabs berücksichtigen  ). Aber auf den Speicher sollte es eigentlich kaum einfluß haben. Wie gesagt, den Renderer gibt es eigentlich nur genau EIN mal ???:L :bahnhof:



Nee das nicht aber ich hab mit meinem Bsp auch keine Speicher Probleme sondern Performance Probleme 

Also der Renderer kostet auf jeden Fall erheblich Performance.

Und der 2 te Knackpunkt bei der Performance sind die zuviele Events die ankommen.
Wenn ich ein eigenes Model schreibe geht die Sache viel schneller (3 sek.).

Wie gesagt ich tendiere zu einer JTable damit bist du viel flexibler.


```
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataListener;


public class ListTest {

	private static int items = 1000000;
	
	public static void main(String[] args) {
		final JFrame frame = new JFrame("Test JListe");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final ListPanel listPanel = new ListPanel();
		final JButton button = new JButton("Add Persons");
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				frame.add(listPanel, BorderLayout.CENTER);
				frame.add(button, BorderLayout.SOUTH);
				frame.pack();
				frame.setVisible(true);
				
			}
		});
		
		button.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				final Date now = new Date();
				List<Person> persons = new ArrayList<Person>();
				for(int i = 0; i < items; i++){
					Person person = new Person();
					person.setFirstname(""+i);
					person.setLastname("\t" + (i*i));
//					listPanel.addPerson(person);
				persons.add(person);
				}
				listPanel.addPersons(persons);
				
				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						final Date after = new Date();
						System.out.println(after.getTime() - now.getTime());
						
					}
				});
				
			}
		});
	}
	
	public static class ListPanel extends JPanel{
		private JList jList;
		
		public ListPanel(){
			jList = new JList(new MyListModel());
//			jList.setCellRenderer(new TabRenderer());
			add(new JScrollPane(jList), BorderLayout.CENTER);
		}
		
		public void addPerson(Person person){
			((DefaultListModel)jList.getModel()).addElement(person);
		}
		
		public void addPersons(List<?> persons){
			((MyListModel)jList.getModel()).addElements(persons);
		}
		
		public static class MyListModel extends AbstractListModel{

			private ArrayList<Object> delegate = new ArrayList<Object>();

			@Override
			public Object getElementAt(int index) {
				return delegate.get(index);
			}

			@Override
			public int getSize() {
				return delegate.size();
			}
	

			/**
			 * Adds the specified component to the end of this list.
			 * 
			 * @param obj
			 *            the component to be added
			 * @see Vector#addElement(Object)
			 */
			public void addElement(Object obj) {
				int index = delegate.size();
				delegate.add(obj);
				fireIntervalAdded(this, index, index);
			}
			
			/**
			 * Adds the specified component to the end of this list.
			 * 
			 * @param obj
			 *            the component to be added
			 * @see Vector#addElement(Object)
			 */
			public void addElements(List<?> objs) {
				int index = delegate.size();
				delegate.addAll(objs);
				fireIntervalAdded(this, index, delegate.size());
			}
			

		}
	}
	
	public static class Person{
		private String firstname;
		private String lastname;
		public void setFirstname(String firstname) {
			this.firstname = firstname;
		}
		public String getFirstname() {
			return firstname;
		}
		public void setLastname(String lastname) {
			this.lastname = lastname;
		}
		public String getLastname() {
			return lastname;
		}
		
		@Override
		public String toString() {
			return firstname + " " + lastname;
		}
	}
	
	public static class TabRenderer extends JTextArea implements ListCellRenderer
	{
	    // JTextArea supports tabs - JTextField not!
	 
	    public Component getListCellRendererComponent(JList list, Object value,
	            int index, boolean isSelected, boolean cellHasFocus)
	    {
	        //super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
	        this.setText(value.toString());
	        this.setFont(list.getFont());
	        this.setBackground(list.getBackground());
	        this.setForeground(list.getForeground());
	        this.setBorder(list.getBorder());
	        this.setTabSize(2);
	        return this;
	    }
	}

	
}
```


----------



## Gast2 (5. Jan 2011)

Zum Denkanstoss ich würde es so ungefähr machen...

```
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;


public class ListTest {

	private static int items = 1000000;
	
	public static void main(String[] args) {
		final JFrame frame = new JFrame("Test JListe");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final ListPanel listPanel = new ListPanel();
		final JButton button = new JButton("Add Persons");
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				frame.add(listPanel, BorderLayout.CENTER);
				frame.add(button, BorderLayout.SOUTH);
				frame.pack();
				frame.setVisible(true);
				
			}
		});
		
		button.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				final Date now = new Date();
				List<Person> persons = new ArrayList<Person>();
				for(int i = 0; i < items; i++){
					Person person = new Person();
					person.setFirstname(""+i);
					person.setLastname("" + (i*i));
//					listPanel.addPerson(person);
					persons.add(person);
				}
				listPanel.addPersons(persons);
				
				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						final Date after = new Date();
						System.out.println(after.getTime() - now.getTime());
						
					}
				});
				
			}
		});
	}
	
	public static class ListPanel extends JPanel{
		private MyTableModel model;
		
		public ListPanel(){
			model = new MyTableModel();
			JTable jTable = new JTable(model);
			jTable.setTableHeader(null);
			jTable.setShowGrid(false);
//			jList.setCellRenderer(new TabRenderer());
			add(new JScrollPane(jTable), BorderLayout.CENTER);
		}
		
		public void addPerson(Person person){
			model.addPerson(person);
		}
		
		public void addPersons(List<Person> persons){
			model.addPersons(persons);
		}
		
		public static class MyTableModel extends AbstractTableModel{

			private List<Person> row  = new ArrayList<Person>();
			
			public void addPerson(Person person){
				int index = row.size();
				row.add(person);
				fireTableRowsInserted(index, index + 1);
			}
			
			public void addPersons(List<Person> persons){
				int index = row.size();
				row.addAll(persons);
				fireTableRowsInserted(index, index + persons.size());
			}
			
			@Override
			public int getColumnCount() {
				return 2;
			}

			@Override
			public int getRowCount() {
				return row.size();
			}

			@Override
			public Object getValueAt(int rowIndex, int columnIndex) {
				Person person = row.get(rowIndex);
				if(columnIndex == 0){
					return person.getFirstname();
				}else if(columnIndex == 1){
					return person.getLastname();
				}
				return null;
			}
			
		}
	}
	
	public static class Person{
		private String firstname;
		private String lastname;
		public void setFirstname(String firstname) {
			this.firstname = firstname;
		}
		public String getFirstname() {
			return firstname;
		}
		public void setLastname(String lastname) {
			this.lastname = lastname;
		}
		public String getLastname() {
			return lastname;
		}
	}
	
}
```


----------



## jf (5. Jan 2011)

Erst einmal vielen Dank an euch alle, dass ihr euch so um mein Problem bemüht!!!



SirWayne hat gesagt.:


> Zum Denkanstoss ich würde es so ungefähr machen...


Dein Code gefällt mir - er ist wirklich verdammt schnell!

Allerdings liegt das Problem bei mir so:
Ich habe viele Datensatz-Klassen, welche von einem abstracten Datensatz erben. Der Nutzer kann beliebige Kombinationen von Datensatz-Arten auswählen welche angezeigt werden sollen. Jede der Klassen hat eine toString()-Methode, welche ich für das Füllen der Liste verwende.
In jedem Datensatz stecken verschieden viele Werte (Zahlen, Datumsangaben, längere Texte, Byte-Arrays... halt alles Mögliche). Für den einen Datensatz würde ich nur 1 Spalte für den anderen allerdings 20 benötigen. - Eine JTable ist daher leider etwas ungünstig.
Ich wöllte außerdem nur sehr ungern an dieser Stelle einen Fallunterschiedung machen, und alle Elemente jedes Datensatztypes einzeln auslesen. Ich könnte zwar den String welche ich jetzt anzeige am Tab aufsplitten und die Array-Elemente auf die Spalten verteilen, aber dann werden die längeren Texte immer vom rechten Spaltenrand abgeschnitten. Zudem sind einige Datensätze so lang, dass sie umgebrochen werden: die JList stellt das wunderbar als mehrzeiligen Eintrag dar - bei der JTable habe ich da so meine Befürchtungen. Hinzu kommt, dass dies sicher wieder etwas Geschwindigkeit kosten würde.
Daher Stelle ich alles in einer Zeile dar. Damit gleichartige Datensätze zumindest grob ausgerichtet sind, verwende ich den TabRenderer (natürlich gibt es hier und da ein paar Sprünge, aber das ist ok).

Ich habe mir auch schon überlegt, alles mit Monospace-Texten zu machen, um es so ausrichten zu können, aber dann würden die Zeilen leider so lang werden, dass man ständig horizontal rollen muss.

Die Ideallösung wäre wohl ein
- eigens ListModel
- ein eigener Thread für das rendern der Liste
- ein leistungsfähigere Möglichkeit den Tabsprung zu beachten

Leider habe ich bei allem noch keine Erfahrung. 
Ich wäre daher sehr dankbar, wenn euch noch etwas dazu einfällt.

Nochmal danke,
jf


----------



## jf (5. Jan 2011)

PS:



Marco13 hat gesagt.:


> Und wer ruft wann die Methode updateList() auf? Wenn das z.B. durch einen Buttonklick passiert, ist alles OK* - aber wenn es auf direktem Weg aus der main gemacht wird, nicht...


Wenn ein Pfad als Parameter beim Start des Programmes übergeben wird, dann wird die Datei geladen.
Die Laderoutine löst updateList() aus. - In aller Regel ziehe ich aber die Dateien per Drag'n'Drop auf die Oberfläche, um sie zu laden. Die Liste wird außerdem aktualisiert, wenn der Nutzer auf diverse Checkboxen klickt (je nach dem, welche Datensatz-Art angezeigt werden soll)



> * OK ist das eigentlich nicht, weil durch das Füllen der Liste das GUI relativ lange blockiert werden kann - eigentlich sollte man das in einen eigenen Thread auslagern, der dann das Einfügen wieder auf dem EDT macht, aber ... das müßte man erst genauer analysieren.


Da hast du Recht. Aber zuerst sollte es erst einmal stabil laufen.



> Wenn es nur um den OutOfMemoryError geht: Die Anwendung mit
> java -Xmx1000m NameDerAnwendung
> starten um 1000MB Speicher zuzusichern. Wenn du von "verschiedenen Daten" redest, und andeutest, dass 1 Million Elemente nur ein Teil der ständig im Speicher liegenden Daten sind, kann es eben sein, dass der standardmäßig zugesicherte Speicher nicht reicht :bahnhof:


Naja, je nach dem, wie groß die Datei ist, welche gerade geladen wurde, steigt der Speicherbedarf meiner Anwendung schon mal um ein MB. Nach jedem Lade-Vorgang rufe ich den GC auf, damit alte Daten wieder aus dem Speicher verschwinden. Dem TaskManager nach, funktioniert dies auch wunderbar.
Woher das OutOfMemory kommt, ist mir daher irgendwie ein Rätsel.


----------



## Gast2 (6. Jan 2011)

jf hat gesagt.:


> Daher Stelle ich alles in einer Zeile dar. Damit gleichartige Datensätze zumindest grob ausgerichtet sind, verwende ich den TabRenderer (natürlich gibt es hier und da ein paar Sprünge, aber das ist ok).
> jf



In der Tabelle kann man auch einen Cellrenderer setzen geht alles mit der Tabelle musst dich halt damit auseinander setzen. Und das mit deiner Klassen und der toString Methode sieht eher nach Arichtekur Probleme aus. Müssen deine Objekte halt ein gemeinsames Interface oder sowas implementieren.

```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.View;

public class ListTest {

	private static int items = 1000000;

	public static void main(String[] args) {
		final JFrame frame = new JFrame("Test JListe");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final ListPanel listPanel = new ListPanel();
		final JButton button = new JButton("Add Persons");
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				frame.add(listPanel, BorderLayout.CENTER);
				frame.add(button, BorderLayout.SOUTH);
				frame.pack();
				frame.setVisible(true);

			}
		});

		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				final Date now = new Date();
				List<Person> persons = new ArrayList<Person>();
				for (int i = 0; i < items; i++) {
					Person person = new Person();
					person.setFirstname("" + i);
					person.setLastname("" + (i * i) + "nächste zeile langer langer langer langer langer Text");
					// listPanel.addPerson(person);
					persons.add(person);
				}
				listPanel.addPersons(persons);

				SwingUtilities.invokeLater(new Runnable() {

					@Override
					public void run() {
						final Date after = new Date();
						System.out.println(after.getTime() - now.getTime());

					}
				});

			}
		});
	}

	public static class ListPanel extends JPanel {
		private MyTableModel model;

		public ListPanel() {
			model = new MyTableModel();
			JTable jTable = new JTable(model);
			jTable.getColumnModel().getColumn(1).setCellRenderer(new TextTableCellRenderer());
			jTable.setTableHeader(null);
			jTable.setShowGrid(false);
			// jList.setCellRenderer(new TabRenderer());
			add(new JScrollPane(jTable), BorderLayout.CENTER);
		}

		public void addPerson(Person person) {
			model.addPerson(person);
		}

		public void addPersons(List<Person> persons) {
			model.addPersons(persons);
		}

		public static class MyTableModel extends AbstractTableModel {

			private List<Person> row = new ArrayList<Person>();

			public void addPerson(Person person) {
				int index = row.size();
				row.add(person);
				fireTableRowsInserted(index, index + 1);
			}

			public void addPersons(List<Person> persons) {
				int index = row.size();
				row.addAll(persons);
				fireTableRowsInserted(index, index + persons.size());
			}

			@Override
			public int getColumnCount() {
				return 2;
			}

			@Override
			public int getRowCount() {
				return row.size();
			}

			@Override
			public Object getValueAt(int rowIndex, int columnIndex) {
				Person person = row.get(rowIndex);
				if (columnIndex == 0) {
					return person.getFirstname();
				} else if (columnIndex == 1) {
					return person.getLastname();
				}
				return null;
			}

		}
	}

	private static class TextTableCellRenderer extends JTextArea implements
			TableCellRenderer {
		protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

		public TextTableCellRenderer() {
			super();
			setWrapStyleWord(true);
			setLineWrap(true);

			setBorder(noFocusBorder);
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			if (isSelected) {
				super.setForeground(table.getSelectionForeground());
				super.setBackground(table.getSelectionBackground());
			} else {
				super.setForeground(table.getForeground());
				super.setBackground(table.getBackground());
			}

			setFont(table.getFont());

			if (hasFocus) {
				setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
				if (!isSelected && table.isCellEditable(row, column)) {
					Color col;
					col = UIManager.getColor("Table.focusCellForeground");
					if (col != null) {
						super.setForeground(col);
					}
					col = UIManager.getColor("Table.focusCellBackground");
					if (col != null) {
						super.setBackground(col);
					}
				}
			} else {
				setBorder(noFocusBorder);
			}

			setEnabled(table.isEnabled());

			setValue(table, row, column, value);

			return this;
		}

		protected void setValue(JTable table, int row, int column, Object value) {
			if (value != null) {
				String text = value.toString();
				setText(text);

				View view = getUI().getRootView(this);
				view.setSize((float) table.getColumnModel().getColumn(column)
						.getWidth() - 3, -1);
				float y = view.getPreferredSpan(View.Y_AXIS);
				int h = (int) Math.ceil(y + 3);

				if (table.getRowHeight(row) != h) {
					table.setRowHeight(row, h);
				}
			} else {
				setText("");
			}
		}
	}

	public static class Person {
		private String firstname;
		private String lastname;

		public void setFirstname(String firstname) {
			this.firstname = firstname;
		}

		public String getFirstname() {
			return firstname;
		}

		public void setLastname(String lastname) {
			this.lastname = lastname;
		}

		public String getLastname() {
			return lastname;
		}
	}

}
```


----------



## Gast2 (6. Jan 2011)

Oder kannst auch alles in einer Column machen und mit der toString methode....
Aber beim umbrechen musst dir dann noch was überlegen dass die untereinander stehen.


```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.View;

public class ListTest {

	private static int items = 1000000;

	public static void main(String[] args) {
		final JFrame frame = new JFrame("Test JListe");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final ListPanel listPanel = new ListPanel();
		final JButton button = new JButton("Add Persons");
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				frame.add(listPanel, BorderLayout.CENTER);
				frame.add(button, BorderLayout.SOUTH);
				frame.pack();
				frame.setVisible(true);

			}
		});

		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				final Date now = new Date();
				List<Person> persons = new ArrayList<Person>();
				for (int i = 0; i < items; i++) {
					Person person = new Person();
					person.setFirstname("" + i);
					person.setLastname("" + (i * i) + "nächste zeile langer langer langer langer langer Text");
					// listPanel.addPerson(person);
					persons.add(person);
				}
				listPanel.addPersons(persons);

				SwingUtilities.invokeLater(new Runnable() {

					@Override
					public void run() {
						final Date after = new Date();
						System.out.println(after.getTime() - now.getTime());

					}
				});

			}
		});
	}

	public static class ListPanel extends JPanel {
		private MyTableModel model;

		public ListPanel() {
			model = new MyTableModel();
			JTable jTable = new JTable(model);
			jTable.getColumnModel().getColumn(0).setCellRenderer(new TextTableCellRenderer());
			jTable.setTableHeader(null);
			jTable.setShowGrid(false);
			// jList.setCellRenderer(new TabRenderer());
			add(new JScrollPane(jTable), BorderLayout.CENTER);
		}

		public void addPerson(Person person) {
			model.addPerson(person);
		}

		public void addPersons(List<Person> persons) {
			model.addPersons(persons);
		}

		public static class MyTableModel extends AbstractTableModel {

			private List<Person> row = new ArrayList<Person>();

			public void addPerson(Person person) {
				int index = row.size();
				row.add(person);
				fireTableRowsInserted(index, index + 1);
			}

			public void addPersons(List<Person> persons) {
				int index = row.size();
				row.addAll(persons);
				fireTableRowsInserted(index, index + persons.size());
			}

			@Override
			public int getColumnCount() {
				return 1;
			}

			@Override
			public int getRowCount() {
				return row.size();
			}

			@Override
			public Object getValueAt(int rowIndex, int columnIndex) {
				return row.get(rowIndex).toString();
			}

		}
	}

	private static class TextTableCellRenderer extends JTextArea implements
			TableCellRenderer {
		protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

		public TextTableCellRenderer() {
			super();
			setWrapStyleWord(true);
			setLineWrap(true);

			setBorder(noFocusBorder);
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			if (isSelected) {
				super.setForeground(table.getSelectionForeground());
				super.setBackground(table.getSelectionBackground());
			} else {
				super.setForeground(table.getForeground());
				super.setBackground(table.getBackground());
			}

			setFont(table.getFont());

			if (hasFocus) {
				setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
				if (!isSelected && table.isCellEditable(row, column)) {
					Color col;
					col = UIManager.getColor("Table.focusCellForeground");
					if (col != null) {
						super.setForeground(col);
					}
					col = UIManager.getColor("Table.focusCellBackground");
					if (col != null) {
						super.setBackground(col);
					}
				}
			} else {
				setBorder(noFocusBorder);
			}

			setEnabled(table.isEnabled());

			setValue(table, row, column, value);

			return this;
		}

		protected void setValue(JTable table, int row, int column, Object value) {
			if (value != null) {
				String text = value.toString();
				setText(text);

				View view = getUI().getRootView(this);
				view.setSize((float) table.getColumnModel().getColumn(column)
						.getWidth() - 3, -1);
				float y = view.getPreferredSpan(View.Y_AXIS);
				int h = (int) Math.ceil(y + 3);

				if (table.getRowHeight(row) != h) {
					table.setRowHeight(row, h);
				}
			} else {
				setText("");
			}
		}
	}

	public static class Person {
		private String firstname;
		private String lastname;

		public void setFirstname(String firstname) {
			this.firstname = firstname;
		}

		public String getFirstname() {
			return firstname;
		}

		public void setLastname(String lastname) {
			this.lastname = lastname;
		}

		public String getLastname() {
			return lastname;
		}
		
		@Override
		public String toString() {
			return firstname + "\t" + lastname;
		}
	}

}
```


----------



## jf (7. Jan 2011)

Hallo SirWayne, vielen Dank für deine Beiträge!



SirWayne hat gesagt.:


> Oder kannst auch alles in einer Column machen und mit der toString methode....


Ja, daran habe ich auch schon gedacht.
Es gibt nur noch zwei Dinge, welche mir etwas missfallen:
- Die Tablänge ist standardmäßig recht lang und verschwendet daher zuviel Platz, bei der JTextArea ich mittels 
	
	
	
	





```
tarea.setTabSize(2);
```
 die Tablänge einstellen. Ist dies bei denem CellRenderer auch möglich?
- Für lange Datensätze, welche die Breite sprengen, müsste noch eine horizontale Rollleiste her. Dies passiert allerdings nicht automatisch. Kann ich die JScrollPane dazu bringen, sie anzuzeigen?



SirWayne hat gesagt.:


> Aber beim umbrechen musst dir dann noch was überlegen dass die untereinander stehen.


Ja, ich breche bei bestimmten Datensätzen bereits in der toString()-Methode per "\n" so um, dass es ein gutes Bild abgibt.
Der CellRenderer soll nicht automatisch umbrechen!
=> Aber das geht ja in deinem Beispiel.

Letztendlich wird aber auch dadurch nur das Problem hinausgeschoben: wenn ich 10.000.000 Elemente mit deiner Variante anzeigen lasse, dann erhalte ich auch wiederrum den "Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space"-Fehler.

Vielleicht sollte ich einfach nur den Fehler abfangen, dass hinzufügen von Elementen abbrechen und dem Nutzer mitteilen, dass es ein Speicherproblem gibt...


----------



## hansmueller (7. Jan 2011)

@jf: Mußt du den alle 10 000 000 Einträge auf einmal in der Liste darstellen? Würde es nicht reichen, zuerst 10 000 Einträge darzustellen und mit ein paar seperaten Buttens neben/unter/über der Liste den Anwender die Möglichkeit zu geben, auf die nächsten/vorherigen 10 000 Einträge weiter/zurückzuschalten? So ähnlich wie in diesem Forum. Wenn ein Thema eine gewisse Länge erreicht hat, wird eine neue Unterseite erstellt. 
So dürfte das ganze wesendlich performanter sein und du hast keine nervenden Fehlermeldungen. :toll:

Wer zur Hölle scrollt schon in einer Liste mit 10 000 000 Einträgen? ???:L

MfG
hansmueller


----------



## Gast2 (7. Jan 2011)

Joa wie gesagt in SWT gibt es dass nur die angezeigten Elemente dargstellt werden, sowas sollte es auch in Swing geben müsste man halt Recherieren...

Zu deinem Speicherproblem nimmt einfach einen Profiler dann siehst du wo dein Speicher leak ist.


----------



## jf (7. Jan 2011)

hansmueller hat gesagt.:


> @jf: Mußt du den alle 10 000 000 Einträge auf einmal in der Liste darstellen? Würde es nicht reichen, zuerst 10 000 Einträge darzustellen und mit ein paar seperaten Buttens neben/unter/über der Liste den Anwender die Möglichkeit zu geben, auf die nächsten/vorherigen 10 000 Einträge weiter/zurückzuschalten? So ähnlich wie in diesem Forum. Wenn ein Thema eine gewisse Länge erreicht hat, wird eine neue Unterseite erstellt.
> So dürfte das ganze wesendlich performanter sein und du hast keine nervenden Fehlermeldungen. :toll:
> 
> Wer zur Hölle scrollt schon in einer Liste mit 10 000 000 Einträgen? ???:L


Naja, die Anmerkung, dass bei 10.000.000 Einträgen der Fehler trotzdem noch das ist, habe ich nur gemacht, weil ich gerne eine richtig saubere Lösung hätte.

Klar ist auch, dass irgendwann jeder Speicher mal voll ist - eine Fehlerbehandlung wäre daher einfach nur konsequent.
Problem hierbei: ich habe keine Ahnung, wie ich diesen OutOfMemory-Fehler abfangen könnte. Mit folgendem geht es zumindest schon mal nicht:


```
try {
                	row.addAll(persons);
                } catch(Exception e) {
                	System.out.println("OutOfMemory!");
                	System.out.println(e.toString());
                }
```

Es ist so, dass vor allem die ersten und die letzten Datensätze von Interesse sind. - Werden alle in einer Liste dargestellt, kann man schnell unter Verwendung der vertikalen Rollleiste zwischen beiden interessanten Stellen hin- und herrollen.

Wir werden die 1.000.000 Datensätze in nur sehr seltenen Fällen erreichen 10.000.000 ist fast ausgeschlossen, von daher wäre nur eine Liste durchaus praktikabel.


----------



## jf (7. Jan 2011)

SirWayne hat gesagt.:


> Joa wie gesagt in SWT gibt es dass nur die angezeigten Elemente dargstellt werden, sowas sollte es auch in Swing geben müsste man halt Recherieren...


Ok, dass werde ich tun. Wie heißt das ganze bei SWT? (könntest du mir einen Methoden-Bezeichner o. ä. geben?)



SirWayne hat gesagt.:


> Zu deinem Speicherproblem nimmt einfach einen Profiler dann siehst du wo dein Speicher leak ist.


Ist das so etwas wie ein Debugger?
Kannst du mir hier evtl. eine Empfehlung geben? Was hat sich bei dir bewährt?


----------



## Gast2 (7. Jan 2011)

jf hat gesagt.:


> Ok, dass werde ich tun. Wie heißt das ganze bei SWT? (könntest du mir einen Methoden-Bezeichner o. ä. geben?)



In SWT ist es ein Style und heißt SWT.Virtual!



jf hat gesagt.:


> Ist das so etwas wie ein Debugger?
> Kannst du mir hier evtl. eine Empfehlung geben? Was hat sich bei dir bewährt?



Es gibt einem JDK dabei. Ich habe ne zeit lang JRat für Performance Sachen benutzt. Da gibst du in deinem VM argumente beim start -javaagent:lib/java/jrat/shiftone-jrat.jar mit lädst die lib vorher herunter. Und wenn du dein Programm beendet hast siehst du welche Methode wieviel Zeit verbraten hat. Musst einfach mal ein bischen googlen.


----------



## jf (7. Jan 2011)

SirWayne hat gesagt.:


> In SWT ist es ein Style und heißt SWT.Virtual!


Meinst du mit Style ein LookAndFeel?



SirWayne hat gesagt.:


> Es gibt einem JDK dabei. Ich habe ne zeit lang JRat für Performance Sachen benutzt. Da gibst du in deinem VM argumente beim start -javaagent:lib/java/jrat/shiftone-jrat.jar mit lädst die lib vorher herunter. Und wenn du dein Programm beendet hast siehst du welche Methode wieviel Zeit verbraten hat. Musst einfach mal ein bischen googlen.


Ok, werde ich tun.
Also der, welcher beim JDK dabei ist, ist nicht der JRat von dem du hier sprichst. Habe ich das richtig verstanden?


----------



## Gast2 (7. Jan 2011)

jf hat gesagt.:


> Meinst du mit Style ein LookAndFeel?
> 
> 
> Ok, werde ich tun.
> Also der, welcher beim JDK dabei ist, ist nicht der JRat von dem du hier sprichst. Habe ich das richtig verstanden?



SWT ist ein eigenes Toolkit und ist was total anderes als Swing.

Google doch einfach mal nach den Begriffen SWT/JFace, Profiler findest jede Menge im Netz


----------

