rxjava遍历文件夹的巨坑

原创
2017/11/17 13:17
阅读数 2.3K

递归遍历文件夹

java 8之前遍历文件夹用File.list(),而rxjava要想遍历文件夹,那么最简单的做法是递归遍历。

public Observable<File> recursiveDir(final File parent) {
	if (!parent.isDirectory()) {
		return Observable.just(parent);
	}
	File[] files = null;
	if ((files = parent.listFiles()) == null || files.length == 0){}
		return Observable.empty();
	}
	return Observable.fromArray(files)
			.flatMap(new Function<File, Observable<File>>() {
				@Override
				public Observable<File> apply(File file) throws Exception {
					return recursiveDir(file);
				}
			});
}

递归的写法简单写起来爽,不过跑起来就要哭死你了,StackOverflow!OutOfMemory!。不信试试遍历下手机sdcard下面的tencent目录,不是巨慢就是崩溃!


堆栈遍历文件夹

rxJava递归遍历文件夹除了本身递归在队栈保存恢复上消耗系统资源,还因为rxjava在每一层递归中产生一个Observable,而Observable如果是并发的很可能很快就消耗尽线程池资源。既然递归方式不行,那就改成自己维护堆栈的方式遍历呗,直接看代码吧

/**
 * Created by droidwolf on 2017/11/14.
 * https://my.oschina.net/droidwolf
 * 转载请注明
 */
public class FileTreeWalker implements Iterable<File> {
    private ArrayDeque<File> mDirectories =null;
    private ArrayDeque<File> mFiles = null;

    public FileTreeWalker walk(File path) {
        if (mDirectories != null && !mDirectories.isEmpty()) {
            mDirectories.clear();
        }
        if (mFiles != null && !mFiles.isEmpty()) {
            mFiles.clear();
        }
        walkDir(path);
        return this;
    }

    private void walkDir(File path) {
        if (path == null || !path.exists() || !path.isDirectory()) {
            return;
        }
        final File[] files = path.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        if(mDirectories==null) mDirectories = new ArrayDeque<File>(256);
        if(mFiles==null) mFiles = new ArrayDeque<File>(512);

        for (File f : files) {
            if (f.isDirectory()) {
                mDirectories.push(f);
            } else {
                mFiles.addLast(f);
            }
        }
    }

    @Override
    public Iterator<File> iterator() {
        return mIterator;
    }
    private final Iterator<File> mIterator=new Iterator<File>() {
        @Override
        public boolean hasNext() {
            return (mFiles!=null &&!mFiles.isEmpty()) || (mDirectories!=null&& !mDirectories.isEmpty());
        }
        @Override
        public File next() {
            if (mFiles != null && !mFiles.isEmpty()) {
                final File f = mFiles.pollFirst();

                return f;
            } else if (mDirectories != null && !mDirectories.isEmpty()) {
                final File dir = mDirectories.pop();

                walkDir(dir);
                return dir;
            }
            return null;
        }
    };
}

rxjava调用FileTreeWalker 

        Observable.fromIterable(new FileTreeWalker().walk(path))
                .subscribeOn(Schedulers.io())
                .filter(file -> {
                    //。。。
                    return false;
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(fileNext -> {
					//。。。
                });

当然也可以在java8中使用FileTreeWalker

StreamSupport.stream(Spliterators.spliteratorUnknownSize(new FileTreeWalker().walk(new File("C:\\")).iterator(),Spliterator.NONNULL),true )
.forEach(file->{
	if(file.isDirectory()) {
		System.out.println(file.getName());
	}else {
		System.out.println("\t"+file.getName());
	}
});

 

总结

递归很多时候写的爽快但遇到瓶颈时优化是个头疼的问题,如果语言自带尾递归优化外挂那还好,没有的话还是得改变原来的思路重来。最后得说一下这个FileTreeWalker实际是不安全的,因为它持有文件夹和文件队列,这使得FileTreeWalker在遍历过程中产生状态的变化,如果在多线程中这是个忌讳。至于怎么改,自己看着办吧!:(

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部