Java ImageIO.read(…)-速度很慢,有更好的方法吗?
我正在加载大量图标,这些图标将在我的应用程序中使用。我计划在服务器启动时从jar加载所有这些文件。然而,数百张图像加起来刚刚超过9MB,执行此任务仍需30秒以上。我现在是在一个单独的线程中完成的,但这让我怀疑我在代码中是否做了一些效率低下的事情。我借用了SO的代码将信息加载到我的结构中。我将代码放入测试类并对其进行了分析。99%的配置文件使用ImageIO.read(..)方法。所以这绝对是瓶颈。下面是一个测试类,它应该提供关于我如何使用ImageIO的图片Java ImageIO.read(…)-速度很慢,有更好的方法吗?,java,Java,我正在加载大量图标,这些图标将在我的应用程序中使用。我计划在服务器启动时从jar加载所有这些文件。然而,数百张图像加起来刚刚超过9MB,执行此任务仍需30秒以上。我现在是在一个单独的线程中完成的,但这让我怀疑我在代码中是否做了一些效率低下的事情。我借用了SO的代码将信息加载到我的结构中。我将代码放入测试类并对其进行了分析。99%的配置文件使用ImageIO.read(..)方法。所以这绝对是瓶颈。下面是一个测试类,它应该提供关于我如何使用ImageIO的图片 public class IconT
public class IconTest {
/**
* @param args the command line arguments
* @throws java.net.URISyntaxException
* @throws java.io.IOException
*/
public static void main(String[] args) throws URISyntaxException, IOException {
URI uri = IconTest.class.getResource("Icons").toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath("Icons/");
} else {
myPath = Paths.get(uri);
}
IconFolder root = new IconFolder(myPath.toFile().getName());
IconFolder parentFolder = root;
HIcon currentIcon = null;
IconFolder folder = null;
HashMap<String,IconFolder> folders = new HashMap<>();
folders.put(parentFolder.getName(), parentFolder);
Stream<Path> walk = Files.walk(myPath, 5);
Iterator<Path> it = walk.iterator();it.next();
while(it.hasNext()){
Path path = it.next();
if(path.toFile().isDirectory()){
folder = new IconFolder(path.toFile().getName());
folders.put(folder.getName(), folder);
String parentName = path.getParent().toFile().getName();
parentFolder = folders.get(parentName);
parentFolder.addSubFolder(folder);
currentIcon =null;
System.out.println("Directory: " + path);
}else{
URL url = path.toUri().toURL();
ImageIcon icon = new ImageIcon(ImageIO.read(url));
//Image image = Toolkit.getDefaultToolkit().getImage(url);
//ImageIcon icon = new ImageIcon(image);
String[] iconName;
iconName = path.getFileName().toString().replaceAll("_000000", "").replaceAll(".png","").split("_",2);
String imageName = iconName[0];
String imageSize = iconName[1];
if(currentIcon==null||!currentIcon.getName().equals(imageName)){
currentIcon = new HIcon(imageName);
folder.addIcon(currentIcon);
currentIcon.setIcon(icon, imageSize );
}else{
currentIcon.setIcon(icon, imageSize);
}
//System.out.println("Image: " + imageName+"-->"+imageSize);
}
}
System.out.println("");
}
}
公共类IconTest{
/**
*@param指定命令行参数
*@throws java.net.URISyntaxException
*@抛出java.io.IOException
*/
公共静态void main(字符串[]args)抛出URISyntaxException、IOException{
URI=IconTest.class.getResource(“Icons”).toURI();
路径myPath;
if(uri.getScheme().equals(“jar”)){
FileSystem FileSystem=FileSystems.newFileSystem(uri,Collections.emptyMap());
myPath=fileSystem.getPath(“Icons/”);
}否则{
myPath=path.get(uri);
}
IconFolder root=新的IconFolder(myPath.toFile().getName());
iconfolderparentfolder=根目录;
HIcon currentIcon=null;
IconFolder文件夹=null;
HashMap folders=新建HashMap();
folders.put(parentFolder.getName(),parentFolder);
streamwalk=Files.walk(myPath,5);
迭代器it=walk.Iterator();it.next();
while(it.hasNext()){
Path=it.next();
if(path.toFile().isDirectory()){
folder=newiconfolder(path.toFile().getName());
folders.put(folder.getName(),folder);
字符串parentName=path.getParent().toFile().getName();
parentFolder=folders.get(parentName);
parentFolder.addSubFolder(文件夹);
currentIcon=null;
System.out.println(“目录:+path”);
}否则{
URL=path.toUri().toURL();
ImageIcon图标=新的ImageIcon(ImageIO.read(url));
//Image Image=Toolkit.getDefaultToolkit().getImage(url);
//ImageIcon图标=新的ImageIcon(图像);
字符串[]图标名;
iconName=path.getFileName().toString().replaceAll(“.000000”).replaceAll(“.png”).split(“.u000000”),2);
字符串imageName=iconName[0];
字符串imageSize=iconName[1];
如果(currentIcon==null | |!currentIcon.getName().equals(imageName)){
currentIcon=新HIcon(图像名称);
文件夹.addIcon(当前图标);
currentIcon.setIcon(图标,图像大小);
}否则{
currentIcon.setIcon(图标,图像大小);
}
//System.out.println(“图像:“+imageName+”-->“+imageSize”);
}
}
System.out.println(“”);
}
}
任何指示都会有帮助。我看了一些我认为是在指出同样的事情的SO帖子。我正在使用带SSD的MacBook Air,所以我认为这会是闪电般的快
我在下面添加了个人资料结果的屏幕截图:
这是将setUseCache
设置为false后的配置文件:
您可以尝试使用
ImageIO.setUseCache(false)
来使用基于内存的缓存而不是基于磁盘的缓存(因为它是默认的)
参考资料:我终于找到了一个解决办法。以下为最新资料: 这是一个非常有趣的解决方案。我决定使用一个执行器,以便读取操作可以并行化。我已经为运行整个任务创建了一个线程,但将其分解为更细粒度的任务确实加快了速度 最后,我把这个时间从38.9秒降到了3.4秒。这是我书中的一大收获。这对我来说很重要,因为我希望我的服务器尽快启动,而且我绝对不想失去39秒的启动时间 我选择将它硬编码为5个线程,因为我在mac上开发,它只有4个内核。我尝试了15个或以上的核心+1,它会失去效率 以下是调整后的示例代码:
public class IconTest {
/**
* @param args the command line arguments
* @throws java.net.URISyntaxException
* @throws java.io.IOException
*/
public static void main(String[] args) throws URISyntaxException, IOException {
test4();
}
public static void test4() throws MalformedURLException, IOException, URISyntaxException{
ExecutorService service = Executors.newFixedThreadPool(5);
URI uri = IconTest.class.getResource("../resources/Icons").toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath("Icons/");
} else {
myPath = Paths.get(uri);
}
IconFolder root = new IconFolder(myPath.toFile().getName());
IconFolder parentFolder = root;
HIcon currentIcon = null;
IconFolder folder = null;
HashMap<String,IconFolder> folders = new HashMap<>();
folders.put(parentFolder.getName(), parentFolder);
Stream<Path> walk = Files.walk(myPath, 10);
Iterator<Path> it = walk.iterator();it.next();
Info.Info("Starting loading icons....");
ImageIO.setUseCache(false);
while(it.hasNext()){
Path path = it.next();
if(path.toFile().isDirectory()){
folder = new IconFolder(path.toFile().getName());
folders.put(folder.getName(), folder);
String parentName = path.getParent().toFile().getName();
parentFolder = folders.get(parentName);
parentFolder.addSubFolder(folder);
currentIcon =null;
Info.Info("Directory: " + path);
}else{
ImageLoadingTask task;
URL url = path.toUri().toURL();
String[] iconName;
iconName = path.getFileName().toString().replaceAll("_000000", "").replaceAll(".png","").split("_",2);
String imageName = iconName[0];
String imageSize = iconName[1];
if(currentIcon==null||!currentIcon.getName().equals(imageName)){
currentIcon = new HIcon(imageName);
folder.addIcon(currentIcon);
task = new ImageLoadingTask(url,currentIcon,imageSize);
service.submit(task);
}else{
task = new ImageLoadingTask(url,currentIcon,imageSize);
service.submit(task);
}
//Info.Info("Image: " + imageName+"-->"+imageSize);
}
}
service.shutdown();
Info.Info("Finished loading icons....");
}
static public class ImageLoadingTask implements Callable<ImageIcon> {
private final URL url;
private final HIcon hIcon;
private final String size;
public ImageLoadingTask(URL url, HIcon hIcon, String size ) {
this.url = url;
this.hIcon=hIcon;
this.size=size;
}
@Override
public ImageIcon call() throws Exception {
ImageIcon icon = new ImageIcon(ImageIO.read(url));
hIcon.setIcon(icon, size);
return icon;
}
}
}
公共类IconTest{
/**
*@param指定命令行参数
*@throws java.net.URISyntaxException
*@抛出java.io.IOException
*/
公共静态void main(字符串[]args)抛出URISyntaxException、IOException{
test4();
}
public static void test4()引发畸形的DurLexception、IOException、URISyntaxException{
ExecutorService=Executors.newFixedThreadPool(5);
URI=IconTest.class.getResource(“../resources/Icons”).toURI();
路径myPath;
if(uri.getScheme().equals(“jar”)){
FileSystem FileSystem=FileSystems.newFileSystem(uri,Collections.emptyMap());
myPath=fileSystem.getPath(“Icons/”);
}否则{
myPath=path.get(uri);
}
IconFolder root=新的IconFolder(myPath.toFile().getName());
iconfolderparentfolder=根目录;
HIcon currentIcon=null;
IconFolder文件夹=null;
HashMap folders=新建HashMap();
folders.put(parentFolder.getName(),parentFolder);
streamwalk=Files.walk(myPath,10);
迭代器it=walk.Iterator();it.next();
Info.Info(“开始加载图标…”);
ImageIO.setUseCache(false);
while(it.hasNext()){
Path=it.next();
if(path.toFile().isDirectory()){
folder=newiconfolder(path.toFile().getName());
folders.put(folder.getName(),folder);
字符串parentName=path.getParent().toFile().getName();
parentFolder=folders.get(parentName);
parentFolder.addSubFolder(文件夹);
currentIcon=null;
Info.Info(“目录:+路径”);
}否则{
图像加载任务;
URL=path.toUri().toURL();