Implementação de lista encadeada de diretórios FileSystem
Estou escrevendo classes wrapper em Java que substituem métodos de uma implementação existente, a fim de lidar com um caso extremo. A implementação completa é um pouco mais complexa do que precisa ser postada aqui, então escrevi uma classe simplificada contendo apenas as partes para as quais estou solicitando assistência.
Resumo do problema
Estou estendendo duas classes:
Uma classe projetada como uma classe de "enumeração", abstraindo um diretório em um sistema de arquivos que contém links simbólicos para outros diretórios. (Mundo real: "/sys/block".). Possui dois métodos, um scan()
método para gerar a lista de subdiretórios (vinculados) e um getFirst()
para retornar o primeiro elemento da lista.
A segunda classe é uma classe de "entrada", abstraindo o diretório apontado enumerado pela primeira classe. Ele tem dois métodos, um getName()
método para retornar o caminho do diretório como uma string e um getNext()
método para iterar para o próximo elemento.
Restrições
- Compatibilidade com JDK 8 ou anterior
- O uso de thread único pode ser assumido
- Os construtores podem ser alterados conforme necessário.
- Deve implementar (pelo menos) as duas classes especificadas e os dois métodos em cada uma.
Foco da revisão
O scan()
método é a minha luta aqui. Acho que posso ter complicado demais a solução de duas maneiras:
try ... catch
Os blocos aninhados noscan()
método parecem incomuns. Estou perdendo uma maneira mais simples de lidar com isso?- (ATUALIZAÇÃO: auto-resposta a esta segunda pergunta, abaixo.) O padrão implementado é obviamente uma lista encadeada individualmente na qual estou trabalhando passando uma
ArrayList
implementação. Posso imaginar aDirEntry
classe contendo apenas seuPath
e umDirEntry next
objeto, mas as tentativas de gerar essa lista parecem ainda mais complexas ou com menos desempenho do que a solução alternativa que criei.
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();
}
}
}
```
Respostas
Eu descobri uma maneira mais simples de fazer a segunda pergunta, construindo a lista encadeada iterando de trás para frente a partir dos caminhos gerados.
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;
}
// ...
}
```