问题
I've written a recursive search method in java GUI application to find a file in a drive.
UI is responsive,
Search happened successfully but JList
does not populate,whereas console prints file names successfully, after 3 clicks on search button files are added in JList
but with repeated names of each file
//nullpointerexception
java.util.concurrent.ExecutionException:
java.lang.NullPointerException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
at javax.swing.SwingWorker.get(SwingWorker.java:602)
at searchapp.searchScreen$4.propertyChange(searchScreen.java:90)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
atjava.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at javax.swing.SwingWorker$SwingWorkerPropertyChangeSupport.firePropertyChange(SwingWorker.java:854)
at javax.swing.SwingWorker$SwingWorkerPropertyChangeSupport$1.run(SwingWorker.java:859)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:738)
at java.awt.EventQueue.access$300(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:699)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:708)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
Caused by: java.lang.NullPointerException
at searchapp.searchWorker.search(searchWorker.java:65)
at searchapp.searchWorker.search(searchWorker.java:66)
at searchapp.searchWorker.search(searchWorker.java:66)
at searchapp.searchWorker.doInBackground(searchWorker.java:51)
at searchapp.searchWorker.doInBackground(searchWorker.java:21)
at javax.swing.SwingWorker$1.call(SwingWorker.java:296)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at javax.swing.SwingWorker.run(SwingWorker.java:335)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
at javax.swing.SwingWorker.get(SwingWorker.java:602)
at searchapp.searchScreen$4.propertyChange(searchScreen.java:90)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at javax.swing.SwingWorker$SwingWorkerPropertyChangeSupport.firePropertyChange(SwingWorker.java:854)
at javax.swing.SwingWorker$SwingWorkerPropertyChangeSupport$1.run(SwingWorker.java:859)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:312)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:738)
at java.awt.EventQueue.access$300(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:699)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:708)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
Caused by: java.lang.NullPointerException
at searchapp.searchWorker.search(searchWorker.java:65)
at searchapp.searchWorker.search(searchWorker.java:66)
at searchapp.searchWorker.search(searchWorker.java:66)
at searchapp.searchWorker.doInBackground(searchWorker.java:51)
at searchapp.searchWorker.doInBackground(searchWorker.java:21)
at javax.swing.SwingWorker$1.call(SwingWorker.java:296)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at javax.swing.SwingWorker.run(SwingWorker.java:335)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
//New Code
public class searchWorker extends SwingWorker<List<File>, File> {
protected static final FileFilter DIRRECTORY_FILE_FILTER = new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
};
private DefaultListModel model;
private File path;
private FileFilter filefilter;
public searchWorker(File path, FileFilter filter, DefaultListModel model) {
this.model = model;
this.path = path;
this.filefilter = filter;
}
@Override
protected void process(List<File> chunks) {
for (File file : chunks) {
model.addElement(file);
}
}
@Override
protected List<File> doInBackground() throws Exception {
return new ArrayList<>(search(path));
}
public List<File> search(File path) {
List<File> files = new ArrayList<>(25);
if (path.exists()) {
File[] list = path.listFiles(filefilter);
if (list != null && list.length > 0) {
files.addAll(Arrays.asList(list));
publish(list);
}
File[] dirs = path.listFiles(DIRRECTORY_FILE_FILTER);
for (File dir : dirs) {
files.addAll(search(dir));
}
}
return files;
}
}
private void searchBtnActionPerformed(java.awt.event.ActionEvent evt) {
searchWorker worker = new searchWorker(new File("c:\\"), new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".txt") && pathname.getName().startsWith("abc");
}
}, lm);
worker.execute();
}
//old code
DefaultListModel lm = new DefaultListModel();
public void search(String path) {
File root = new File(path);
File[] list = root.listFiles();
if (list == null) {
return;
}
for (File f : list) {
if (f.isDirectory()) {
if (list == null) {
return;
}
search(f.getAbsolutePath());
} else {
if (f.getName().endsWith(".txt") && f.getName().startsWith("abc")) {
lm.addElement(f.getName());
System.out.println(f.getName());
found = true;
}
}
}
private void formWindowOpened(java.awt.event.WindowEvent evt) {
jList1.setModel(lm);
}
private void searchBtnActionPerformed(java.awt.event.ActionEvent evt) {
//just added this code in my program to resolve unresponsive UI
Thread t = new Thread(new Runnable() {
@Override
public void run() {
search("c:\\");
}
});
t.start();
}
回答1:
The basic problem is, you're not actually modifying the ListModel
in anyway.
Swing is a single threaded environment, you've used a Thread
to ensure that the UI is not blocked while the search is carried out, which is good, but Swing is also not thread safe, meaning that you shouldn't update the UI from outside of the context of the Event Dispatching Thread, so the Thread
is bad.
While you "could" use a Thread
, a SwingWorker
would present a better (and generally simpler) solution to the problem (not to mention a more reusable one)
public static class SearchWorker extends SwingWorker<List<File>, File> {
protected static final FileFilter DIRECTORY_FILE_FILTER = new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
};
private DefaultListModel model;
private File path;
private FileFilter fileFilter;
public SearchWorker(File path, FileFilter filter, DefaultListModel model) {
this.model = model;
this.path = path;
this.fileFilter = filter;
}
@Override
protected void process(List<File> chunks) {
for (File file : chunks) {
model.addElement(file);
}
}
@Override
protected List<File> doInBackground() throws Exception {
return new ArrayList<>(search(path));
}
public List<File> search(File path) {
List<File> files = new ArrayList<>(25);
if (path.exists() && path.isDirectory()) {
File[] list = path.listFiles(fileFilter);
if (list != null && list.length > 0) {
files.addAll(Arrays.asList(list));
publish(list);
}
File[] dirs = path.listFiles(DIRECTORY_FILE_FILTER);
if (dirs != null) {
for (File dir : dirs) {
files.addAll(search(dir));
}
}
}
return files;
}
}
Then you would simply start it using something like...
SearchWorker worker = new SearchWorker(new File("c:\\"), new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".txt") && pathname.getName().startsWith("abc");
}
}, lm);
worker.execute();
See Worker Threads and SwingWorker for more details
Runnable example...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
DefaultListModel model = new DefaultListModel();
JList list = new JList(model);
setLayout(new BorderLayout());
add(new JScrollPane(list));
JLabel label = new JLabel("...");
add(label, BorderLayout.SOUTH);
SearchWorker worker = new SearchWorker(new File("c:\\"), new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".png");
}
}, model);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
SearchWorker worker = (SearchWorker) evt.getSource();
if ("state".equals(evt.getPropertyName())) {
if (worker.isDone()) {
label.setText("Finished");
try {
List<File> files = worker.get();
} catch (Exception e) {
e.printStackTrace();
}
}
} else if ("path".equals(evt.getPropertyName())) {
File path = (File) evt.getNewValue();
label.setText(path.toString());
}
}
});
worker.execute();
}
}
public static class SearchWorker extends SwingWorker<List<File>, File> {
protected static final FileFilter DIRECTORY_FILE_FILTER = new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
};
private DefaultListModel model;
private File path;
private FileFilter fileFilter;
public SearchWorker(File path, FileFilter filter, DefaultListModel model) {
this.model = model;
this.path = path;
this.fileFilter = filter;
}
@Override
protected void process(List<File> chunks) {
for (File file : chunks) {
model.addElement(file);
}
}
@Override
protected List<File> doInBackground() throws Exception {
return new ArrayList<>(search(path));
}
public List<File> search(File path) {
firePropertyChange("path", null, path);
List<File> files = new ArrayList<>(25);
if (path.exists() && path.isDirectory()) {
File[] list = path.listFiles(fileFilter);
if (list != null && list.length > 0) {
files.addAll(Arrays.asList(list));
publish(list);
}
File[] dirs = path.listFiles(DIRECTORY_FILE_FILTER);
if (dirs != null) {
for (File dir : dirs) {
files.addAll(search(dir));
}
}
}
return files;
}
}
}
回答2:
Check the following changes.
public void search(String path) {
//DefaultListModel lm = new DefaultListModel();
//If you are not using the model for other purposes you can declare it here itself
//to clear the previous values if any [this is why repetitive values]
lm.clear();
File root = new File(path);
File[] list = root.listFiles();
if (list == null) {
return;
}
for (File f : list) {
if (f.isDirectory()) {
// Unnecessary test for null
// if (list == null) {
// return;
// }
search(f.getAbsolutePath());
} else {
if (f.getName().endsWith(".txt") && f.getName().startsWith("abc")) {
lm.addElement(f.getName());
System.out.println(f.getName());
found = true;
}
}
}
jList1.setModel(lm);
}
来源:https://stackoverflow.com/questions/31010273/jlist-does-not-populates-after-search-button-clicked