5
\$\begingroup\$

I wrote a function in Java that edit file name, and replace each space char into dash char. Currently I iterate all the files in a specific directory, iterate in each file name, creating a new file name, and replace the file in the directory.

I guess that the current complexity is \$O(N*M)\$ with

  • \$N\$ being the number of files in directory and
  • \$M\$ being the number of chars in each file.

Can the run-time-complexity be improved?

public static void editSpace(String source, String target) {

    // Source directory where all the files are there
    File dir = new File(source);
    File[] directoryListing = dir.listFiles();

    // Iterate in each file in the directory
    for (File file : directoryListing) {

        String childName = file.getName();
        String childNameNew = "";

        // Iterate in each file name and change every space char to dash char
        for (int i = 0; i < childName.length(); i++) {

            if (childName.charAt(i) == ' ') {
                childNameNew += "-";
            } else {
                childNameNew += childName.charAt(i);
            }
        }

        // Update the new directory of the child
        String childDir = target + "\\" + childNameNew;

        // Renaming the file and moving it to a new location
        if (!(childNameNew.equals(""))
                && (file.renameTo(new File(childDir)))) {

            // If file copied successfully then delete the original file .
            file.delete();

            // Print message
            System.out.println(childName + " File moved successfully to "
                    + childDir);
        }

        // Moving failed
        else {
            // Print message
            System.out.println(childName + " Failed to move the file to "
                    + childDir);
        }
    }

}
\$\endgroup\$
4
\$\begingroup\$

Hello and Welcome to Code Review. The runtime complexity in terms of reading characters (n files * m characters) for substitution cannot be improved, you can use the String replace(char oldChar, char newChar) method:

String childName = file.getName();
String childNameNew = "";
for (int i = 0; i < childName.length(); i++) {
    if (childName.charAt(i) == ' ') {
        childNameNew += "-";
    } else {
        childNameNew += childName.charAt(i);
    }
}

You can substitute your block with one line:

String childNameNew = childName.replace(' ', '-');

From Java 7 it is discouraged using File renameTo method, you can use from class Files the move method and you can write the mv file part of your code in this way:

// Calculate oldPath and newPath for your file with Paths.get method
try {
    Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING);  
    System.out.println(oldPath + " File moved successfully to " + newPath);
} catch (IOException e) {
    System.out.println(oldPath + " Failed moved the file to " + newPath);   
}
\$\endgroup\$
2
\$\begingroup\$

As dariosicily said, the complexity can not be improved but there are some really dirty tricks you can do to improve performance by minimizing the number of objects being created... Keep in mind that this optimization is a bit useless if you only have a few thousand files with short names or if the number of files needing renaming is large compared to the total number of files.

Instead of listing all files and creating a File object to represent them and then going through the file names, you can create a FileNameFilter that does the renaming before creating any unnecessary objects.

class RenamingFileNameFilter implements FilenameFilter {
    // Reuse the StringBuilder for each file.
    final StringBuilder newName = new StringBuilder(64);

    @Override
    public boolean accept(File dir, String name) {
        newName.setLength(0);

        // Boolean flag that keeps track if file name changed.
        // Do the replacing manually to avoid a second iteration over
        // the file name to see if renaming is actually needed.
        boolean renameNeeded = false;
        for (int i = 0; i < name.length(); i++) {
            final char ch = name.charAt(i);
            if (ch == ' ') {
                renameNeeded = true;
                ch = '-';
            }
            newName.append(ch);
        }

        // Only create File objects if the file needs renaming.
        if (renameNeeded) {
            File oldFile = new File(dir, name);
            File newFile = new File(dir, newName.toString());
            oldFile.renameTo(newFile);
        }

        return false;
    }
}

Pass the filter to File.list(FileNameFilter) and the files are renamed. However, this abuses the FileNameFilter contract by introducing a major side effect to a component that is supposed to be "read only", so it will confuse people who maintain the code, unless you document and name it really carefully.

Also, add error checking. :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.