JList verursacht Absturz

jf

Bekanntes Mitglied
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!
 

jf

Bekanntes Mitglied
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:

Java:
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

Bekanntes Mitglied
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.

Java:
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

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

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

Top Contributor
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
Code:
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

Bekanntes Mitglied
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
Java:
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
Java:
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.

Code:
...
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

Bekanntes Mitglied
Hallo Marco, vielen Dank für deinen Beitrag!

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
 

Marco13

Top Contributor
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:
 
G

Gast2

Gast
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

Bekanntes Mitglied
Hab es mal schnell getestet.

Mit
Marco13 hat gesagt.:
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

Bekanntes Mitglied
Habe grade was interessantes herausgefunden.

Wenn man die Zeile
Java:
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
 
G

Gast2

Gast
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.

Java:
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.
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
Habe grade was interessantes herausgefunden.

Wenn man die Zeile
Java:
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.

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:
 
G

Gast2

Gast
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.

Java:
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;
	    }
	}

	
}
 
Zuletzt bearbeitet von einem Moderator:
G

Gast2

Gast
Zum Denkanstoss ich würde es so ungefähr machen...
Java:
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

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

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

Bekanntes Mitglied
PS:

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.
 
G

Gast2

Gast
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.
Java:
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;
		}
	}

}
 
Zuletzt bearbeitet von einem Moderator:
G

Gast2

Gast
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.

Java:
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

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

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
Code:
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?

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

Bekanntes Mitglied
@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
 
G

Gast2

Gast
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

Bekanntes Mitglied
@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:

Java:
                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

Bekanntes Mitglied
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?)

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?
 
G

Gast2

Gast
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!

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

Bekanntes Mitglied
In SWT ist es ein Style und heißt SWT.Virtual!
Meinst du mit Style ein LookAndFeel?

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?
 
G

Gast2

