Je cherche à développeur un Viewer CSV en Java/Swing. Pas de problème, je crée l’interface et je me concentre sur la classe CSVTableModel qui s’occupe de gérer les données à afficher.
Petit rappel, CSVTableModel implémente l’interface TableModel :
[codebox]/*
- @# CSVTableModel.java 1.0 3 mai 2006
- Copyright 2006 Loic Dreux
*/
package org.csvviewer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
/**
*
*
-
@author Loic Dreux
-
@version 1.0
/
public class CSVTableModel extends AbstractTableModel
{
// Charset and decoder for ISO-8859-15
private static Charset charset = Charset.forName(“ISO-8859-15”);
private static CharsetDecoder decoder = charset.newDecoder();
// Pattern used to parse lines
private static Pattern linePattern = Pattern.compile(".\r?\n");
private static Pattern dataPattern = Pattern.compile(";");private boolean isLoaded;
private Map<Integer, Integer> indexLines;
private Map<Integer, String[]> buffer;private int rowCount;
private int columnCount;private CharBuffer cb;
private Thread t;/**
-
@param csvFile
-
@throws IOException
*/
public CSVTableModel(File csvFile) throws IOException
{
isLoaded = false;indexLines = Collections.synchronizedMap(new HashMap<Integer, Integer>());
buffer = Collections.synchronizedMap(new HashMap<Integer, String[]>());columnCount = 1;
rowCount = 0;// Ouvre le fichier et récupère le canal via le flux
FileInputStream fis = new FileInputStream(csvFile);
FileChannel fc = fis.getChannel();// Récupère la taille du fichier
int sz = (int) fc.size();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);// Décode le fichier dans un CharBuffer
cb = decoder.decode(bb);// Ferme le canal et le flux
fc.close();// On lance le compteur de ligne
t = new Thread(new CountLinesThread());
t.start();
}
/*
- (non-Javadoc)
-
@see javax.swing.table.TableModel#getRowCount()
*/
public int getRowCount()
{
return rowCount;
}
/*
- (non-Javadoc)
-
@see javax.swing.table.TableModel#getColumnCount()
*/
public int getColumnCount()
{
return columnCount;
}
/*
-
(non-Javadoc)
-
@see javax.swing.table.TableModel#getValueAt(int, int)
*/
public Object getValueAt(int rowIndex, int columnIndex)
{
if (!isLoaded && !buffer.containsKey(rowIndex)) {
new LoadData(rowIndex, rowIndex).execute();
return “”;
}if (columnIndex < buffer.get(rowIndex).length)
return buffer.get(rowIndex)[columnIndex];
return “”;
}
/**
-
@author Loic Dreux
-
@version 1.0
*/
public class CountLinesThread implements Runnable
{
public void run()
{
Matcher lm = linePattern.matcher(cb);
int lines = 0;while (lm.find()) { rowCount++; lines++; indexLines.put(lines, lm.start()); if (lm.end() == cb.limit()) break; if ((lines % 50) == 0) { CSVTableModel.this.fireTableRowsInserted(lines - 50, lines); } } CSVTableModel.this.fireTableRowsInserted(lines - (lines % 50), lines); new LoadData(1, lines).execute();
}
}
private class LoadData extends SwingWorker<Object, Object>
{
private int firstRow;
private int lastRow;
private boolean allData;public LoadData(int firstRow, int lastRow) { this(firstRow, lastRow, false); } public LoadData(int firstRow, int lastRow, boolean allData) { this.firstRow = firstRow; this.lastRow = lastRow; this.allData = allData; } @Override public Object doInBackground() { for (int i = firstRow; i <= lastRow; i++) { if (!buffer.containsKey(i)) { Matcher lm = linePattern.matcher(cb.subSequence(indexLines .get(i + 1), cb.length())); if (lm.find()) { buffer.put(i, dataPattern.split(lm.group())); if (buffer.get(i).length > columnCount) { columnCount = buffer.get(i).length; CSVTableModel.this.fireTableStructureChanged(); } } else { System.out.println("Erreur : " + i); } } } return null; } @Override public void done() { CSVTableModel.this.fireTableRowsUpdated(firstRow, lastRow); if(allData) { indexLines = null; cb = null; t = null; System.gc(); } }
}
}
[/codebox] -
Tout ce passe bien pour les fichiers modestes (500 lignes). Mais je souhaite faire un code qui fonctionne pour de gros fichiers (500000) lignes. Et là, c’est la catastrophe, le fait de tout sauvegarder dans une HashTable<Integer, String[]> nuit au performance.
J’avais testé avec un buffer qui calculait seulement les lignes à afficher et qui en sauvegardait que 1000 en mémoire, c’était plus lent à l’affichage : la table affichait pendant 1/2s des valeurs vide avant de mettre les bonnes valeurs, mais il y avait une plus grande réactivité car le calcul se faisait toujours dans un Thread séparé ; et l’application prenait beaucoup moins de mémoire.
Là, je ne sais plus trop quoi faire… Quelqu’un à une idée de comment stocké tout ça ? Je suppose que ca ne doit pas être trop difficile, faudrai que je regarde du coté de HSSQL, un SGBD léger en Java pour voir comment il stocke ces infos.