File.listFiles 文件过滤器接口 FilenameFilter 和 FileFilter
在操作读取文件的时候,经常会遇到需要过滤某些格式的文件,比如图片或者 jar 包文件,对于这种情况,Java 提供了文件过滤器接口 FilenameFilter
和 FileFilter
。
FilenameFilter 和 FileFilter 都是用来过滤文件,例如过滤,以 .jpg
或者 .java
结尾的文件,通过看他们的源码:通过使用 File
类中 String[] list(FilenameFilter filter)
或者 public File[] listFiles(FileFilter filter)
方法,把 FilenameFilter 或者 FileFilter 接口对象作为参数传入,通过实现接口里面的 boolean accept(File dir, String name)
或者 boolean accept(File pathname)
方法来过滤出满足条件的文件。
那么他们有什么区别呢?通过下面的源码片段可以看出,FilenameFilter 的 accept
方法接受的参数为待处理文件父目录对象和文件名称字符串,而 FileFilter 的 accept
接受的参数为待处理文件对象。另外 File.list
接口返回的是文件名称字符串数组 String[]
,所以自然的,只提供了参数为 FilenameFilter
的方法。
下面通过一个假定的需求来展示这两个过滤器的使用:我们做了一个加载器,需要读取文件夹下的驱动文件,驱动文件以 .jar
或 .zip
的后缀形式存在,该文件夹下还有一些说明文档,我们的代码片段就是要读取所有的驱动文件,忽略其它文档。
驱动文件目录下的文件如图所示:
FilenameFilter
看下 FilenameFilter 接口的 accept
原型:
/**
* Tests if a specified file should be included in a file list.
*
* @param dir 被找到的文件所在的目录
* @param name 被找到的文件的文件名
* @return `true` if and only if the name should be
* included in the file list; `false` otherwise.
*/
boolean accept(File dir, String name);
这个方法返回值:当且仅当该文件符合 accept
实现的逻辑时返回 true;否则返回 false。
我们需要做的就是实现 accept
方法,加入我们的实现逻辑。
@Test
public void testFilenameFilter()
{
File driverDir = new File("E:\\driver");
File[] drivers = driverDir.listFiles(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
name = name.toLowerCase();
return name.endsWith(".jar") || name.endsWith(".zip");
}
});
for (File driver : drivers)
{
System.out.println(driver.getName());
}
}
输出过滤后的文件名称:
jtds-1.3.1.jar
mysql-connector-java-5.1.24-bin.jar
ojdbc6.jar
orai18n.jar
pg.zip
xdb6.jar
FileFilter
FileFilter 接口的 accept
原型:
/**
* Tests whether or not the specified abstract pathname should be
* included in a pathname list.
*
* @param pathname 被找到文件的 File 对象
* @return `true` if and only if `pathname`
* should be included
*/
boolean accept(File pathname);
返回值和 FilenameFilter 是一样的,当且仅当该文件符合 accept
实现的逻辑时返回 true;否则返回 false。
我们使用 FileFilter 来重新实现上面的需求。
@Test
public void testFileFilter()
{
File driverDir = new File("E:\\driver");
File[] drivers = driverDir.listFiles(new FileFilter()
{
@Override
public boolean accept(File pathname)
{
String name = pathname.getName().toLowerCase();
return name.endsWith(".jar") || name.endsWith(".zip");
}
});
for (File driver : drivers)
{
System.out.println(driver.getName());
}
}
输出结果是一样的:
jtds-1.3.1.jar
mysql-connector-java-5.1.24-bin.jar
ojdbc6.jar
orai18n.jar
pg.zip
xdb6.jar
lambda
JDK 8 为 Java 带来了 lambda 特性,使用我们能够使用更简洁的方式来书写 Java 代码。在源码包中使用 @FunctionalInterface
注解标注的接口都是支持 lambda 实现的,我们也可以使用这个注解来声明自己的 lambda 接口,这不是本文的主题,暂时不表。
FilenameFilter
和 FileFilter
文件过滤器接口也是支持 lambda 的,因为他们的接口声明都有 @FunctionalInterface
注解。
@FunctionalInterface
public interface FilenameFilter {
boolean accept(File dir, String name);
}
@FunctionalInterface
public interface FileFilter {
boolean accept(File pathname);
}
我们使用 lambda 的方式来重写上面 FilenameFilter
实现需求的代码:
@Test
public void testFilenameFilter()
{
File driverDir = new File("E:\\driver");
File[] drivers = driverDir.listFiles((File dir, String name) ->
{
name = name.toLowerCase();
return name.endsWith(".jar") || name.endsWith(".zip");
});
Arrays.asList(drivers).forEach(System.out::println);
}
是不是非常简洁!
关于如何使用 File.list(FilenameFilter)
接口和使用 lambda 重写上面 FileFilter
实现需求的代码,留给读者尝试。