Java file equals

2019-02-21 10:03发布

I don't know about you guys but at least I expected that f1 would be equal to f2 in the below code but apparently that's not the case! What's your thoughts about this? It seems like I have to write my own equals method to support it, right?

import java.io.*;

public class FileEquals
{
    public static void main(String[] args)
    {
        File f1 = new File("./hello.txt");
        File f2 = new File("hello.txt");
        System.out.println("f1: " + f1.getName());
        System.out.println("f2: " + f2.getName());
        System.out.println("f1.equals(f2) returns " + f1.equals(f2));
        System.out.println("f1.compareTo(f2) returns " + f1.compareTo(f2));
    }
}

7条回答
劫难
2楼-- · 2019-02-21 10:31

Here is the implementation of both methods:

/**
 * Tests this abstract pathname for equality with the given object.
 * Returns <code>true</code> if and only if the argument is not
 * <code>null</code> and is an abstract pathname that denotes the same file
 * or directory as this abstract pathname.  Whether or not two abstract
 * pathnames are equal depends upon the underlying system.  On UNIX
 * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows
 * systems it is not.
 *
 * @param   obj   The object to be compared with this abstract pathname
 *
 * @return  <code>true</code> if and only if the objects are the same;
 *          <code>false</code> otherwise
 */
public boolean equals(Object obj) {
    if ((obj != null) && (obj instanceof File)) {
        return compareTo((File)obj) == 0;
    }
    return false;
}
/**
 * Compares two abstract pathnames lexicographically.  The ordering
 * defined by this method depends upon the underlying system.  On UNIX
 * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows
 * systems it is not.
 *
 * @param   pathname  The abstract pathname to be compared to this abstract
 *                    pathname
 *
 * @return  Zero if the argument is equal to this abstract pathname, a
 *          value less than zero if this abstract pathname is
 *          lexicographically less than the argument, or a value greater
 *          than zero if this abstract pathname is lexicographically
 *          greater than the argument
 *
 * @since   1.2
 */
public int compareTo(File pathname) {
    return fs.compare(this, pathname);
}
查看更多
小情绪 Triste *
3楼-- · 2019-02-21 10:40

If you just want to check if the files are the same based on their path use

java.nio.file.Files#isSameFile

E.g.

Assert.assertTrue(Files.isSameFile(
     new File("some-project\.\hello.txt").toPath(),
     new File("some-project\hello.txt").toPath()
));
查看更多
时光不老,我们不散
4楼-- · 2019-02-21 10:42

If you are using windows see class Win32FileSystem

The comparison method is like below, so it is very normal that your file objects are different.

    public int compare(File f1, File f2) {
      return f1.getPath().compareToIgnoreCase(f2.getPath());
    }

Add those lines to your code as well

        System.out.println(f1.getPath());
        System.out.println(f2.getPath());

and it will print

.\hello.txt
hello.txt

Hence they are not equal as the comparison is made using path proeprty of File object

查看更多
虎瘦雄心在
5楼-- · 2019-02-21 10:49

To properly test equals, you must call getCanonicalFile(). e.g.

public static void main(String[] args) throws IOException
   {
       File f1 = new File("./hello.txt").getCanonicalFile();
       File f2 = new File("hello.txt").getCanonicalFile();
       System.out.println("f1: " + f1.getAbsolutePath());
       System.out.println("f2: " + f2.getAbsolutePath());
       System.out.println("f1.equals(f2) returns " + f1.equals(f2));
       System.out.println("f1.compareTo(f2) returns " + f1.compareTo(f2));
   }

Will return true for equals. Note that getCanonicalFile may throw an IOException so I added that to the method signature.

查看更多
放荡不羁爱自由
6楼-- · 2019-02-21 10:50

The quicker way I found to diff on two files is below.

That's just proposition to work it around.

Not sure about the performance (what if files are 10 GB each?)

    File file = new File("/tmp/file.txt");
    File secondFile = new File("/tmp/secondFile.txt");

    // Bytes diff
    byte[] b1 = Files.readAllBytes(file.toPath());
    byte[] b2 = Files.readAllBytes(secondFile.toPath());

    boolean equals = Arrays.equals(b1, b2);

    System.out.println("the same? " + equals);

    // List Diff
    List<String> c1 = Files.readAllLines(file.toPath());
    List<String> c2 = Files.readAllLines(secondFile.toPath());

    boolean containsAll = c1.containsAll(c2);
    System.out.println("the same? " + containsAll);                
}

EDIT

But still, diff utility on unix system would be much quicker and verbose. Depends what you need to compare.

查看更多
太酷不给撩
7楼-- · 2019-02-21 10:52

Not, it's not the case. Because equals is comparing equality of absolute paths (in your case above it is something like:

some-project\.\hello.txt
some-project\hello.txt

So they are naturally different.

It seems like I have to write my own equals method to support it, right?

Probably yes. But first of all, you have to know what you want to compare? Only pathnames? If yes, compare its canonical path in this way:

f1.getCanonicalPath().equals(f2.getCanonicalPath())

But if you want compare content of two different files, then yes, you should write your own method - or simply just copy from somewhere on the internet.

查看更多
登录 后发表回答