Finding a the "sub-path" in a hierarchical system, Java - java

I have this Hierarchical file system that is built like so:
class StorageUnit{
String name;
StorageUnit(String nameInput){
this.name = nameInput;
}
}
class Folder extends StorageUnit{
ArrayList<StorageUnit> items = new ArrayList<StorageUnit>();
void addContent(StorageUnit item){
this.items.add(item);
}
}
class File extends StorageUnit{}
Given a path (not necessarily the complete path) for example for this system:
A
| B
| | C
| | | code.java
| | bye.log
| Aa.txt
| aa.py
a path be given like so:
B/C/code.java
I am trying to make a function return the specified File if there exists a path where the given path is part of it, for example if it finds the path:
A/B/C/code.java
the function would return code.java since the found path contains the given path,
else it returns null.
I thought about recursion, however, can't figure out what to do it if I end up in a file that doesn't contain the file am looking for:
//The entire code here belongs to the class StorageUnit
public File findFile(String path){
String[] paths = path.split("/");
return findFile_aux(paths,0);
}
public File findFile_aux(String[] paths, int i){
for(StorageItem item : this.items){
if(item.getName().equals(paths[i])){
if(i == paths.length-1) {
return ((File) item);
}
item.findFile_aux(paths, i+1);
}
item.findFile_aux(paths, i);
}
return null;
}

You missed to return your recursive call
You have to give the current folder to the recursive call, otherwise you allways look at A's items.
You get a ClassCastException if your path leads to a folder
private static File find(Folder root, String path) {
return find(path.split("/"), Collections.singletonList(root), 0);
}
private static File find(String[] path, List<StorageUnit> units, int depth) {
if (depth >= path.length) {
// file not found
return null;
}
String currentStep = path[depth];
for (StorageUnit unit : units) {
if (currentStep.equals(unit.name)) {
// step found
if (depth == path.length - 1) {
// last step -> unit found
if (unit instanceof File) {
return (File) unit;
} else {
// path doesn't lead to a file (point 3)
System.err.println("path doesn't leed to a file but " + unit);
return null;
}
} else if (unit instanceof Folder) {
// next step
Folder folder = (Folder) unit;
// recursive call with items of current folder (point 2)
File file = find(path, folder.items, depth + 1);
if (file != null) {
// found it (point 1)
return file;
}
// else -> not found -> ignore unit
}
}
}
return null;
}

It is not nessesary to use recursion, you can use SimpleFileVisitor:
public class FileFounder {
private Path path;
private List<String> fileList = new ArrayList<>();
public FileFounder(Path path) {
this.path = path;
}
public List<String> getStringPathList() throws IOException {
Files.walkFileTree(path, new SFV());
return fileList;
}
private class SFV extends SimpleFileVisitor<Path> {
#Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
fileList.add(path.toAbsolutePath().toString());
return CONTINUE;
}
}
}
Usage:
FileFounder fileFounder = new FileFounder(rootPath);
List<String> listOfStringAdressOfFilesInRoot = fileFounder.getStringPathList();

Related

Java, search files of given pattern and get the directory names and complete filenames

