是否有Java';在浏览巨大的目录时,它的性能很差?
我试图一次处理一个通过网络存储的文件。由于缓冲不是问题,读取文件速度很快。我遇到的问题只是在文件夹中列出目录。在许多文件夹中,每个文件夹至少有10k个文件 由于File.list()返回的是数组而不是iterable,因此性能非常慢。Java启动并收集文件夹中的所有名称,并在返回之前将其打包到数组中 此错误条目为,没有解决方法。他们只是说JDK7已经解决了这个问题 有几个问题:是否有Java';在浏览巨大的目录时,它的性能很差?,java,performance,directory-walk,Java,Performance,Directory Walk,我试图一次处理一个通过网络存储的文件。由于缓冲不是问题,读取文件速度很快。我遇到的问题只是在文件夹中列出目录。在许多文件夹中,每个文件夹至少有10k个文件 由于File.list()返回的是数组而不是iterable,因此性能非常慢。Java启动并收集文件夹中的所有名称,并在返回之前将其打包到数组中 此错误条目为,没有解决方法。他们只是说JDK7已经解决了这个问题 有几个问题: 有没有人能解决这个性能瓶颈 我是否在努力实现不可能的目标?即使只是在目录上迭代,性能还会很差吗 我可以使用具有此功能的
一个不可移植的解决方案是对操作系统进行本机调用并将结果流式传输 适用于Linux 你可以这样看。您可以像链表一样遍历目录结构,并批量或单独返回结果 适用于Windows
在windows中,使用和API的行为相当相似。另一种方法是通过不同的协议提供文件服务。据我所知,您正在为此使用SMB,而java只是试图将它们作为常规文件列出 这里的问题可能不仅仅是java(当您使用Microsoft Explorer x:\shared打开该目录时,它的行为如何),根据我的经验,这也需要相当长的时间 您可以将协议更改为类似HTTP的内容,只需获取文件名即可。通过这种方式,您可以通过http检索文件列表(10k行应该不会太多),并让服务器处理文件列表。这将非常快,因为它将使用本地资源(服务器中的资源)运行 当你有了这个列表,你就可以按照你现在所做的方式逐一处理它们 关键点是在节点的另一侧有一个辅助机制 这是否可行 今天:
File [] content = new File("X:\\remote\\dir").listFiles();
for ( File f : content ) {
process( f );
}
提议:
String [] content = fetchViaHttpTheListNameOf("x:\\remote\\dir");
for ( String fileName : content ) {
process( new File( fileName ) );
}
http服务器可以是一个非常小且简单的文件
如果您现在就是这样做的,那么您要做的就是将所有10k文件信息提取到您的客户机上(我不知道其中有多少信息),而您只需要文件名供以后处理
如果现在处理速度非常快,可能会稍微慢一点。这是因为预取的信息不再可用
试试看。虽然不好看,但我曾经通过在启动应用程序之前将dir/ls的输出传输到一个文件,并传入文件名,解决了这种问题 如果需要在应用程序中执行,可以使用system.exec(),但这会造成一些不好的后果 你问。第一种形式会非常快,第二种形式也应该非常快 确保对所选命令的每行执行一项(裸、无装饰、无图形)、完整路径和递归选项 编辑: 30分钟就可以拿到目录清单,哇 我突然想到,如果使用exec(),可以将其标准输出重定向到管道中,而不是将其写入文件 如果您这样做了,您应该立即开始获取文件,并且能够在命令完成之前开始处理 这种互动实际上可能会减慢速度,但也许不会——你可以试试 哇,我刚刚为您找到了.exec命令的语法,发现了这个,可能正是您想要的(它列出了一个使用exec和“ls”的目录,并将结果传输到您的程序中进行处理):(Jörg在sun的一条注释中提供,以替换Oracle破坏的内容) 不管怎么说,这个想法很简单,但是正确的代码很烦人。我要去从互联网上偷一些密码,然后把它们破解掉 /** *注意:仅将此作为最后手段使用!这是特定于windows甚至windows的 *虽然这不是一个好的解决方案,但它应该是快速的。 * *要使用它,请扩展FileProcessor并使用列表调用processFiles(“…”) *如果您想让它们像/s…一样,请提供多种选项。。。我强烈推荐/b * *重写processFile,它将为每行输出调用一次。 */ 导入java.io.*; 公共抽象类文件处理器 { 公共void进程文件(字符串选项) { 进程进程=null; BufferedReader inStream=null; //打电话给你好班 尝试 { 进程=Runtime.getRuntime().exec(“cmd/c dir”+dirOptions); } 捕获(IOE异常) { System.err.println(“exec()方法上的错误”); e、 printStackTrace(); } //读取被调用程序的标准输出流 尝试 { inStream=新的缓冲读取器( 新的InputStreamReader(process.getInputStream()); processFile(inStream.readLine()); } 捕获(IOE异常) { System.err.println(“inStream.readLine()上的错误”); e、 printStackTrace(); } }//结束方法 /**重写此方法--将为每个文件调用一次*/ 公共抽象void进程文件(字符串文件名); }//结束类
感谢您的代码捐赠者,我怀疑问题是否与您引用的bug报告有关。 这里的问题是“仅”内存使用,但不一定是速度。 如果你有足够的内存,这个错误与你的问题无关 你应该衡量你的问题是否与记忆有关。打开垃圾收集器日志,并使用例如来分析内存使用情况 我怀疑这与SMB协议有关 /** * Note: Only use this as a last resort! It's specific to windows and even * at that it's not a good solution, but it should be fast. * * to use it, extend FileProcessor and call processFiles("...") with a list * of options if you want them like /s... I highly recommend /b * * override processFile and it will be called once for each line of output. */ import java.io.*; public abstract class FileProcessor { public void processFiles(String dirOptions) { Process theProcess = null; BufferedReader inStream = null; // call the Hello class try { theProcess = Runtime.getRuntime().exec("cmd /c dir " + dirOptions); } catch(IOException e) { System.err.println("Error on exec() method"); e.printStackTrace(); } // read from the called program's standard output stream try { inStream = new BufferedReader( new InputStreamReader( theProcess.getInputStream() )); processFile(inStream.readLine()); } catch(IOException e) { System.err.println("Error on inStream.readLine()"); e.printStackTrace(); } } // end method /** Override this method--it will be called once for each file */ public abstract void processFile(String filename); } // end class
private static class SequentialIterator implements Iterator<File> {
private DirectoryStack dir = null;
private File current = null;
private long limit;
private FileFilter filter = null;
public SequentialIterator(String path, long limit, FileFilter ff) {
current = new File(path);
this.limit = limit;
filter = ff;
dir = DirectoryStack.getNewStack(current);
}
public boolean hasNext() {
while(walkOver());
return isMore && (limit > count || limit < 0) && dir.getCurrent() != null;
}
private long count = 0;
public File next() {
File aux = dir.getCurrent();
dir.advancePostition();
count++;
return aux;
}
private boolean walkOver() {
if (dir.isOutOfDirListRange()) {
if (dir.isCantGoParent()) {
isMore = false;
return false;
} else {
dir.goToParent();
dir.advancePostition();
return true;
}
} else {
if (dir.isCurrentDirectory()) {
if (dir.isDirectoryEmpty()) {
dir.advancePostition();
} else {
dir.goIntoDir();
}
return true;
} else {
if (filter.accept(dir.getCurrent())) {
return false;
} else {
dir.advancePostition();
return true;
}
}
}
}
private boolean isMore = true;
public void remove() {
throw new UnsupportedOperationException();
}
}
public class DirectoryStack {
private class Element{
private File files[] = null;
private int currentPointer;
public Element(File current) {
currentPointer = 0;
if (current.exists()) {
if(current.isDirectory()){
files = current.listFiles();
Set<File> set = new TreeSet<File>();
for (int i = 0; i < files.length; i++) {
File file = files[i];
set.add(file);
}
set.toArray(files);
}else{
throw new IllegalArgumentException("File current must be directory");
}
} else {
throw new IllegalArgumentException("File current not exist");
}
}
public String toString(){
return "current="+getCurrent().toString();
}
public int getCurrentPointer() {
return currentPointer;
}
public void setCurrentPointer(int currentPointer) {
this.currentPointer = currentPointer;
}
public File[] getFiles() {
return files;
}
public File getCurrent(){
File ret = null;
try{
ret = getFiles()[getCurrentPointer()];
}catch (Exception e){
}
return ret;
}
public boolean isDirectoryEmpty(){
return !(getFiles().length>0);
}
public Element advancePointer(){
setCurrentPointer(getCurrentPointer()+1);
return this;
}
}
private DirectoryStack(File first){
getStack().push(new Element(first));
}
public static DirectoryStack getNewStack(File first){
return new DirectoryStack(first);
}
public String toString(){
String ret = "stack:\n";
int i = 0;
for (Element elem : stack) {
ret += "nivel " + i++ + elem.toString()+"\n";
}
return ret;
}
private Stack<Element> stack=null;
private Stack<Element> getStack(){
if(stack==null){
stack = new Stack<Element>();
}
return stack;
}
public File getCurrent(){
return getStack().peek().getCurrent();
}
public boolean isDirectoryEmpty(){
return getStack().peek().isDirectoryEmpty();
}
public DirectoryStack downLevel(){
getStack().pop();
return this;
}
public DirectoryStack goToParent(){
return downLevel();
}
public DirectoryStack goIntoDir(){
return upLevel();
}
public DirectoryStack upLevel(){
if(isCurrentNotNull())
getStack().push(new Element(getCurrent()));
return this;
}
public DirectoryStack advancePostition(){
getStack().peek().advancePointer();
return this;
}
public File[] peekDirectory(){
return getStack().peek().getFiles();
}
public boolean isLastFileOfDirectory(){
return getStack().peek().getFiles().length <= getStack().peek().getCurrentPointer();
}
public boolean gotMoreLevels() {
return getStack().size()>0;
}
public boolean gotMoreInCurrentLevel() {
return getStack().peek().getFiles().length > getStack().peek().getCurrentPointer()+1;
}
public boolean isRoot() {
return !(getStack().size()>1);
}
public boolean isCurrentNotNull() {
if(!getStack().isEmpty()){
int currentPointer = getStack().peek().getCurrentPointer();
int maxFiles = getStack().peek().getFiles().length;
return currentPointer < maxFiles;
}else{
return false;
}
}
public boolean isCurrentDirectory() {
return getStack().peek().getCurrent().isDirectory();
}
public boolean isLastFromDirList() {
return getStack().peek().getCurrentPointer() == (getStack().peek().getFiles().length-1);
}
public boolean isCantGoParent() {
return !(getStack().size()>1);
}
public boolean isOutOfDirListRange() {
return getStack().peek().getFiles().length <= getStack().peek().getCurrentPointer();
}
}
JNIEXPORT jstring JNICALL Java_javaxt_io_File_GetFiles(JNIEnv *env, jclass, jstring directory)
{
HANDLE hFind;
try {
//Convert jstring to wstring
const jchar *_directory = env->GetStringChars(directory, 0);
jsize x = env->GetStringLength(directory);
wstring path; //L"C:\\temp\\*";
path.assign(_directory, _directory + x);
env->ReleaseStringChars(directory, _directory);
if (x<2){
jclass exceptionClass = env->FindClass("java/lang/Exception");
env->ThrowNew(exceptionClass, "Invalid path, less than 2 characters long.");
}
wstringstream ss;
BOOL bContinue = TRUE;
WIN32_FIND_DATAW data;
hFind = FindFirstFileW(path.c_str(), &data);
if (INVALID_HANDLE_VALUE == hFind){
jclass exceptionClass = env->FindClass("java/lang/Exception");
env->ThrowNew(exceptionClass, "FindFirstFileW returned invalid handle.");
}
//HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
//DWORD dwBytesWritten;
// If we have no error, loop thru the files in this dir
while (hFind && bContinue){
/*
//Debug Print Statment. DO NOT DELETE! cout and wcout do not print unicode correctly.
WriteConsole(hStdOut, data.cFileName, (DWORD)_tcslen(data.cFileName), &dwBytesWritten, NULL);
WriteConsole(hStdOut, L"\n", 1, &dwBytesWritten, NULL);
*/
//Check if this entry is a directory
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
// Make sure this dir is not . or ..
if (wstring(data.cFileName) != L"." &&
wstring(data.cFileName) != L"..")
{
ss << wstring(data.cFileName) << L"\\" << L"\n";
}
}
else{
ss << wstring(data.cFileName) << L"\n";
}
bContinue = FindNextFileW(hFind, &data);
}
FindClose(hFind); // Free the dir structure
wstring cstr = ss.str();
int len = cstr.size();
//WriteConsole(hStdOut, cstr.c_str(), len, &dwBytesWritten, NULL);
//WriteConsole(hStdOut, L"\n", 1, &dwBytesWritten, NULL);
jchar* raw = new jchar[len];
memcpy(raw, cstr.c_str(), len*sizeof(wchar_t));
jstring result = env->NewString(raw, len);
delete[] raw;
return result;
}
catch(...){
FindClose(hFind);
jclass exceptionClass = env->FindClass("java/lang/Exception");
env->ThrowNew(exceptionClass, "Exception occured.");
}
return NULL;
}
JNIEXPORT jlongArray JNICALL Java_javaxt_io_File_GetFileAttributesEx(JNIEnv *env, jclass, jstring filename)
{
//Convert jstring to wstring
const jchar *_filename = env->GetStringChars(filename, 0);
jsize len = env->GetStringLength(filename);
wstring path;
path.assign(_filename, _filename + len);
env->ReleaseStringChars(filename, _filename);
//Get attributes
WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
BOOL result = GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &fileAttrs);
if (!result) {
jclass exceptionClass = env->FindClass("java/lang/Exception");
env->ThrowNew(exceptionClass, "Exception Occurred");
}
//Create an array to store the WIN32_FILE_ATTRIBUTE_DATA
jlong buffer[6];
buffer[0] = fileAttrs.dwFileAttributes;
buffer[1] = date2int(fileAttrs.ftCreationTime);
buffer[2] = date2int(fileAttrs.ftLastAccessTime);
buffer[3] = date2int(fileAttrs.ftLastWriteTime);
buffer[4] = fileAttrs.nFileSizeHigh;
buffer[5] = fileAttrs.nFileSizeLow;
jlongArray jLongArray = env->NewLongArray(6);
env->SetLongArrayRegion(jLongArray, 0, 6, buffer);
return jLongArray;
}
import java.io.File;
import java.io.FilenameFilter;
public class Temp {
private static void processFile(File dir, String name) {
File file = new File(dir, name);
System.out.println("processing file " + file.getName());
}
private static void forEachFile(File dir) {
String [] ignore = dir.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
processFile(dir, name);
return false;
}
});
}
public static void main(String[] args) {
long before, after;
File dot = new File(".");
before = System.currentTimeMillis();
forEachFile(dot);
after = System.currentTimeMillis();
System.out.println("after call, delta is " + (after - before));
}
}