Linked-List-Implementierung von FileSystem-Verzeichnissen

Aug 16 2020

Ich schreibe Wrapper-Klassen in Java, die Methoden einer vorhandenen Implementierung überschreiben, um einen Randfall zu behandeln. Die vollständige Implementierung ist etwas komplexer, als hier gepostet werden muss, daher habe ich eine vereinfachte Klasse geschrieben, die nur die Teile enthält, für die ich um Unterstützung bitte.

Zusammenfassung des Problems

Ich erweitere zwei Klassen:

Eine Klasse, die als "Aufzählungs"-Klasse konzipiert ist und ein Verzeichnis in einem Dateisystem abstrahiert, das symbolische Links zu anderen Verzeichnissen enthält. (Reale Welt: "/sys/block".). Es hat zwei Methoden, eine scan()Methode zum Generieren der Liste der (verknüpften) Unterverzeichnisse und eine getFirst()zum Zurückgeben des ersten Elements der Liste.

Die zweite Klasse ist eine "Einstiegs"-Klasse, die das von der ersten Klasse aufgezählte Verzeichnis, auf das gezeigt wird, abstrahiert. Es hat zwei Methoden, eine getName()Methode, um den Pfad des Verzeichnisses als Zeichenfolge zurückzugeben, und eine getNext()Methode, um zum nächsten Element zu iterieren.

Einschränkungen

  • Kompatibilität mit JDK 8 oder früher
  • Es kann von einer Single-Thread-Nutzung ausgegangen werden
  • Konstruktoren können nach Bedarf geändert werden.
  • Muss (mindestens) die beiden angegebenen Klassen und die beiden Methoden auf jeder implementieren.

Fokus der Überprüfung

Die scan()Methode ist mein Kampf hier. Ich glaube, ich habe die Lösung auf zwei Arten zu kompliziert gemacht:

  • Die verschachtelten try ... catchBlöcke in der scan()Methode scheinen ungewöhnlich. Übersehe ich eine einfachere Möglichkeit, damit umzugehen?
  • (UPDATE: Diese zweite Frage wurde unten selbst beantwortet.) Das implementierte Muster ist offensichtlich eine einfach verknüpfte Liste, die ich umgehe, indem ich eine ArrayListImplementierung weitergebe. Ich kann mir vorstellen, dass die DirEntryKlasse nur seine Pathund ein DirEntry nextObjekt enthält, aber Versuche, eine solche Liste zu generieren, scheinen noch komplexer oder weniger leistungsfähig zu sein als die von mir erstellte Problemumgehung.
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DeviceList {

    /**
     * Class representing a parent directory which contains symbolic links to other
     * directories
     */
    static class DirEnumerator {

        private Path dirPath;
        private List<DirEntry> entryList = Collections.emptyList();

        public DirEnumerator(String path) {
            dirPath = FileSystems.getDefault().getPath(path);
        }

        /**
         * Scans the directory for entries
         *
         * @return The number of entries found
         */
        public int scan() {
            try (Stream<Path> paths = Files.walk(dirPath)) {
                List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
                    try {
                        return Files.readSymbolicLink(p);
                    } catch (IOException e) {
                        return p;
                    }
                }).collect(Collectors.toList());
                this.entryList = new ArrayList<>();
                for (int i = 0; i < linkedDirs.size(); i++) {
                    this.entryList.add(new DirEntry(entryList, linkedDirs.get(i), i));
                }
                return this.entryList.size();
            } catch (IOException e) {
                this.entryList = Collections.emptyList();
                return 0;
            }
        }

        /**
         * Gets the first entry in the scanned list
         *
         * @return The first entry if it exists; null otherwise
         */
        public DirEntry getFirst() {
            return entryList.isEmpty() ? null : entryList.get(0);
        }

    }

    /**
     * Class representing a directory
     */
    static class DirEntry {
        private List<DirEntry> entryList;
        private Path path;
        private int index;

        public DirEntry(List<DirEntry> entryList, Path path, int i) {
            this.entryList = entryList;
            this.path = path;
            this.index = i;
        }

        /**
         * Gets the path name of the directory entry
         *
         * @return a string representing the path
         */
        public String getName() {
            return this.path.toString();
        }

        /**
         * Gets the next entry in the list
         *
         * @return the next entry if it exists; null otherwise
         */
        public DirEntry getNext() {
            int nextIndex = index + 1;
            return nextIndex < entryList.size() ? entryList.get(nextIndex) : null;
        }
    }

    public static void main(String[] args) {
        // Test on any directory containing symbolic links to other directories
        DirEnumerator de = new DirEnumerator("/sys/block");
        int n = de.scan();
        System.out.println("Found " + n + " directories.");

        DirEntry e = de.getFirst();
        while (e != null) {
            System.out.println("Directory: " + e.getName());
            e = e.getNext();
        }
    }
}
```

Antworten

DanielWiddis Aug 16 2020 at 00:27

Ich habe einen einfacheren Weg gefunden, um die zweite Frage zu lösen, indem ich die verknüpfte Liste aufbaue, indem ich von den generierten Pfaden rückwärts iteriere.

    static class DirEnumerator {

        private Path dirPath;
        private DirEntry first = null;

        // ...

        public int scan() {
            try (Stream<Path> paths = Files.walk(dirPath)) {
                List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
                    try {
                        return Files.readSymbolicLink(p);
                    } catch (IOException e) {
                        return p;
                    }
                }).collect(Collectors.toList());
                this.first = null;
                int i = linkedDirs.size();
                while (i-- > 0) {
                    this.first = new DirEntry(linkedDirs.get(i), first);
                }
                return linkedDirs.size();
            } catch (IOException e) {
                this.first = null;
                return 0;
            }
        }

        // ...
    }

    static class DirEntry {
        private Path path;
        private DirEntry next;

        public DirEntry(Path path, DirEntry next) {
            this.path = path;
            this.next = next;
        }

       // ...
    }
```