I am new to Java. Looking for code to search for files with .ofg extension in all the sub-directories of /var/data.
The desired outputs are
the subdirectory name(s), which has the files with those files
the full names of the files
the number of those files in that subdirectory.
There are some tutorials available, but nothing i could find fitting to my code base; like
public class FindFiles {
int inProcThreshold = 0;
protected File recurfile(File file) {
File[] dirlist = file.listFiles();
for (File f : dirlist) {
if (f.isDirectory()) {
return f;
}
}
return null;
}
protected int numOfInProcs(String location, int level, int maxdepth) {
File base = new File(location);
File[] firstlevelfiles = base.listFiles();
while (level <= maxdepth) {
for (File afile : firstlevelfiles) {
if (afile.isDirectory()) {
base = recurfile(afile);
} else {
if (afile.getName().endsWith(".txt")) {
inProcThreshold++;
}
}
}
level++;
}
return inProcThreshold;
}
public static void main(String[] args) {
FindFiles test = new FindFiles();
String dirToList = "I:\\TEST-FOLDER";
String ext = ".txt";
int count = test.numOfInProcs(dirToList, 0, 10);
System.out.println("Number of txt files are " + count);
}
}
This is the code I am trying but it returns 0 as output to me. I am trying to search for files with extension.txt in the I:\TEST-FOLDER subfolders.
Use this filter by giving directory addres in dirName Parameter it will list all directories with extension .ofg
import java.io.File;
import java.io.FilenameFilter;
public class Filter {
public File[] finder( String dirName){
File dir = new File(dirName);
return dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String filename)
{ return filename.endsWith(".ofg"); }
} );
}
}
I think what you are looking for is Files.find. Pass it a Predicate which checks that path.toString().endsWith(".ofg"),
It will return a Stream of Path objects representing the matching files. You can extract all the data you want by iterating on this Stream.
If you are not required to write the recursive part yourself (for practice or as task), you could use Files#walkFileTree with a custom implementation of the FileVisitor Interface (As #Mena proposed in his comment).
Extend the SimpleFileVisitor class (or implement the FileVisitor interface) and provide your code to be executed on each file:
public class OfgFolderCollectingFileVisitor extends SimpleFileVisitor<Path> {
/** Stores the matching file paths */
private final List<Path> collectedPaths = new LinkedList<>();
#Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
// check if the current file is an .ofg file
if (file.toString().endsWith(".ofg")) {
// it is -> add it's containing folder to the collection
this.collectedPaths.add(file.getParent());
}
return super.visitFile(file, attrs);
}
public List<Path> getCollectedPaths() {
return this.collectedPaths;
}
}
Then pass an instance of your implementation to Files#walkFileTree and check the collected paths afterwards:
final OfgFolderCollectingFileVisitor visitor = new OfgFolderCollectingFileVisitor();
try {
Files.walkFileTree(Paths.get("/var/data"), visitor);
} catch (final IOException ex) {
ex.printStackTrace();
return;
}
// let's see if something matched our criteria
final List<Path> ofgContainers = visitor.getCollectedPaths();
System.out.printf("Files found: %d%n", ofgContainers.size());
if (!ofgContainers.isEmpty()) {
System.out.printf("%nContaining directories:%n");
for (final Path ofgContainer : ofgContainers) {
System.out.printf("- %s%n", ofgContaininer);
}
}
Here is some example output (yes, folder2 and it's subfolder contain an .ofg file)
Files found: 3
Containing directories:
- \var\data\folder1\folder1.1
- \var\data\folder2
- \var\data\folder2\folder2.2

How to remove duplicate files with same name but different extension?

I have a large number of images in a directory. The problem with some of the images is that they have duplicates with the same name but different extension, e.g. image1.jpg, image1.jpeg, image1.png, which are all the same images, same name but different extensions. How can I find and remove these duplicates using Java? There are a lot of tools for finding duplicates but I cant find any tool or script for this specific problem. Any help would be greatly appreciated.
The only way to achieve this, imho, is creating an helper class:
public class FileUtil {
String fileName;
File file;
boolean delete = true;
public FileUtil(String fileName, File file) {
super();
this.fileName = fileName.substring(0, fileName.indexOf("."));
this.file = file;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public boolean isDelete() {
return delete;
}
public void setDelete(boolean delete) {
this.delete = delete;
}
#Override
public String toString() {
return "FileUtil [fileName=" + fileName + ", file=" + file + ", delete=" + delete + "]";
}
}
then you can use this for collecting and deleting your items:
try (Stream<Path> paths = Files.walk(Paths.get("c:/yourPath/"))) {
List<FileUtil> listUtil = new ArrayList<FileUtil>();
paths
.filter(Files::isRegularFile)
.map(filePath -> filePath.toFile())
.collect(Collectors.toList())
.forEach(file -> listUtil.add(new FileUtil(file.getName(), file)));
Map<String, List<FileUtil>> collect = listUtil.stream()
.collect(Collectors.groupingBy(FileUtil::getFileName));
for(String key : collect.keySet() ) {
List<FileUtil> list = collect.get(key);
if(list.size() > 1) {
list.stream().findFirst().ifPresent(f -> f.setDelete(false));
list.stream()
.filter(fileUtil -> fileUtil.isDelete())
.forEach(fileUtil -> fileUtil.getFile().delete());
}
}
} catch (IOException e) {
e.printStackTrace();
}
In this way I'm keeping a random item, if you prefer you can modify the class for keeping only the extension that you want, for example .png
I hope this helps :)
Read in all your files into a List of some sort:
List<File> filesInFolder = Files.walk(Paths.get("\\path\\to\\folder"))
.filter(Files::isRegularFile)
.map(Path::toFile)
.collect(Collectors.toList());
Then just loop through them and delete if the file doesn't end with the extension you want:
filesInFolder.stream().filter((file) -> (!file.toString().endsWith(".jpg"))).forEach((file) -> {
file.delete();
});
You can tailor this to your specific need.
Here is MCVE:
This example implements a Set to remove duplicate images automatically by only providing the path of the folder/directory that contains the images (just a different idea to show the other available options and how to avail of OO Features in Java)
import java.io.File;
import java.util.HashSet;
import java.util.Set;
public class DuplicateRemover {
// inner class to represent an image
class Image{
String path; // the absolute path of image file as a String
// constructor
public Image(String path) {
this.path = path;
}
#Override
public boolean equals(Object o) {
if(o instanceof Image){
// if both base names are equal -> delete the old one
if(getBaseName(this.path).equals(getBaseName(((Image)o).path))){
File file = new File(this.path);
return file.delete();
}
}
return false;
}
#Override
public int hashCode() {
return 0; // in this case, only "equals()" method is considered for duplicate check
}
/**
* This method to get the Base name of the image from the path
* #param fileName
* #return
*/
private String getBaseName(String fileName) {
int index = fileName.lastIndexOf('.');
if (index == -1) { return fileName; }
else { return fileName.substring(0, index); }
}
}
Set<Image> images; // a set of image files
//constructor
public DuplicateRemover(){
images = new HashSet<>();
}
/**
* Get the all the images from the given folder
* and loop through all files to add them to the images set
* #param dirPath
*/
public void run(String dirPath){
File dir = new File(dirPath);
File[] listOfImages = dir.listFiles();
for (File f : listOfImages){
if (f.isFile()) {
images.add(new Image(f.getAbsolutePath()));
}
}
}
//TEST
public static void main(String[] args) {
String dirPath = "C:\\Users\\Yahya Almardeny\\Desktop\\folder";
/* dir contains: {image1.png, image1.jpeg, image1.jpg, image2.png} */
DuplicateRemover dr = new DuplicateRemover();
// the images set will delete any duplicate image from the folder
// according to the logic we provided in the "equals()" method
dr.run(dirPath);
// print what images left in the folder
for(Image image : dr.images) {
System.out.println(image.path);
}
//Note that you can use the set for further manipulation if you have in later
}
}
Result
C:\Users\Yahya Almardeny\Desktop\folder\image1.jpeg
C:\Users\Yahya Almardeny\Desktop\folder\image2.png

FileSystem Implementation, Recursively listing files

I'm working on implementing the ls method in my program. I need to create a recursive method that will walk through my FileSystem.
Here is my FileSystem implementation right now:
import java.util.ArrayList;
public class FileSystem {
private Directory root;
private Directory wDir;
private ArrayList<File> files = new ArrayList<File>();
// Constructor
public FileSystem() {
}
// Constructor with parameters
public FileSystem(Directory root) {
this.root = root;
wDir = root;
files.add(root);
}
// Returns the FileSystem's files
public ArrayList<File> getFiles() {
return files;
}
// Returns the working directory
public Directory getWDir() {
return wDir;
}
// Sets the working directory
public void setWDir(Directory d) {
wDir = d;
}
// Returns the root file. This will always be / in our program
public File getRoot() {
return root;
}
public File getFile(File f, String name) {
if (f.isDirectory()) {
for (File c : ((Directory) f).getChildren()) {
if (c.getName().equals(name))
return c;
}
}
return null;
}
// Currently only used in cat method, getFile is better
File findFile(File f, String name) {
if (f.getName().equals(name))
return f;
File file = null;
if (f.isDirectory()) {
for (File c : ((Directory) f).getChildren()) {
file = findFile(c, name);
if (file != null)
break;
}
}
return file;
}
// Returns true if file is found
boolean isFile(String name) {
File file = null;
file = getFile(wDir, name);
if (file != null) {
return true;
}
return false;
}
// Creates Directory
public void mkdir(String path) {
files.add(new Directory(path));
int size = files.size();
// Sets the parent
files.get(size - 1).setParent(wDir);
// Sets the child
wDir.addChild(files.get(size - 1));
}
// Changes working directory
public void cd(String s) {
if (s.equals("..")) {
if (wDir != root) {
wDir = wDir.getParent();
}
} else if (s.equals("/")) {
wDir = root;
} else {
wDir = (Directory) getFile(wDir, s);
}
}
// Provides absolute filename
public void pwd() {
if (wDir == root) {
System.out.println("/");
} else {
System.out.println(wDir.getPath());
}
}
// Lists children of current working directory
public void ls() {
ArrayList<File> children = wDir.getChildren();
if (children != null) {
for (int i = 0; i < children.size(); i++) {
String childName = children.get(i).getName();
System.out.print(childName + " ");
}
}
}
// Lists children of file(s) inputted by user
public void ls(File f) {
String name = f.getName();
if (f instanceof TextFile) {
System.out.println(f.getPath());
} else {
ArrayList<File> children = ((Directory) f).getChildren();
if (children != null) {
for (int i = 0; i < children.size(); i++) {
String childName = children.get(i).getName();
System.out.print(childName + " ");
}
}
}
}
// Creates a TextFile or edit's TextFile's content if already exists in the
// tree
public void edit(String name, String content) {
files.add(new TextFile(name, content));
// Setting TextFile parent
files.get(files.size() - 1).setParent(wDir);
// Setting Parent's child
wDir.addChild(files.get(files.size() - 1));
}
// Prints the content of TextFile
public void cat(String name) {
File f = findFile(root, name);
System.out.println(((TextFile) f).getContent());
}
}
As an example of what it needs to do, let's say I have a tree like this:
/
/ \
a b
/ \
x c
/ \
y d
If the user were to enter: ls -r a, my main class would convert that String value using the getFile method, and I would enter that into my recursive function. It would then make use of either ls() or ls(File f), and my main program would output something like this:
a:
x
a/x:
y
a/x/y:
How should I go about creating this method?
Also I should note that I have a Main class, a File class, and a TextFile and Directory class that inherit File.
Any other information that is needed just let me know and I will update this post with it.
You could use something like this:
public void ls(File f) {
System.out.println(f); //or whatever is needed to print the filename
if(f instanceof Directory) {
List<File> fileList = ((Directory)f).getFiles();
//with Java 8
fileList.forEach(subFile -> System.out.println(subFile));
fileList.forEach(subFile -> ls(subFile));
//without Java 8
for(File subFile : fileList) {
System.out.println(subFile);
}
for(File subFile : fileList) {
ls(subFile);
}
System.out.println();
}
}
Basically the first loop is printing all the files in the current directory and the second loop is doing that for all the subdirectories. If the File is not a Directory only it's name is printed. Here I'm assuming that your Directory class has a getFiles() method that returns a List of all Files in the Directory

copying a file from local share to linux local drive

Please mention me the link if this is a duplicate and has the apt answer.
The actual theme of my project is to copy a '.zip' file (installation file) from the local server to any machines based on OS. Let the path be //123.1.23.3.
In windows i can copy it directly like FileUtils.copyFiles(srcFile, destFile).
In Linux, I don know how to achieve it. I even did like considering the srcFile to be SmbFile(i.e samba file) and the destFile to be a File and the problem here is, either i should use
`FileUtils.copyFiles(srcFile, destFile)`. (If both of them are 'File's)
or
`srcFile.copyTo(destFile)` (If both files are 'SmbFile's)
but both is not possible here bcoz srcFile - SmbFile(file in local server) and destFile - File(local drive).
If anyone advice me to use streams to copy it, is there any way in linux to directly copy a zip file without extracting it as i did in windows (in a single step).
Because i have a seperate methods to extract or tar the files in windows and linux respectively, and if i use streams here i need to extract it and there would no need of the above seperate methods.
i think i made it clear.Thank u.
it can be achieved using IOUtils.copy(src.getInputStream(), new FileOutputStream(destFile));
Hi you can use the JAVA NIO package for the file copy utility
import java.nio.file.*;
import static java.nio.file.StandardCopyOption.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import java.io.IOException;
import java.util.*;
public class Copy {
/**
* Returns {#code true} if okay to overwrite a file ("cp -i")
*/
static boolean okayToOverwrite(Path file) {
String answer = System.console().readLine("overwrite %s (yes/no)? ", file);
return (answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes"));
}
/**
* Copy source file to target location. If {#code prompt} is true then
* prompt user to overwrite target if it exists. The {#code preserve}
* parameter determines if file attributes should be copied/preserved.
*/
static void copyFile(Path source, Path target, boolean prompt, boolean preserve) {
CopyOption[] options = (preserve) ?
new CopyOption[] { COPY_ATTRIBUTES, REPLACE_EXISTING } :
new CopyOption[] { REPLACE_EXISTING };
if (!prompt || Files.notExists(target) || okayToOverwrite(target)) {
try {
Files.copy(source, target, options);
} catch (IOException x) {
System.err.format("Unable to copy: %s: %s%n", source, x);
}
}
}
/**
* A {#code FileVisitor} that copies a file-tree ("cp -r")
*/
static class TreeCopier implements FileVisitor<Path> {
private final Path source;
private final Path target;
private final boolean prompt;
private final boolean preserve;
TreeCopier(Path source, Path target, boolean prompt, boolean preserve) {
this.source = source;
this.target = target;
this.prompt = prompt;
this.preserve = preserve;
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
// before visiting entries in a directory we copy the directory
// (okay if directory already exists).
CopyOption[] options = (preserve) ?
new CopyOption[] { COPY_ATTRIBUTES } : new CopyOption[0];
Path newdir = target.resolve(source.relativize(dir));
try {
Files.copy(dir, newdir, options);
} catch (FileAlreadyExistsException x) {
// ignore
} catch (IOException x) {
System.err.format("Unable to create: %s: %s%n", newdir, x);
return SKIP_SUBTREE;
}
return CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
copyFile(file, target.resolve(source.relativize(file)),
prompt, preserve);
return CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
// fix up modification time of directory when done
if (exc == null && preserve) {
Path newdir = target.resolve(source.relativize(dir));
try {
FileTime time = Files.getLastModifiedTime(dir);
Files.setLastModifiedTime(newdir, time);
} catch (IOException x) {
System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
}
}
return CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
if (exc instanceof FileSystemLoopException) {
System.err.println("cycle detected: " + file);
} else {
System.err.format("Unable to copy: %s: %s%n", file, exc);
}
return CONTINUE;
}
}
static void usage() {
System.err.println("java Copy [-ip] source... target");
System.err.println("java Copy -r [-ip] source-dir... target");
System.exit(-1);
}
public static void main(String[] args) throws IOException {
boolean recursive = false;
boolean prompt = false;
boolean preserve = false;
// process options
int argi = 0;
while (argi < args.length) {
String arg = args[argi];
if (!arg.startsWith("-"))
break;
if (arg.length() < 2)
usage();
for (int i=1; i<arg.length(); i++) {
char c = arg.charAt(i);
switch (c) {
case 'r' : recursive = true; break;
case 'i' : prompt = true; break;
case 'p' : preserve = true; break;
default : usage();
}
}
argi++;
}
// remaining arguments are the source files(s) and the target location
int remaining = args.length - argi;
if (remaining < 2)
usage();
Path[] source = new Path[remaining-1];
int i=0;
while (remaining > 1) {
source[i++] = Paths.get(args[argi++]);
remaining--;
}
Path target = Paths.get(args[argi]);
// check if target is a directory
boolean isDir = Files.isDirectory(target);
// copy each source file/directory to target
for (i=0; i<source.length; i++) {
Path dest = (isDir) ? target.resolve(source[i].getFileName()) : target;
if (recursive) {
// follow links when copying files
EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
TreeCopier tc = new TreeCopier(source[i], dest, prompt, preserve);
Files.walkFileTree(source[i], opts, Integer.MAX_VALUE, tc);
} else {
// not recursive so source must not be a directory
if (Files.isDirectory(source[i])) {
System.err.format("%s: is a directory%n", source[i]);
continue;
}
copyFile(source[i], dest, prompt, preserve);
}
}
}
}

Search for file in directory with multiple directories

Here's my goal. I want to be able to pass a parent directory and a filename to a method that searches for that specific file in the directory and any sub-directories. Below is the code I have been working with but can not get it to do exactly what I want. It will find the file I specify but will not return anything.
private static File findFile(File dir, String name) {
String file = "";
File[] dirlist = dir.listFiles();
search:
for(int i = 0; i < dirlist.length; i++) {
if(dirlist[i].isDirectory()) {
findFile(dirlist[i], name);
} else if(dirlist[i].getName().matches(name)) {
file = dirlist[i].toString();
break search;
}
}
return new File(file);
}
I know that when the method finds a directory and calls itself it resets the file variable which is where I am storing the found file. So that is why I am getting a blank return. I am not sure how to accomplish this goal or if it's even possible.
The problem is that you're not returning anything from the recursive call:
if(dirlist[i].isDirectory()) {
findFile(dirlist[i], name); // <-- here
} else if(dirlist[i].getName().matches(name)) {
I would do the following:
private static File findFile(File dir, String name) {
File result = null; // no need to store result as String, you're returning File anyway
File[] dirlist = dir.listFiles();
for(int i = 0; i < dirlist.length; i++) {
if(dirlist[i].isDirectory()) {
result = findFile(dirlist[i], name);
if (result!=null) break; // recursive call found the file; terminate the loop
} else if(dirlist[i].getName().matches(name)) {
return dirlist[i]; // found the file; return it
}
}
return result; // will return null if we didn't find anything
}
In fact there are many solutions to do the job.
I assume that you want to find a unique file (or the first one) found in a directory tree that matches with the fileName.
It is a problem of optimization because there are multiple ways to explore solutions, and we want to find an acceptable solution.
1- Solution using FileUtils.listFiles
public static File searchFileWithFileUtils(final File file, final String fileName) {
File target = null;
if(file.isDirectory()) {
Collection<File> files = FileUtils.listFiles(file, null, true);
for (File currFile : files) {
if (currFile.isFile() && currFile.getName().equals(fileName)) {
target = currFile;
break;
}
}
}
return target;
}
The solution using the library FileUtils is not a suitable solution because the method FileUtils#listFiles() loads all the directory/folder tree (the cost is expensive !).
We don't need to know all the tree, we can choose a better algorithm which stops when the file is found.
2- Recursive Solution
public static File searchFileRecursive(final File file, final String search) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
File target = searchFileRecursive(f, search);
if(target != null) {
return target;
}
}
} else {
if (search.equals(file.getName())) {
return file;
}
}
return null;
}
The algorithm tests if the file exists inside any folder. If not, it tries subfolder of the current folder... recursively. If the file is not found in the current branch it tries another subfolder.
The exploration is deep, and for any file in a deepness of 1 the algorithm will explore the entirety of previous subfolders (previous branches are completely explored !).
This algorithm has the best performances for files in a deep location inside the first branch.
In the majority of cases, the file location is not deep, so let explore another algorithm that works in most of cases.
3- Fastest Solution : exploration by deepness
public static File searchFileByDeepness(final String directoryName, final String fileName) {
File target = null;
if(directoryName != null && fileName != null) {
File directory = new File(directoryName);
if(directory.isDirectory()) {
File file = new File(directoryName, fileName);
if(file.isFile()) {
target = file;
}
else {
List<File> subDirectories = getSubDirectories(directory);
do {
List<File> subSubDirectories = new ArrayList<File>();
for(File subDirectory : subDirectories) {
File fileInSubDirectory = new File(subDirectory, fileName);
if(fileInSubDirectory.isFile()) {
return fileInSubDirectory;
}
subSubDirectories.addAll(getSubDirectories(subDirectory));
}
subDirectories = subSubDirectories;
} while(subDirectories != null && ! subDirectories.isEmpty());
}
}
}
return target;
}
private static List<File> getSubDirectories(final File directory) {
File[] subDirectories = directory.listFiles(new FilenameFilter() {
#Override
public boolean accept(final File current, final String name) {
return new File(current, name).isDirectory();
}
});
return Arrays.asList(subDirectories);
}
For each deepness, the algorithm searches the file inside all folders of the same level. If the file is not found, it tries the next level (deepness++).
Due to the parallel exploration (symmetry), this solution is suitable in most of cases.
Comparison:
public class FileLocationFinder {
public static void main(final String[] args) {
String rootFolder = args[0];
String fileName = args[1];
long start = System.currentTimeMillis();
File target = searchFileWithFileUtils(new File(rootFolder), fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
target = searchFileRecursive(new File(rootFolder), fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
target = searchFileByDeepness(rootFolder, fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
}
// Solution with FileUtils#listFiles
//--------------------------------------------
public static File searchFileWithFileUtils(final File file, final String fileName) {
File target = null;
if(file.isDirectory()) {
Collection<File> files = FileUtils.listFiles(file, null, true);
for (File currFile : files) {
if (currFile.isFile() && currFile.getName().equals(fileName)) {
target = currFile;
break;
}
}
}
return target;
}
// Recursive solution
//--------------------------------------------
public static File searchFileRecursive(final File file, final String search) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
File target = searchFileRecursive(f, search);
if(target != null) {
return target;
}
}
} else {
if (search.equals(file.getName())) {
return file;
}
}
return null;
}
// Fastest solution
//--------------------------------------------
public static File searchFileByDeepness(final String directoryName, final String fileName) {
File target = null;
if(directoryName != null && fileName != null) {
File directory = new File(directoryName);
if(directory.isDirectory()) {
File file = new File(directoryName, fileName);
if(file.isFile()) {
target = file;
}
else {
List<File> subDirectories = getSubDirectories(directory);
do {
List<File> subSubDirectories = new ArrayList<File>();
for(File subDirectory : subDirectories) {
File fileInSubDirectory = new File(subDirectory, fileName);
if(fileInSubDirectory.isFile()) {
return fileInSubDirectory;
}
subSubDirectories.addAll(getSubDirectories(subDirectory));
}
subDirectories = subSubDirectories;
} while(subDirectories != null && ! subDirectories.isEmpty());
}
}
}
return target;
}
private static List<File> getSubDirectories(final File directory) {
File[] subDirectories = directory.listFiles(new FilenameFilter() {
#Override
public boolean accept(final File current, final String name) {
return new File(current, name).isDirectory();
}
});
return Arrays.asList(subDirectories);
}
}
Result:
searchFileWithFileUtils: 20186ms | searchFileRecursive: 1134ms | searchFileByDeepness: 16ms
[EDIT]
You can also use Java 8 Files API to do this job :
public static File searchFileJava8(final String rootFolder, final String fileName) {
File target = null;
Path root = Paths.get(rootFolder);
try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, (path, attr) ->
path.getFileName().toString().equals(fileName))) {
Optional<Path> path = stream.findFirst();
if(path.isPresent()) {
target = path.get().toFile();
}
}
catch (IOException e) {
}
return target;
}
But the execution time is not better (994ms).

Categories

Resources