Reputation: 451
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));
}
}
Upvotes: 25
Views: 28192
Reputation: 10586
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.
Upvotes: 34
Reputation: 12625
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()
));
Upvotes: 2
Reputation: 1564
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.
Upvotes: 2
Reputation: 1143
If you only want to compare the CONTENTS of each file, you could read the contents into a byte array like this:
byte[] f1 = Files.readAllBytes(file1);
byte[] f2 = Files.readAllBytes(file2);
And then compare exactly what you want from there.
Note that this method call only exists in Java 7. For older versions, Guava and Apache have methods to do similar but with different names and details.
Edit: OR a better option (especially if you're comparing large files) might be to simply compare byte by byte rather than loading the entire file into memory, like this:
FileInputStream f1 = new FileInputStream(file1);
DataInputStream d1 = new DataInputStream(f1);
FileInputStream f2 = new FileInputStream(file2);
DataInputStream d2 = new DataInputStream(f2);
byte b1 = d1.readByte();
byte b2 = d2.readByte();
And then compare from there.
Upvotes: 5
Reputation: 14558
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
Upvotes: 0
Reputation: 15729
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.
Upvotes: 9
Reputation: 117587
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);
}
Upvotes: 1