Existe alguma vantagem em se usar algoritmo recursivo em Java? O que a recursividade afeta no desempenho da aplicação?
Obrigado.
Existe alguma vantagem em se usar algoritmo recursivo em Java? O que a recursividade afeta no desempenho da aplicação?
Obrigado.
Java não é uma linguagem muito 'otimizada" para algoritmos recursivos se você precisar dos seguintes recursos que linguagens orientadas para algoritmos recursivos normalmente têm:
a) “Tail-call optimization” - em determinadas condições, um algoritmo recursivo é automaticamente transformado em um iterativo pelo runtime da linguagem, e isso economiza espaço em pilha.
b) Pilha de execução alocada dinamicamente (no Java essa pilha tem tamanho fixo, dada pelo parâmetro da JVM “-Xss” ).
Basicamente, chamadas de métodos adicionam um overhead no processamento, em função de empilhar variáveis locais e registradores. Mas na maioria das vezes, o inimigo em algoritmos recursivos não é esse overhead, e sim o próprio algoritmo, que pode gerar recalculos e aumentar a complexidade desnecessariamente. O exemplo clássico é a série de fibonnaci, que na sua versão recursiva é muito mais cara do que a versão iterativa. Por outro lado,em algoritmos como quicksort, busca em árvore binária, etc. a diferença deve ser muito pouca.
Uma das vantagens é que alguns algorítmos (especialmente os de árvore) ficam muito mais claros de maneira recursiva.
Diga como é que você lista os arquivos em diretórios e subdiretórios, ou os registros de uma JTree, sem usar um algoritmo recursivo.
Isso é possível usando pilhas, mas o algoritmo torna-se difícil de entender se não for em forma recursiva.
Um exemplo de como é possível listar recursivamente os arquivos em diretórios e subdiretórios (veja o método “listarArquivos” abaixo). Ele é muito simples de entender e é recursivo.
Como normalmente não há 10000 níveis de diretórios em um sistema de arquivos, mas sim muito menos, o método recursivo não irá explodir a pilha de execução do Java.
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.security.*;
import java.nio.*;
import java.nio.channels.*;
import java.math.*;
class AcharArquivosIguais {
public AcharArquivosIguais() {
}
public void listarArquivos (File diretorio, Set<File> arquivos) {
File[] listagem = diretorio.listFiles();
for (File f : listagem) {
if (f.isDirectory() && !f.getName().equals (".") && !f.getName().equals ("..")) {
listarArquivos (f, arquivos);
} else {
arquivos.add (f);
}
}
}
private static byte[] buffer = new byte[10 * 1024 * 1024];
private static String hashFile (File arq) {
String s = "error";
try {
/*
MessageDigest dgst = MessageDigest.getInstance ("SHA1");
FileInputStream fis = new FileInputStream (arq);
int nBytes;
dgst.reset();
while ((nBytes = fis.read (buffer)) > 0) {
dgst.update (buffer, 0, nBytes);
}
byte[] bytes = dgst.digest();
fis.close();
*/
MessageDigest dgst = MessageDigest.getInstance ("SHA1");
DigestInputStream dis = new DigestInputStream (new FileInputStream (arq), dgst);
int nBytes;
dgst.reset();
while ((nBytes = dis.read (buffer)) > 0) {
}
dis.close();
byte[] bytes = dgst.digest();
BigInteger bd = new BigInteger (bytes);
s = bd.toString (16);
} catch (NoSuchAlgorithmException ex) {
} catch (IOException ex) {
ex.printStackTrace();
}
return s;
}
public void acharArquivosIguais (Collection<File> arquivos) {
Map<String, List<File>> hash2file = new ConcurrentHashMap<String, List<File>>();
int n = 1;
for (File f : arquivos) {
System.err.printf ("\r%7d %,8dK %-61.61s", n, f.length() / 1024, f.getName());
String hash = hashFile (f);
List<File> files;
if (!hash2file.containsKey (hash)) {
files = new ArrayList<File>();
hash2file.put (hash, files);
} else {
files = hash2file.get (hash);
}
files.add (f);
++n;
}
System.out.println ();
// Agora vamos ver, nessa lista, que arquivos têm mais de uma entrada.
boolean arquivosRepetidos = 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 (File f : h2f.getValue()) {
System.out.println (" " + f);
}
}
}
if (!arquivosRepetidos) {
System.out.println ("Não foram encontrados arquivos repetidos.");
}
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println ("Sintaxe: java -cp . AcharArquivosIguais diretorio");
System.exit (1);
}
AcharArquivosIguais aai = new AcharArquivosIguais();
Set<File> arquivos = new TreeSet<File>(new Comparator<File>() {
public int compare(File o1, File o2) {
String n1 = o1.getName();
String n2 = o2.getName();
int cmp = n1.compareToIgnoreCase (n2);
if (cmp == 0) return o1.compareTo (o2);
else return cmp;
}
});
System.err.println ("Procurando os arquivos...");
aai.listarArquivos (new File (args[0]), arquivos);
System.err.printf ("Lendo os %d arquivos...%n", arquivos.size());
aai.acharArquivosIguais (arquivos);
}
}