Gast
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
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Jlist füllen AWT, Swing, JavaFX & SWT 4
H JList.setEnabled(false) - Text soll nicht grau sein AWT, Swing, JavaFX & SWT 2
BananenBrötchen Swing Arraylist in jList anzeigen AWT, Swing, JavaFX & SWT 6
J Swing JList wird nicht angezeigt AWT, Swing, JavaFX & SWT 6
A Swing JList zu JTree AWT, Swing, JavaFX & SWT 11
G Eingabe in JTextField in Array speichern und in JList ausgeben AWT, Swing, JavaFX & SWT 7
cezary Choice, JComboBox oder JList AWT, Swing, JavaFX & SWT 5
J JList scrollen AWT, Swing, JavaFX & SWT 3
Neumi5694 Swing JList - Klick abfangen AWT, Swing, JavaFX & SWT 3
G Swing Swing Binding JList funktioniert nicht AWT, Swing, JavaFX & SWT 5
M Swing JList Auswahl-GUI-Konzept AWT, Swing, JavaFX & SWT 9
C JList: Bestimmte Objekte ausschließen AWT, Swing, JavaFX & SWT 1
F JList aktualisiert nicht AWT, Swing, JavaFX & SWT 24
F JList auffüllen aus Array von Instanzen AWT, Swing, JavaFX & SWT 4
F Event wenn Maus eine JList verlässt AWT, Swing, JavaFX & SWT 13
F JList bei Windowbuilder AWT, Swing, JavaFX & SWT 8
F JList zeigt keine Einträge mehr AWT, Swing, JavaFX & SWT 1
P JList erstellen AWT, Swing, JavaFX & SWT 4
G DefaultListModel für JList AWT, Swing, JavaFX & SWT 2
P Einer JList mit eigenem ListModel einen Eintrag hinzfügen AWT, Swing, JavaFX & SWT 5
P Übergeben von ListModel an nichtparametrisierte JList AWT, Swing, JavaFX & SWT 8
M Ein Element in mehreren JList selektieren AWT, Swing, JavaFX & SWT 5
J Swing Probleme mit ListSelectionListener(), Inhalte der JList werden gelöscht? AWT, Swing, JavaFX & SWT 6
Z Swing Variable eines Objekt aus Hashmap in JList anzeigen AWT, Swing, JavaFX & SWT 1
Nabbit Über JList kein JPopupMenu AWT, Swing, JavaFX & SWT 2
O Swing JList beim Klicken in der GUI erstellen AWT, Swing, JavaFX & SWT 6
C Swing JList zeigt keine neuen Einträge an AWT, Swing, JavaFX & SWT 1
B Swing Auf JList-Elemente aus einer anderen Klasse zugreifen AWT, Swing, JavaFX & SWT 1
T Generische Klasse in JList über DefaultListModel AWT, Swing, JavaFX & SWT 1
T Swing WindowBuilder: JList wird nicht angezeigt AWT, Swing, JavaFX & SWT 2
C Button Text in JList neue Zeile AWT, Swing, JavaFX & SWT 6
C Hilfe bei JSpinner und JList AWT, Swing, JavaFX & SWT 1
D Swing ArrayList in Jlist/ List oder Array in JList/List AWT, Swing, JavaFX & SWT 5
S JList ist leer, aber DefaultListModel hat die Daten? AWT, Swing, JavaFX & SWT 9
M JList wird angezeigt, JTable (DefaultTableModel) nicht AWT, Swing, JavaFX & SWT 7
H Swing JList/JTable mit JButton, JTextField, Image, JComboBox und JLable AWT, Swing, JavaFX & SWT 2
D Swing Jlist Größe während der Laufzeit verändern. AWT, Swing, JavaFX & SWT 11
Q JList Update Problem AWT, Swing, JavaFX & SWT 1
W Sichtbarer Bereich JScrollPane vs. JList AWT, Swing, JavaFX & SWT 3
N AWT Markierte Zeile jList aus mySQL DB löschen AWT, Swing, JavaFX & SWT 2
S JList repaint AWT, Swing, JavaFX & SWT 1
F Swing JTextField in JList (Funktion beibehalten) AWT, Swing, JavaFX & SWT 2
H JList AWT, Swing, JavaFX & SWT 5
B Swing Bild von Webseite herunterladen, diese dann in JList anzeigen. AWT, Swing, JavaFX & SWT 1
B Swing Dynamische JList mit Bildern AWT, Swing, JavaFX & SWT 4
D JList&JButton erst nach Resize des JFRame sichtbar AWT, Swing, JavaFX & SWT 2
S JList Index finden AWT, Swing, JavaFX & SWT 6
3 Swing JList-Inhalt als verschlüsselte Datei schreiben und lesen AWT, Swing, JavaFX & SWT 1
N JComponenten in JList oder doch anders? AWT, Swing, JavaFX & SWT 0
S Swing JList, ArrayList + DefaultModel AWT, Swing, JavaFX & SWT 5
B Tabelleneinträge in JList anzeigen AWT, Swing, JavaFX & SWT 5
P JList/JScroolPane Text verschieben und text wrappen AWT, Swing, JavaFX & SWT 3
D Swing JList keine Selektionsänderung bei "dragged-Maus" AWT, Swing, JavaFX & SWT 4
M JList Model filtern AWT, Swing, JavaFX & SWT 4
K Swing JList - Element richtig hinzufügen AWT, Swing, JavaFX & SWT 4
T Swing Elemente der JList überlappend darstellen AWT, Swing, JavaFX & SWT 5
P Swing JList mit Objekten - Name anzeigen AWT, Swing, JavaFX & SWT 5
Ollek JList - neuer Eintrag in ArrayList dadurch keine Anzeige der Objekte AWT, Swing, JavaFX & SWT 18
K Mehrfachselektierungen in einer JList AWT, Swing, JavaFX & SWT 6
L Swing Component alignment innerhalb einer JList AWT, Swing, JavaFX & SWT 3
M Swing JList Listener AWT, Swing, JavaFX & SWT 20
M Accelerator zu JMenuItem des JPopupMenu einer JList AWT, Swing, JavaFX & SWT 2
S Swing JList wird nicht angezeigt AWT, Swing, JavaFX & SWT 3
M Element aus JList eines JFileChooser entfernen AWT, Swing, JavaFX & SWT 3
E selektierter Datensatz in JList als String Datentyp AWT, Swing, JavaFX & SWT 23
E selektiertes Element aus JList zurückgeben AWT, Swing, JavaFX & SWT 7
E JList /DefaultListModel mit Datenbank verbinden AWT, Swing, JavaFX & SWT 10
L SQL & Jtable o. JList AWT, Swing, JavaFX & SWT 5
K Swing JList und veränderte CellRenderer AWT, Swing, JavaFX & SWT 8
B JList + ListCellRenderer AWT, Swing, JavaFX & SWT 5
B JList -> Disabled AWT, Swing, JavaFX & SWT 6
J JList, Anzeige aktualisieren AWT, Swing, JavaFX & SWT 7
I Jlist isselectionEmpty AWT, Swing, JavaFX & SWT 4
I Jlist dynamisch aus Datenbank füllen AWT, Swing, JavaFX & SWT 14
R Swing unbegrenzte Anzahl vonEinträgen in JList AWT, Swing, JavaFX & SWT 2
MiMa Dateien aus Ordner in einer JList Ausgeben AWT, Swing, JavaFX & SWT 32
P Elemente einer JList ausgeben AWT, Swing, JavaFX & SWT 11
M JList mit 2-zeiligen Objekten füllen AWT, Swing, JavaFX & SWT 7
S JList mit Einträgen versehen schlägt fehl AWT, Swing, JavaFX & SWT 4
F Swing ArrayList in JList anzeigen und Objekte auswählen AWT, Swing, JavaFX & SWT 2
B JList zeigt Elemente nicht an AWT, Swing, JavaFX & SWT 3
S Fragen zu JList AWT, Swing, JavaFX & SWT 3
B Aktualisierung einer JList AWT, Swing, JavaFX & SWT 5
W JList valueChanges weiterleiten? AWT, Swing, JavaFX & SWT 14
D JButton-Click in JList mit Panels (auf dem der Button ist) AWT, Swing, JavaFX & SWT 6
E Jlist nur mit Klasse? AWT, Swing, JavaFX & SWT 5
D Swing JList <-> AdapterModel <-> Model AWT, Swing, JavaFX & SWT 4
1 JList - Elemente mit rechter Maustaste wählen AWT, Swing, JavaFX & SWT 6
M JList mit DefaultListModel und listener AWT, Swing, JavaFX & SWT 4
B JList nach dem Rendern sortieren AWT, Swing, JavaFX & SWT 3
T Wie ist das "Lookup-Verhalten" von JList, JCombobox änderbar? AWT, Swing, JavaFX & SWT 4
D JList items voneinander trennen AWT, Swing, JavaFX & SWT 10
E Keine Ausgabe von JList bei drücken von Button auf die Konsole AWT, Swing, JavaFX & SWT 7
M gleiches Element in JList mehrmals hintereinander auswählen AWT, Swing, JavaFX & SWT 6
T JList und Objekte AWT, Swing, JavaFX & SWT 7
M Swing JList Item in andere JList während Ausführung AWT, Swing, JavaFX & SWT 4
V JList filtern AWT, Swing, JavaFX & SWT 6
GianaSisters jList nur einmal ein Element anklicken AWT, Swing, JavaFX & SWT 3
TheWhiteShadow JList-Inhalt nach Änderung weg AWT, Swing, JavaFX & SWT 3
T JList ruft getElementAt nicht mehr auf AWT, Swing, JavaFX & SWT 4

Ähnliche Java Themen


Oben