【问题标题】:Faster way to collect in arraylist in java在java中收集arraylist的更快方法
【发布时间】:2019-04-06 17:04:53
【问题描述】:

我有一个包含许多文件的目录,想要过滤具有特定名称的文件并将它们保存在fileList ArrayList 中,它以这种方式工作,但需要很多时间。有没有办法让它更快?

String processingDir = "C:/Users/Ferid/Desktop/20181024";
String CorrId = "00a3d321-171c-484a-ad7c-74e22ffa3625");
Path dirPath = Paths.get(processingDir);       

ArrayList<Path> fileList;

try (Stream<Path> paths = Files.walk(dirPath))
{           
    fileList = paths.filter(t -> (t.getFileName().toString().indexOf("EPX_" + 
    corrId + "_") >= 0)).collect(Collectors.toCollection(ArrayList::new));
}

在尝试条件下遍历目录不需要太多时间,但在fileList 中收集它需要很多时间,我不知道究竟是哪个操作性能如此差或需要改进哪些操作. (这当然不是完整的代码,只是相关的东西)

【问题讨论】:

  • 为什么你认为收藏是昂贵的部分?请注意,您要求流执行的所有操作(例如filtermap 等)通常仅在调用终止方法时执行,collect 就是。
  • 我已经测试过了,那行fileList = paths.filter(t -&gt; (t.getFileName().toString().indexOf("EPX_" + corrId + "_") &gt;= 0)).collect(Collectors.toCollection(ArrayList::new)); 是花了很多时间的那一行
  • 看看能不能转成并行流。

标签: java list java-8 stream


【解决方案1】:

如果每次扫描文件的速度太慢,您可以在启动时建立文件索引,或者在文件更改时保持和维护。

您可以使用Watch Service 在程序运行时添加或删除文件时收到通知。

查询会快得多,因为它完全在内存中。第一次加载需要相同的时间,但可能会在您最初需要之前加载背景。

例如

static Map<String, List<Path>> pathMap;
public static void initPathMap(String processingDir) throws IOException {
    try (Stream<Path> paths = Files.walk(Paths.get(processingDir))) {
        pathMap = paths.collect(Collectors.groupingBy(
                p -> getCorrId(p.getFileName().toString())));
    }
    pathMap.remove(""); // remove entries without a corrId.
}


private static String getCorrId(String fileName) {
    int start = fileName.indexOf("EPX_");
    if (start < 0)
        return "";
    int end = fileName.indexOf("_", start + 4);
    if (end < 0)
        return "";
    return fileName.substring(start + 4, end);
}

// later 
    String corrId = "00a3d321-171c-484a-ad7c-74e22ffa3625";
    List<Path> pathList = pathMap.get(corrId); // very fast.

您可以通过编写以下代码使这段代码更简洁,但是,我不希望它更快。

List<Path> fileList;

try (Stream<Path> paths = Files.walk(dirPath)) {           
    String find = "EPX_" + corrId + "_"; // only calculate this once
    fileList = paths.filter(t -> t.getFileName().contains(find))
                    .collect(Collectors.toList());
}

成本是扫描目录文件所花费的时间。处理文件名的成本要低得多。

使用 SSD,或仅扫描已缓存在内存中的目录会显着加快速度。

对此进行测试的一种方法是在干净启动后多次执行该操作(因此它不会被缓存)。第一次运行所用时间的长短告诉您从磁盘加载数据所花费的时间。

【讨论】:

  • 遗憾的是,对目录的访问只是为了测试它,我必须提高代码中的性能,因为它不是为了在我的设备上进行本地改进
  • @SilverFullbuster 如果 1% 的时间花在你的代码上,那么你不能将其改进超过 1%
  • @SilverFullbuster 您可以做的是通过提前加载该信息并缓存它来避免遍历目录。
  • 感谢您的努力!有些想法对我有帮助,但您上次编辑的建议引发了一些 ava.lang.NullPointerException,我不知道为什么
【解决方案2】:

来自java.nio.file.Files.walk(Path)api:

通过遍历文件返回一个 惰性填充 且带有 Path 的 Stream 以给定起始文件为根的树。

这就是为什么它给你的印象是“在 try 条件下遍历目录并没有花费太多时间”。

实际上,真正的交易大多是在collect上完成的,这不是collect机制的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-05
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 2014-10-03
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    相关资源
    最近更新 更多