Eu fiz um método que verifica se dois arquivos (texto ou binários não importa) são identicos ou não. Entretanto a execução do método está demorando demais quando os arquivos idênticos são um pouco maiores (no meu exemplo eu tenho várias duplicidades acima de 20 Mb) .
Um simples comando diff (unix) está resolvendo o mesmo problema muito mais rápido. Gostaria de saber se existe alguma maneira de se reescrever esse mesmo método de forma a fazê-lo ficar mais eficiente.
Alguém aí poderia me ajudar com isso?
segue o método:
publicstaticbooleanverificaDuplicidade(Stringfile1,Stringfile2){Filef1=newFile(file1);Filef2=newFile(file2);intbyte_f1;intbyte_f2;if(f1.length()==f2.length()){try{InputStreamisf1=newFileInputStream(f1);InputStreamisf2=newFileInputStream(f2);for(longi=0;i<=f1.length();i++){try{byte_f1=isf1.read();byte_f2=isf2.read();if(byte_f1!=byte_f2){isf1.close();isf2.close();returnfalse;// tamanhos iguais e conteudos diferentes}}catch(IOExceptionex){}}}catch(FileNotFoundExceptionex){}}else{returnfalse;// tamanho e conteudo diferente}returntrue;// arquivos iguais}
Por que você não cria Threads para comparar ranges de bytes? Tipo, para cada 10MB você cria uma Thread, sendo que possa ter no maximo 10 Threads comparando os ranges de um In/OutputStream.
Edit:
Use tambem BufferedOutput/InputStream que ja deve ajudar a lot
T
thingol
Seu problema não é de threads e sim de comparar os arquivos byte a byte. Isso torna a leitura de arquivos excessivamente lenta. Leia os arquivos de 1MB em 1MB (por exemplo), e compare os arrays de 1MB entre si.
T
thingol
Rode este programa em seu disco, e surpreenda-se com a quantidade de arquivos duplicados entre si.
Ele acha recursivamente os arquivos que são iguais entre si.
importjava.util.*;importjava.io.*;importjava.security.*;importjava.math.*;classAcharArquivosIguais{publicAcharArquivosIguais(){}publicvoidlistarArquivos(Filediretorio,Set<File>arquivos){File[]listagem=diretorio.listFiles();for(Filef:listagem){if(f.isDirectory()&&!f.getName().equals(".")&&!f.getName().equals("..")){listarArquivos(f,arquivos);}else{arquivos.add(f);}}}privatestaticStringhashFile(Filearq){Strings="error";try{MessageDigestdgst=MessageDigest.getInstance("SHA1");FileInputStreamfis=newFileInputStream(arq);byte[]buffer=newbyte[20480];intnBytes;dgst.reset();while((nBytes=fis.read(buffer))>0){dgst.update(buffer,0,nBytes);}byte[]bytes=dgst.digest();fis.close();BigIntegerbd=newBigInteger(bytes);s=bd.toString(16);}catch(NoSuchAlgorithmExceptionex){}catch(IOExceptionex){}returns;}publicvoidacharArquivosIguais(Collection<File>arquivos){Map<String,List><File>>hash2file=newTreeMap<String,List><File>>();for(Filef:arquivos){System.out.print("\r"+f);Stringhash=hashFile(f);List<File>files;if(!hash2file.containsKey(hash)){files=newArrayList<File>();hash2file.put(hash,files);}else{files=hash2file.get(hash);}files.add(f);}System.out.println();// Agora vamos ver, nessa lista, que arquivos têm mais de uma entrada.booleanarquivosRepetidos=false;for(Map.Entry<String,List><File>>h2f:hash2file.entrySet()){if(h2f.getValue().size()>1){arquivosRepetidos=true;System.out.println("---");System.out.println(h2f.getValue().size()+" arquivos com o hash "+h2f.getKey()+":");System.out.println("---");for(Filef:h2f.getValue()){System.out.println(" "+f);}}}if(!arquivosRepetidos){System.out.println("Não foram encontrados arquivos repetidos.");}}publicstaticvoidmain(String[]args){if(args.length!=1){System.err.println("Sintaxe: java -cp . AcharArquivosIguais diretorio");System.exit(1);}AcharArquivosIguaisaai=newAcharArquivosIguais();Set<File>arquivos=newTreeSet<File>();aai.listarArquivos(newFile(args[0]),arquivos);aai.acharArquivosIguais(arquivos);}}
mizumoto
thingol,
A ideia do array foi boa!
Usei um buffer de 1 Mb (1048576 bytes) e a comparação entre os arquivos ficou quase que instantânea.
segue o código:
public static boolean verificaDuplicidade(String file1, String file2) {
File f1 = new File(file1);
File f2 = new File(file2);
byte[] f1_buf = new byte[1048576];
byte[] f2_buf = new byte[1048576];
if (f1.length() == f2.length()) {
try {
InputStream isf1 = new FileInputStream(f1);
InputStream isf2 = new FileInputStream(f2);
try {
while (isf1.read(f1_buf) >= 0) {
isf2.read(f2_buf);
for (int j = 0; j < f1_buf.length; j++) {
if (f1_buf[j] != f2_buf[j]) {
return false;
}
}
}
} catch (IOException e) {
}
} catch (FileNotFoundException e) {
}
} else {
return false; // tamanho e conteudo diferente
}
return true; // arquivos iguais
}
Mais tarde eu vou dar uma olhada nesse exemplo aí que vc passou,
Obrigado pela ajuda!
T
thingol
O seu programa ainda está errado, se algum dos arquivos não for múltiplo de 1MB. Você precisa ver quantos bytes foram lidos (é o retorno de read), e comparar apenas a quantidade de bytes lidos por read, não os arrays completos.
mizumoto
thingol,
Observando mais cuidadosamente o programa, reparei que o método read() não zera o array. Entretanto na última leitura do arquivo quando o mesmo não é multiplo de 1Mb (ou do tamanho do meu buffer) o método read() preenche apenas as posições retornadas, mantendo as demais posiçõesdo array com os mesmos valores obtidos na leitura anterior.
Sendo assim, o programa não estava própriamente errado, mas estava fazendo desnecessáriamente a leitura desses bytes adicionais, causando uma queda no desempenho da execução.
Todavia, a sua observação serviu para que eu pudesse otimizar ainda mais o meu programa, pois realmente não havia percebido essas leituras desnecessárias.
segue a nova versão do método:
publicstaticbooleanverificaDuplicidade(Stringfile1,Stringfile2){Filef1=newFile(file1);Filef2=newFile(file2);byte[]f1_buf=newbyte[1048576];byte[]f2_buf=newbyte[1048576];intlen;if(f1.length()==f2.length()){try{InputStreamisf1=newFileInputStream(f1);InputStreamisf2=newFileInputStream(f2);try{while(isf1.read(f1_buf)>=0){len=isf2.read(f2_buf);for(intj=0;j<len;j++){if(f1_buf[j]!=f2_buf[j]){returnfalse;// tamanho igual e conteudo diferente}}}}catch(IOExceptione){}}catch(FileNotFoundExceptione){}}else{returnfalse;// tamanho e conteudo diferente}returntrue;// arquivos iguais}
Mais uma vez, obrigado, cara!
Odyo
Estou com esta necessidade no escritório.
E estava escrevendo uma classe do zero para tratar arquivos.
O tópico foi de extrema ajuda.
A única forma de verificar se dois arquivos são idênticos é comparar todos os bytes ?