6
votes

Répertoires traversants sans utiliser de récursivité?

le problème Je dois écrire un logiciel simple qui, donnant certaines contraintes, ajoute à une liste de fichiers. L'utilisateur pourrait choisir entre deux "types" du répertoire: une avec un * wildcard signifiant qu'il devrait également explorer des sous-répertoires et le classique sans caractères génériques qui viennent d'obtenir des fichiers présents dans ce répertoire.

ce que je fais

Je fais maintenant la chose la plus stupide: xxx

i N'ayez pas besoin de construire un arbre parce que j'ai juste besoin de la liste des fichiers, alors je pensais peut-être qu'il y a un moyen plus efficace de le faire.

Je lisais sur le TREMEDEL Mais, comme je le comprends, il s'agit simplement d'une interface pour implémenter un JTREE. >


7 commentaires

Y a-t-il une raison pour laquelle vous choisissez de ne pas utiliser de récursivité? Dans certains cas, il plus vite que de le faire sans.


Parce que je ne sais pas combien de répertoires seront dans un répertoire sauvage, alors je pensais à des problèmes de mémoire.


Avez-vous vérifié Stackoverflow.com/Questtions/1086907/...


Eh bien, mon logiciel devrait travailler sous Windows et Linux, en utilisant la recherche d'une interopérabilité des pauses, non?


Je pense qu'il n'y a pas de meilleure solution que de récursives ici - si vous vous attendez vraiment à courir dans des problèmes de mémoire, ce qui semble être vraiment improbable donner au fait que vous travaillez sur des répertoires ici, vous pouvez limiter la profondeur de la récursive. Mais d'abord, je vérifierais la profondeur maximale de votre répertoire de votre système de fichiers - les chances sont bonnes que cette valeur rend déjà vos soucis non fondés.


Qu'en est-il d'utiliser quelque chose comme la profondeur ou la largeur d'abord? Est-ce même une solution pour ce que je fais? Je ne cherche pas un seul objectif, mais plusieurs objectifs. Je dois explorer toujours l'arbre entier.


Votre solution récursive affichée ci-dessus est déjà un DFS - Donc, oui, DFS ou BFS fonctionnerait bien ici.


8 Réponses :


0
votes

Si vous choisissez d'utiliser la récursion, j'ai trouvé un exemple qui peut être proche de celui que vous utilisez actuellement pour éliminer toute ambiguïté.

// Process only directories under dir
public static void visitAllDirs(File dir) {
    if (dir.isDirectory()) {
        process(dir);

        String[] children = dir.list();
        for (int i=0; i<children.length; i++) {
            visitAllDirs(new File(dir, children[i]));
        }
    }
}


0 commentaires

1
votes

La récursion peut toujours être transformée en boucle.
Une solution rapide et sale possible (non testée) suit: xxx

Veuillez noter que le nœud source doit être un répertoire pour que tout soit affiché.
En outre, il s'agit d'un premier affichage de largeur. Si vous souhaitez d'abord une profondeur, vous devez modifier «l'APPEND» pour placer le fichier juste après le nœud actuel de la liste des matrices.

Je ne suis pas sûr de la consomation de la mémoire, cependant.
Cordialement
Guillaume


0 commentaires

9
votes

En ce moment, je fais la chose la plus stupide ...

Récursion n'est ni "stupide" ni nécessairement inefficace. En effet, dans ce cas particulier, une solution récursive est susceptible d'être plus efficace qu'une solution non récursive. Et bien sûr, la solution récursive est plus facile à coder et à comprendre que les alternatives.

Le seul problème potentiel avec la récursion est que vous pouvez déborder la pile si l'arborescence répertoire est pathologiquement profonde.

Si vous voulez vraiment éviter la récursion, le moyen naturel de le faire est d'utiliser une structure de données "pile de liste de fichier". Chaque lieu où vous auriez récupéré, vous appuyez sur la liste contenant les objets de fichier du répertoire actuel sur la pile, lisez le nouveau répertoire et commencez à travailler sur eux. Ensuite, lorsque vous avez terminé, affichez la pile et continuez avec le répertoire parent. Cela vous donnera une travertielle de profondeur. Si vous souhaitez une première traverse d'une largeur, utilisez une structure de données "file d'attente de fichier" au lieu d'une pile.


5 commentaires

Stupide signifiant la première chose à laquelle je pensais faire. Sans analyse plus profonde.


Pas tout à fait correct. Tandis que pour un petit arbre de répertoire, la récursivité est négligeable plus efficace que l'itération (dans l'ordre de plusieurs millisecondes), pour un très grand arbre, la récursion est des minutes (et donc sensiblement) plus lentement que l'itération. Il n'est pas tout à fait difficile de saisir pourquoi non plus. Si vous devez allouer et renvoyer un nouvel ensemble de variables de pile, chaque fois, appelez une fonction et stockez tous les résultats précédents jusqu'à votre retour, vous êtes bien sûr plus lent que lorsque vous initiez une structure de pile AA sur le tas une fois, et Utilisez cela pour chaque itération.


L'arbre n'a pas besoin d'être en profondeur pathologiquement pour que cet effet soit remarqué (tandis que la vitesse lente n'est pas un débordement de pile, ses conséquences très négatives ne sont pas très différentes d'un bug Stackoverflow). De plus, je n'appellerais pas avoir beaucoup de fichiers "pathologiques", car si vous faites un index sur votre lecteur principal, vous aurez naturellement beaucoup de fichiers. Avoir une documentation HTML et le nombre de fichiers explosent. Vous constaterez que sur un grand nombre de fichiers, une itération se termine en moins de 30 secondes, tandis qu'une récursion a besoin d'appx. 3 minutes.


