Java File.listFiles order

本文详细介绍了Java中文件操作的相关知识,包括如何获取目录下的文件列表,并解释了文件列表返回结果的不确定性及其对类加载的影响。同时提供了一个案例演示如何通过自定义过滤器对文件进行排序,以及使用Java内置的排序方法实现类似`ls -ta`的功能。

Java File.listFiles order

 

The documentation for the Java JDK specifically mentions that the files returns can be in any order.

There is no guarantee that the name strings in the resulting array will appear in any specific order; they are not, in particular, guaranteed to appear in alphabetical order.

The actual ordering of the files varies from platform to platform, and often is a result of the physical order of the files in the directory structure. On Solaris, this order will often reverse if you copy a folder. Generally this is not noticeable, as most tools sort the files in some order before returning them to the user.

Unfortunately, file ordering is important when the files are added to a classloader. As files at the front of the classpath are searched before files at the end, if you have two different classes in your classpath, the order may not be stable. This isn't a problem if you specify the full classpath when you launch Java, but if you load files dynamically, like JBoss does, then your behavior is subject to change. The following is a test case which may pass depending on your platform and file system. For me, it fails on Windows XP, but passes on Solaris 10.

	public static void main(String[] args) throws IOException {
		final List<String> files =  java.util.Arrays.asList("c", "b", "a");
		for (String f : files) {
			File file = new File(f);
			file.createNewFile();
			file.deleteOnExit();	
		}
		FilenameFilter filenameFilter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return files.contains(name);
			}
		};
		File[] ondisk = new File(".").listFiles(filenameFilter);
		for(int i = 0; i < files.size(); i++) {
			assert ondisk[i].getName().equals(files.get(i)) : "Expected file " + files.get(i) + " but found " + ondisk[i].getName();
		}
	}

Depending on your application, you may want to sort the files alphabetically, or by last modified. The following example emulates the ls -ta command:

	private static final Comparator<File> lastModified = new Comparator<File>() {
		@Override
		public int compare(File o1, File o2) {
			return o1.lastModified() == o2.lastModified() ? 0 : (o1.lastModified() < o2.lastModified() ? 1 : -1 ) ;
		}
	};
	public void testFileSort() throws Exception {
		File[] files = new File(".").listFiles();
		Arrays.sort(files, lastModified);
		System.out.println(Arrays.toString(files));
	}
