Implementacja powiązanej listy katalogów FileSystem
Piszę klasy opakowujące w Javie, które przesłaniają metody istniejącej implementacji, aby obsłużyć przypadek skrajny. Pełna implementacja jest nieco bardziej złożona, niż trzeba ją tutaj opublikować, dlatego napisałem uproszczoną klasę zawierającą tylko części, nad którymi proszę o pomoc.
Podsumowanie problemu
Rozszerzam dwie klasy:
Jedna klasa zaprojektowana jako klasa „wyliczeniowa”, abstrakcyjna katalog w systemie plików, który zawiera dowiązania symboliczne do innych katalogów. (Świat rzeczywisty: „/ sys / block”.). Ma dwie metody: scan()
metodę generowania listy (połączonych) podkatalogów i metodę getFirst()
zwracania pierwszego elementu listy.
Druga klasa jest klasą „wpisową”, abstrakcyjną dla wskazanego katalogu wyliczanego przez pierwszą klasę. Ma dwie metody, getName()
metodę zwracania ścieżki katalogu jako ciąg znaków i getNext()
metodę iteracji do następnego elementu.
Ograniczenia
- Zgodność z JDK 8 lub starszym
- Można założyć użycie jednowątkowe
- W razie potrzeby konstruktorzy mogą być modyfikowani.
- Musi zaimplementować (co najmniej) dwie określone klasy i dwie metody w każdej z nich.
Przedmiot przeglądu
scan()
Metoda jest moja walka tutaj. Myślę, że mogłem nadmiernie skomplikować rozwiązanie na dwa sposoby:
- Zagnieżdżone
try ... catch
bloki wscan()
metodzie wydają się nietypowe. Czy brakuje mi prostszego sposobu, aby sobie z tym poradzić? - (AKTUALIZACJA: Samodzielna odpowiedź na to drugie pytanie, poniżej). Zaimplementowany wzorzec jest oczywiście pojedynczo połączoną listą, nad którą pracuję, przekazując
ArrayList
implementację. Mogę sobie wyobrazićDirEntry
klasę zawierającą tylko jejPath
iDirEntry next
obiekt, ale próby wygenerowania takiej listy wydają się jeszcze bardziej złożone lub mniej wydajne niż obejście, które stworzyłem.
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();
}
}
}
```
Odpowiedzi
Wymyśliłem prostszy sposób wykonania drugiego pytania, budując listę połączoną, wykonując iterację wstecz od wygenerowanych ścieżek.
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;
}
// ...
}
```