@Stefansteiger - J'aimerais voir le code que vous avez couru pour obtenir ces résultats. Pourquoi? Parce que ce que vous rapportez est (pour moi) totalement contre-intuitif. Et si l'effet est réel, je doute que c'était pour les raisons que vous indiquez.


@Stefansteiger - ma remarque "pathologique" concerne la profondeur de l'arborescence de répertoires, pas le nombre de fichiers. Je digère toujours votre référence. (Je n'ai pas lu c # couramment)



1
votes

Je suis un vrai novice, mais après avoir travaillé pendant une semaine sur ce problème ... J'ai une solution propre ... merci pour toute l'aide de Patry and Ebal. XXX


0 commentaires

0
votes

Partie, merci de conseils. J'ai transformé un peu votre code, c'est ce que j'ai xxx


1 commentaires

Le Itérateur IT n'est pas utilisé.



4
votes

Ma solution itérative:

  ArrayDeque<File> stack = new ArrayDeque<File>();

    stack.push(new File("<path>"));

    int n = 0;

    while(!stack.isEmpty()){

        n++;
        File file = stack.pop();

        System.err.println(file);

        File[] files = file.listFiles();

        for(File f: files){

            if(f.isHidden()) continue;

            if(f.isDirectory()){
                stack.push(f);
                continue;
            }

            n++;
            System.out.println(f);


        }

    }

    System.out.println(n);


2 commentaires

Impossible d'utiliser la pile système? Roulez le vôtre! +1: D


Lucas Batistins: Vous inversez l'ordre de traitement par rapport à la récursive. Vous devez faire cela à la place: pour (fichier f: fichiers.Reverse ()) pour avoir le même ordre. Ou respectivement: pour (laissez i = fichiers.length-1; i> -1; --i) pile.push (fichiers [i]);



2
votes

@Stephen c: Ci-dessous selon votre demande, mon code de référence dont j'ai parlé dans les commentaires (C # - pas Java). de
Notez qu'il devrait utiliser chronomètre au lieu de DateTime pour une meilleure précision, mais sinon c'est bien.
Je n'ai pas testé si l'itération fournit les mêmes fichiers numériques que la récursion, mais cela devrait.

en fait, si vous faites attention à la médiane, vous remarquerez que cela commence déjà à montrer avec seulement très peu de Fichiers. STRT> (mon dossier de bureau contient 2210 fichiers, 415 dossiers, 3,2 Go Total, la plupart des fichiers volumineux dans le dossier de téléchargement, appdata et un plus grand nombre de fichiers en raison d'un projet C # plus grand [Server Mail-Server] sur mon bureau). P>

Pour obtenir les chiffres que j'ai parlé dans le commentaire, installez Cygwin (avec tout [c'est environ 100 Go, je pense]) et indexez le dossier Cygwin. P>

comparaison de vitesse p>

Comme mentionné dans les commentaires, il n'est pas tout à fait correct de le dire Peu importe.

Tandis que pour un petit arbre de répertoire, la récursivité est négligeable plus efficace que l'itération (dans l'ordre de plusieurs millisecondes), pour un très grand arbre, la récursion est des minutes (et donc sensiblement) plus lentement que l'itération. Il n'est pas tout à fait difficile de saisir pourquoi non plus. Si vous devez allouer et renvoyer un nouvel ensemble de variables de pile, chaque fois, appelez une fonction et stockez tous les résultats précédents jusqu'à votre retour, vous êtes bien sûr plus lent que lorsque vous initiez une structure de pile AA sur le tas une fois, et utilisez cela pour chaque itération. P>

L'arborescence n'a pas besoin d'être pathologiquement profondément pour que cet effet soit remarqué (alors que la vitesse lente n'est pas un débordement de pile, ses conséquences très négatives ne sont pas très différentes d'un Stackoverflow-bug). De plus, je n'appellerais pas avoir beaucoup de fichiers "pathologiques", car si vous faites un index sur votre lecteur principal, vous aurez naturellement beaucoup de fichiers. Avoir une documentation HTML et le nombre de fichiers explosent. Vous constaterez que sur un grand nombre de fichiers, une itération se termine en moins de 30 secondes, tandis qu'une récursion a besoin d'appx. 3 minutes. P> xxx pré>

Si vous devez préserver l'ordre de traversé, une version simplifiée va comme ceci: P>

public static void PathTraverse(string initialDirectory)
{
    System.Collections.Generic.Stack<string> stack =
        new System.Collections.Generic.Stack<string>();
    stack.Push(initialDirectory);

    while (stack.Count != 0)
    {
        string element = stack.Pop();
        System.Console.WriteLine(element);

        System.IO.FileAttributes attr = System.IO.File.GetAttributes(element);
        if ((attr & System.IO.FileAttributes.Directory) == System.IO.FileAttributes.Directory)
        {
            string[] children = System.IO.Directory.GetFileSystemEntries(element, "*.*", System.IO.SearchOption.TopDirectoryOnly);

            for (int i = children.Length - 1; i > -1; --i)
            {
                stack.Push(children[i]);
            }
        }
    }

} // End Function PathTraverse 


0 commentaires

0
votes

Basé sur la solution Patry Guillaume

public static List<File> getFolderTree(File node)
  {
    List<File> directories = new ArrayList();
    if (node.isDirectory())
    {
      directories.add(node);
    }

    for(int i=0; i<directories.size(); i++)
    {
      File dir = directories.get(i);
      String[] subNote = dir.list();
      for (String filename : subNote)
      {
        File subNode = new File(dir, filename);

        if (subNode.isDirectory())
        {
          directories.add(subNode);
        }
      }
    }
    return directories;
  }


0 commentaires