问题描述
我正在开发一个使用JTable
向用户显示信息的Swing程序。
该表目前有700多个条目,但我发现了一个非常奇怪的错误,似乎可以随机再现。
有时, JTable
会导致IndexOutOfBoundsException
,其索引为0,大小为0。我已经多次编译并运行了程序,而没有更改任何内容,并且此异常将随机出现,当异常不出现时,程序将正常运行。
我无法提供此问题的代码,因为它需要超过10个文件才能运行。
为什么JTable
会发生这种情况?
我发现程序在一次运行期间如何抛出异常而在同一程序的另一次运行中没有代码更改就不会引发异常真的很奇怪...是否存在引起这种行为的常见错误?程式?
我不确定我应该包括代码的哪一部分,但这是一些其他信息。
我创建了一个名为PlayerTableModel
的自定义TableModel
,用于呈现JTable
,数据存储在另一个类的ArrayLists
中。
这是TableModel
的代码
public class PlayerTableModel extends AbstractTableModel {
ArrayList<User> users = FileHandler.getAllPlayers();
ArrayList<PlayerSummary.Player> summaries = FileHandler.getAllSummaries();
@Override
public int getColumnCount() {
return 7;
}
@Override
public int getRowCount() {
return users.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
User user = users.get(rowIndex);
switch (columnIndex) {
case 0:
if (user.getSteamId().equalsIgnoreCase(summaries.get(rowIndex).getSteamID())) { // This is line 39
return summaries.get(rowIndex).getPersonaName();
} else {
return null;
}
case 1:
return user.getDateAdded();
case 2:
return user.getDateUpdated();
case 3:
return user.getNumberOfBans();
case 4:
return user.getNumberOfGameBans();
case 5:
return user.getDaysSinceLastBan();
case 6:
return user.getSteamId();
}
return null;
}
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case 0:
return "ID";
case 1:
return "Date added";
case 2:
return "Date updated";
case 3:
return "VAC bans";
case 4:
return "Game bans";
case 5:
return "Last ban (days)";
case 6:
return "64-Bit SteamID";
}
return null;
}
public boolean isCellEditable(int row, int column) {
return false;
}
}
在运行程序时,有时会出现此异常,这似乎是某种线程问题?
线程“ AWT-EventQueue-0”中的异常java.lang.IndexOutOfBoundsException:在PlayerTableModel的java.util.ArrayList.rangeCheck(未知源),在java.util.ArrayList.rangeCheck(未知源)处,索引:0,大小:0。 javax.swing.JTable.getValueAt(未知源)处的javax.swing.JTable.prepareRenderer(javax.swing.plaf.synth.SynthTableUI.paintCell(未知源)处的getValueAt(PlayerTableModel.java:39) .swing.plaf.synth.SynthTableUI.paintCells(未知源)在javax.swing.plaf.synth.SynthTableUI.paint(未知源)在javax.swing.plaf.synth.SynthTableUI.update(未知源)在javax.swing javax.swing.JComponent.paintComponent(未知源)javax.swing.JComponent.paint(未知源)javax.swing.JComponent.paintChildren(未知源)javax.swing.JViewport处JComponent.paint(未知源) java.swing.JComponent.paintChildren的.paint(未知源)javax.swing.JComponent.paintChildren的javax.swing.JComponent.paint(未知源) (未知源)在javax.swing.JComponent.paint(未知源)在javax.swing.JComponent.paintChildren(未知源)在javax.swing.JComponent.paint(未知源)在javax.swing.JComponent.paintChildren(未知) javax.swing.JComponent.paint中的Source(未知源)javax.swing.JComponent.paintToOffscreen中的javax.swing.RepaintManager $ PaintManager.paintDoubleBuffered(未知源)中的javax.swing.RepaintManager $ PaintManager.paint (未知源)在javax.swing.RepaintManager.paint(未知源)在javax.swing.JComponent._paintImmediately(未知源)在javax.swing.RepaintManager.paint($ java。 javax.swing.RepaintManager $ 4.run中的Unknown Source)(java.security.AccessController.doPrivileged(本地方法)处的java.security.ProtectionDomain $ 1.doIntersectionPrivilege(Unknown Source)处的javax.swing.RepaintManager.paintDirtyRegions(未知源)未知来源),位于javax.swing.RepaintManager.paintDirtyRegions (未知源)在javax.swing.RepaintManager.prePaintDirtyRegions(未知源)在javax.swing.RepaintManager.access $ 1300(未知源)在javax.swing.RepaintManager $ ProcessingRunnable.run(在java.awt.event处)。 java.awt.EventQueue上的InvocationEvent.dispatch(未知源).dispatchatchEventImpl(java.awt.EventQueue.access $ 500处的未知源)(java.awt.EventQueue $ 3.run处的未知源)(java.awt处的未知源)。 EventQueue $ 3.run(未知源),位于java.security.AccessController.doPrivileged(本机方法),位于java.security.ProtectionDomain $ 1.doIntersectionPrivilege(未知源),位于java.awt.EventQueue.dispatchatchEvent(未知源),位于java.awt。 java.awt.EventDispatchThread.pumpEventsForFilter(未知源)处的EventDispatchThread.pumpOneEventForFilters(未知源)java.awt.EventDispatchThread.pumpEvents(未知源)处的java.awt.EventDispatchThread.pumpEventsForHierarchy(未知源)处。 pumpEvents(未知源),位于java.awt.Ev entDispatchThread.run(未知来源)
使用FileHandler
类可以从文件中读写数据:
public class FileHandler implements Serializable {
private static ArrayList<User> tracked = new ArrayList<User>();
private static ArrayList<PlayerSummary.Player> trackedSummaries = new ArrayList<PlayerSummary.Player>();
private static ArrayList<PlayerSummary.Player> trackedSummariesUnsorted = new ArrayList<PlayerSummary.Player>();
private static ArrayList<String[]> games = new ArrayList<String[]>();
private static String savedString = "";
private static String APIKEY = "";
public static void writeToFile(String fileName, Object objToWrite) {
try {
// Write object to file.
FileOutputStream fOS = new FileOutputStream(fileName);
ObjectOutputStream oOS = new ObjectOutputStream(fOS);
oOS.writeObject(objToWrite);
oOS.close();
// Write string to text file so that it can be displayed.
if (fileName.equalsIgnoreCase("apikey.txt")) {
File file = new File(fileName);
PrintWriter pw = new PrintWriter(file);
pw.print(objToWrite);
pw.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void readFromFile(String fileName, String objType) {
FileInputStream fIS;
try {
fIS = new FileInputStream(fileName);
ObjectInputStream oIS = new ObjectInputStream(fIS);
if (objType.equalsIgnoreCase("ArrayList<User>")) {
tracked = (ArrayList<User>) oIS.readObject();
} else if (objType.equalsIgnoreCase("ArrayList<PlayerSummary.Player>")) {
if (fileName.equalsIgnoreCase("summaries.tmp")) {
trackedSummaries = (ArrayList<PlayerSummary.Player>) oIS.readObject();
}
if (fileName.equalsIgnoreCase("s_unsorted.tmp")) {
trackedSummariesUnsorted = (ArrayList<PlayerSummary.Player>) oIS.readObject();
}
} else if (objType.equalsIgnoreCase("ArrayList<String[]>")) {
games = (ArrayList<String[]>) oIS.readObject();
} else if (objType.equalsIgnoreCase("apikey")) {
APIKEY = (String) oIS.readObject();
}else {
System.out.println("Object type " + objType + " needs to be implemented!");
}
oIS.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static ArrayList<User> getAllPlayers() {
return tracked;
}
public static ArrayList<PlayerSummary.Player> getAllSummaries() {
return trackedSummaries;
}
public static ArrayList<PlayerSummary.Player> getAllSummariesUnsorted() {
return trackedSummariesUnsorted;
}
public static ArrayList<String[]> getGames() {
return games;
}
public static String getAPIKey() {
try {
BufferedReader br = new BufferedReader(new FileReader("apikey.txt"));
APIKEY = br.readLine();
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(BanTracker.getFrames()[0], "File not found! Did you save your API key?");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return APIKEY;
}
public static void updateArrayList(ArrayList<User> a) {
tracked = a;
}
}
1楼
您的FileHandler类未在上加载文件,因此导致该线程与Event Dispatch Thread之间出现竞争状态。
无论如何,您都不应静态访问文件; 在创建JTable之前,在创建时将用户和摘要注入到表模型中。 换句话说,只需创建一个构造函数:
public class PlayerTableModel extends AbstractTableModel {
private final List<User> users;
private final List<PlayerSummary.Player> summaries;
public PlayerTableModel(List<User> users, List<PlayerSummary.Player> summaries) {
this.users = new ArrayList<User>(users);
this.summaries = new ArrayList<PlayerSummary.Player>(summaries);
}
}
然后,在构造JTable之前,请确保已加载文件:
TableModel model = new PlayerTableModel(FileHandler.getAllPlayers(),
FileHandler.getAllSummaries());
JTable table = new JTable(model);
这应该确保所有加载均在预期的时间(程序启动时)进行,并且错误应消失。
另外,请尝试修改TableModel来执行此操作:
@Override
public int getRowCount() {
return Math.min(users.size(), summaries.size());
}
我不确定是否能解决您的问题,但是如果您这样做,我认为您会更加快乐:
public class CompletePlayer {
public final User user;
public final PlayerSummer.Player summary;
}
然后,仅使用一个ArrayList而不是两个。 阅读Ernest Friedman的帖子