package com.kotei.overseas.navi.update; import android.util.Log; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class FileUtils { private static final String TAG = "FileUtils"; public static final int BUFFER_SIZE = 10 * 1024 * 1024; // 10MB缓冲区 private static long totalSize = 0; private static long processedSize = 0; private static long lastCheckTime = 0; /** * 带进度压缩目录 * * @param sourceDir 源目录 * @param outputFile 输出文件 * @param callback 进度回调 * @return 是否成功 */ public static boolean compressDirectoryWithProgress(File sourceDir, File outputFile, USBOfflineUpdater.ProgressCallback callback) { // 检查参数是否为 null if (sourceDir == null) { throw new IllegalArgumentException("Source directory cannot be null"); } if (outputFile == null) { throw new IllegalArgumentException("Output file cannot be null"); } totalSize = getDirectorySize(sourceDir); try (FileOutputStream fos = new FileOutputStream(outputFile); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) { zos.setLevel(Deflater.BEST_SPEED); // 压缩目录 boolean compressResult = zipSubDirectoryWithProgress("", sourceDir, zos, callback, true); // 3. 压缩完成后删除原始数据 if (compressResult) { Log.i(TAG, "压缩成功,开始删除原始数据..."); return deleteDirectoryContents(sourceDir); } return false; } catch (IOException e) { Log.e(TAG, "压缩目录失败", e); return false; } } /** * 带进度压缩子目录 * * @param basePath 基础路径 * @param dir 当前目录 * @param zos Zip输出流 * @param callback 进度回调 * @param deleteAfterAdd 添加后是否删除文件 * @return 是否成功 */ private static boolean zipSubDirectoryWithProgress(String basePath, File dir, ZipOutputStream zos, USBOfflineUpdater.ProgressCallback callback, boolean deleteAfterAdd) throws IOException { // 检查参数是否为 null if (dir == null) { throw new IllegalArgumentException("Directory cannot be null"); } if (zos == null) { throw new IllegalArgumentException("ZipOutputStream cannot be null"); } byte[] buffer = new byte[BUFFER_SIZE]; File[] files = dir.listFiles(); if (files == null) return true; // 按文件大小排序,先处理大文件 Arrays.sort(files, (f1, f2) -> { if (f1.isDirectory() && !f2.isDirectory()) return -1; if (!f1.isDirectory() && f2.isDirectory()) return 1; return Long.compare(f2.length(), f1.length()); // 大文件优先 }); for (File file : files) { if (Thread.currentThread().isInterrupted()) { return false; } String path = basePath + file.getName(); if (file.isDirectory()) { // 递归处理子目录 if (!zipSubDirectoryWithProgress(path + File.separator, file, zos, callback, deleteAfterAdd)) { return false; } // 删除空目录 if (deleteAfterAdd && file.listFiles().length == 0) { if (!file.delete()) { Log.w(TAG, "删除空目录失败: " + file.getAbsolutePath()); } } } else { try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis)) { zos.putNextEntry(new ZipEntry(path)); int length; while ((length = bis.read(buffer)) > 0) { zos.write(buffer, 0, length); processedSize += length; // 更新进度 if (callback != null) { callback.onProgress(processedSize, totalSize); } } zos.closeEntry(); // 压缩后立即删除文件 if (deleteAfterAdd) { if (!file.delete()) { Log.w(TAG, "删除文件失败: " + file.getAbsolutePath()); } else { Log.d(TAG, "已删除: " + file.getAbsolutePath()); } } } catch (InterruptedException e) { throw new RuntimeException(e); } } } return true; } /** * 计算目录大小 * * @param directory 目录 * @return 目录大小(字节) */ public static long getDirectorySize(File directory) { long size = 0; if (directory == null || !directory.exists()) return 0; if (directory.isDirectory()) { File[] files = directory.listFiles(); if (files != null) { for (File file : files) { size += getDirectorySize(file); } } } else { size = directory.length(); } return size; } // 静态内部类:解压任务 private static class ExtractTask implements Callable<Long> { private final ZipFile zip; private final ZipEntry entry; private final File file; private final USBOfflineUpdater.ProgressCallback callback; private final long totalSize; public ExtractTask(ZipFile zip, ZipEntry entry, File file, USBOfflineUpdater.ProgressCallback callback, long totalSize) { this.zip = zip; this.entry = entry; this.file = file; this.callback = callback; this.totalSize = totalSize; } @Override public Long call() throws IOException { long entrySize = 0; try (InputStream in = zip.getInputStream(entry); FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos, 128 * 1024)) { byte[] buffer = new byte[128 * 1024]; int length; while ((length = in.read(buffer)) > 0) { bos.write(buffer, 0, length); entrySize += length; // 定期报告进度(每100MB) zwx修改-对应:Skipped 98 frames! The application may be doing too much work on its main thread. if (callback != null && entrySize % (100 * 1024 * 1024) == 0) { synchronized (FileUtils.class) { callback.onProgress(entrySize, totalSize); } } } bos.flush(); } catch (IOException e) { Log.e(TAG, "解压文件失败: " + file.getName(), e); throw e; } catch (InterruptedException e) { throw new RuntimeException(e); } return entrySize; } } /** * 带进度解压ZIP文件 * * @param zipFile ZIP文件 * @param targetDir 目标目录 * @param callback 进度回调 * @return 是否成功 */ public static boolean extractZipWithProgress(File zipFile, File targetDir, USBOfflineUpdater.ProgressCallback callback) { long totalSize = zipFile.length(); AtomicLong processedSize = new AtomicLong(0); // 使用原子类型保证线程安全 // 创建线程池(4线程) ExecutorService executor = Executors.newFixedThreadPool(4); List<Future<Long>> futures = new ArrayList<>(); try (ZipFile zip = new ZipFile(zipFile)) { // 第一遍:预创建所有目录 Map<String, Boolean> createdDirs = new HashMap<>(); Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) { File dir = new File(targetDir, entry.getName()); if (!dir.exists() && !dir.mkdirs()) { Log.w(TAG, "创建目录失败: " + dir.getAbsolutePath()); } createdDirs.put(entry.getName(), true); } } // 第二遍:并行解压文件 entries = zip.entries(); while (entries.hasMoreElements()) { final ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) continue; // 确保父目录存在 File file = new File(targetDir, entry.getName()); File parent = file.getParentFile(); if (parent != null && !parent.exists()) { if (!parent.mkdirs()) { Log.w(TAG, "创建父目录失败: " + parent.getAbsolutePath()); } } // 提交解压任务 futures.add(executor.submit(new ExtractTask( zip, entry, file, callback, totalSize ))); } // 等待所有任务完成并统计进度 for (Future<Long> future : futures) { try { long size = future.get(); processedSize.addAndGet(size); // 更新总进度 if (callback != null) { callback.onProgress(processedSize.get(), totalSize); } } catch (ExecutionException e) { Log.e(TAG, "解压任务失败", e.getCause()); } } return true; } catch (IOException | InterruptedException e) { Log.e(TAG, "解压失败: " + zipFile.getName(), e); return false; } finally { executor.shutdownNow(); } } /** * 带进度拷贝文件 * * @param src 源文件 * @param dest 目标文件 * @param callback 进度回调 * @return 是否成功 */ public static boolean copyFileWithProgress(File src, File dest, USBOfflineUpdater.ProgressCallback callback) { long totalSize = src.length(); long copiedSize = 0; try (InputStream in = new BufferedInputStream(new FileInputStream(src)); OutputStream out = new BufferedOutputStream(new FileOutputStream(dest))) { byte[] buffer = new byte[BUFFER_SIZE]; int length; while ((length = in.read(buffer)) > 0) { if (Thread.currentThread().isInterrupted()) { return false; } out.write(buffer, 0, length); copiedSize += length; // 优化后的检查逻辑(每秒最多1次) long currentTime = System.currentTimeMillis(); if (currentTime - lastCheckTime > 1000) { if (Thread.currentThread().isInterrupted()) { return false; } if (USBOfflineUpdater.getInstance().isCancelled) { return false; } // 更新进度 if (callback != null) { callback.onProgress(copiedSize, totalSize); } lastCheckTime = currentTime; } } return true; } catch (IOException e) { Log.e(TAG, "拷贝失败: " + src.getName(), e); return false; } catch (InterruptedException e) { throw new RuntimeException(e); } } /** * 压缩目录 * * @param sourceDir 源目录 * @param outputFile 输出文件 * @return 是否成功 */ public static boolean compressDirectory(File sourceDir, File outputFile) { try (FileOutputStream fos = new FileOutputStream(outputFile); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) { zipSubDirectory("", sourceDir, zos); return true; } catch (IOException e) { Log.e(TAG, "压缩目录失败", e); return false; } } private static void zipSubDirectory(String basePath, File dir, ZipOutputStream zos) throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; File[] files = dir.listFiles(); if (files == null) return; for (File file : files) { String path = basePath + file.getName(); if (file.isDirectory()) { zipSubDirectory(path + File.separator, file, zos); } else { try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis)) { zos.putNextEntry(new ZipEntry(path)); int length; while ((length = bis.read(buffer)) > 0) { zos.write(buffer, 0, length); } zos.closeEntry(); } } } } /** * 解压ZIP文件 * * @param zipFile ZIP文件 * @param targetDir 目标目录 * @return 是否成功 */ public static boolean extractZip(File zipFile, File targetDir) { return extractZipWithProgress(zipFile, targetDir, null); } /** * 递归删除文件/目录 * * @param file 要删除的文件或目录 * @return 是否成功 */ public static boolean deleteRecursive(File file) { boolean success = true; if (file.isDirectory()) { File[] children = file.listFiles(); if (children != null) { for (File child : children) { success &= deleteRecursive(child); } } } if (!file.delete()) { Log.w(TAG, "删除失败: " + file.getAbsolutePath()); success = false; } return success; } /** * 删除目录内容(保留目录本身) * * @param directory 要清空的目录 * @return 是否成功 */ public static boolean deleteDirectoryContents(File directory) { if (directory == null || !directory.exists() || !directory.isDirectory()) { return false; } File[] files = directory.listFiles(); if (files == null) return true; // 空目录 boolean success = true; for (File file : files) { if (file.isDirectory()) { success &= deleteRecursive(file); } else { if (!file.delete()) { Log.w(TAG, "删除文件失败: " + file.getAbsolutePath()); success = false; } } } return success; } } 检查目前的删除方式
09-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值