การใช้งานรายการที่เชื่อมโยงของไดเร็กทอรี FileSystem

Aug 15 2020

ฉันกำลังเขียนคลาส wrapper ใน Java ซึ่งจะแทนที่เมธอดของการนำไปใช้งานที่มีอยู่เพื่อจัดการกับ edge case การใช้งานเต็มรูปแบบซับซ้อนกว่าที่จะต้องโพสต์ไว้ที่นี่เล็กน้อยดังนั้นฉันจึงเขียนคลาสแบบง่ายที่มีเฉพาะส่วนที่ฉันขอความช่วยเหลือเท่านั้น

สรุปปัญหา

ฉันกำลังขยายสองชั้น:

คลาสหนึ่งได้รับการออกแบบเป็นคลาส "การแจงนับ" โดยแยกไดเร็กทอรีบนระบบไฟล์ที่มีลิงก์สัญลักษณ์ไปยังไดเร็กทอรีอื่น (โลกแห่งความจริง: "/ sys / block".). มีสองวิธีscan()วิธีการสร้างรายการของไดเร็กทอรีย่อย (เชื่อมโยง) และgetFirst()ส่งกลับองค์ประกอบแรกของรายการ

คลาสที่สองคือคลาส "รายการ" ซึ่งเป็นนามธรรมของไดเร็กทอรีชี้ไปที่แจกแจงโดยคลาสแรก มีสองวิธีgetName()วิธีการคืนเส้นทางของไดเรกทอรีเป็นสตริงและgetNext()วิธีการวนซ้ำไปยังองค์ประกอบถัดไป

ข้อ จำกัด

  • เข้ากันได้กับ JDK 8 หรือรุ่นก่อนหน้า
  • อาจถือว่าการใช้งานแบบเธรดเดียว
  • ตัวสร้างอาจเปลี่ยนแปลงได้ตามความต้องการ
  • ต้องใช้ (อย่างน้อย) สองคลาสที่ระบุและสองวิธีในแต่ละคลาส

จุดเน้นของการทบทวน

scan()วิธีคือการต่อสู้ของฉันที่นี่ ฉันคิดว่าฉันอาจมีวิธีแก้ปัญหาที่ซับซ้อนเกินไปในสองวิธี:

  • try ... catchบล็อกที่ซ้อนกันในscan()วิธีนี้ดูเหมือนผิดปกติ ฉันพลาดวิธีจัดการที่ง่ายกว่านี้หรือเปล่า
  • (อัปเดต: ตอบคำถามที่สองนี้ด้วยตัวเองด้านล่าง) รูปแบบการนำไปใช้นั้นเห็นได้ชัดว่าเป็นรายการที่เชื่อมโยงกันเพียงอย่างเดียวซึ่งฉันกำลังดำเนินการโดยส่งต่อArrayListการนำไปใช้งาน ฉันนึกภาพออกว่าDirEntryคลาสที่มีเฉพาะของมันPathและDirEntry nextอ็อบเจกต์ แต่ความพยายามในการสร้างรายการดังกล่าวดูเหมือนซับซ้อนหรือมีประสิทธิภาพน้อยกว่าวิธีแก้ปัญหาที่ฉันสร้างขึ้น
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();
        }
    }
}
```

คำตอบ

DanielWiddis Aug 16 2020 at 00:27

ฉันได้หาวิธีที่ง่ายกว่าในการทำคำถามที่สองแล้วคือการสร้างรายการที่เชื่อมโยงโดยการทำซ้ำย้อนกลับจากเส้นทางที่สร้างขึ้น

    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;
        }

       // ...
    }
```