hadoop的FileSystem类中,遍历文件目录的三种方法(源码和区别)

hdfs的java api中,可以调用FileSystem类来管理文件
该类中有三个不同的方法(listStatusIterator, listLocatedStatus, listFiles),都是用于获取指定目录下的所有文件(文件夹)
那么这三个方法有什么区别呢?

  1. listStatusIterator方法和listLocatedStatus方法非常类似,都可以获取到所有的文件和文件夹. 但listStatusIterator方法返回的迭代器中的元素是FileStatus,后者返回的是LocatedFileStatus.
  2. LocatedFileStatu是FileStatus的子类,它额外增加了一个属性,就是BlockLocation[].
    BlockLocation用于记录块的网络位置,包括块副本所在的主机,与块关联的文件偏移量,长度,是否已损坏等。
  3. listFiles方法只能列出文件,不过它有两个参数.
    第一个参数指定目录位置,第二个参数是布尔类型,指定是否递归
    也就是说,此方法可以递归的列出目录下的所有文件,不包括文件夹

经验总结

  1. 可以通过FileSystem.get方法来返回一个FileSystem对象
    虽然看起来很类似于单例,但是FileSystem是一个抽象类,它不可能返回本类对象
    它返回的是自己的实现类
  2. 源码中,将数组转换为了迭代器.要对数组实现遍历,一定离不开角标.
    如果需要同时使用多个数组,特别是要在递归的时候,会先后使用不同的数组,记录每个数组的角标会很麻烦.迭代器对象就不需要考虑角标
  3. 使用多个迭代器,可以实现用has next+next控制到每一次递归

源码分析如下

	/* 1.我们用FileSystem.get方法来获取FileSystem类的一个对象,但其实FileSystem是一个抽象类.
	 * 也就是说,我们真正获取到的是它的子类,然后做了一个自动类型提升
	 * 2.源码中,多次调用了listStatus方法来获取到文件数组
	 * 如果直接去看FileSystem类中的该方法,会发现它是一个抽象方法
	 * 联系第1点,可以想到,这里是一个多态,实际调用的是子类方法
     */ 
	public abstract FileStatus[] listStatus(Path var1) throws FileNotFoundException, IOException;
	/* 3.我们可以直接调用listStatus方法来查看数组
	 * 也可以通过listStatusIterator来间接查看
	 */
	public RemoteIterator<FileStatus> listStatusIterator(final Path p) throws FileNotFoundException, IOException {
        /* 4.RemoteIterator是一个接口,只有两个方法:has Next()和next()
         * 这个方法是new了一个匿名内部类,并return了该类
         */
        return new RemoteIterator<FileStatus>() {
            // 核心就是listStatus方法返回的数组
            // 这里将数组做成了迭代器的形式
            private final FileStatus[] stats = FileSystem.this.listStatus(p);
            private int i = 0;

            public boolean hasNext() {
                return this.i < this.stats.length;
            }

            public FileStatus next() throws IOException {
                if (!this.hasNext()) {
                    throw new NoSuchElementException("No more entry in " + p);
                } else {
                    return this.stats[this.i++];
                }
            }
        };
    }
	/* 5.此方法返回的也是RemoteIterator, 它实际调用的是同名的protected方法
	 * 参数DEFAULT_FILTER是默认过滤器, 实际效果是不过滤任何文件
	 */
    public RemoteIterator<LocatedFileStatus> listLocatedStatus(Path f) throws FileNotFoundException, IOException {
        return this.listLocatedStatus(f, DEFAULT_FILTER);
    }
    protected RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f, final PathFilter filter) throws FileNotFoundException, IOException {
        // 和第4点一样,这个方法的方法体中只有一个return,return的是一个匿名内部类
        return new RemoteIterator<LocatedFileStatus>() {
            private final FileStatus[] stats = FileSystem.this.listStatus(f, filter);
            private int i = 0;

            public boolean hasNext() {
                return this.i < this.stats.length;
            }

            public LocatedFileStatus next() throws IOException {
                if (!this.hasNext()) {
                    throw new NoSuchElementException("No more entry in " + f);
                } else {
                    FileStatus result = this.stats[this.i++];
	/* 6.区别在这里
     * listStatus方法返回迭代器中的元素是FileStatus,但是此方法返回的是LocatedFileStatus.
     * LocatedFileStatu是FileStatus的子类,它额外增加了一个属性,就是BlockLocation[], BlockLocation用于记录块的网络位置,包括块副本所在的主机,与块关联的文件偏移量,长度,是否已损坏等。
     * 此处的三目运算符用于生成BlockLocation数组,文件目录的BlockLocation[]长度为0,因为目录没有对应的块信息,目录属于元数据
                     */
                    BlockLocation[] locs = result.isFile() ? FileSystem.this.getFileBlockLocations(result.getPath(), 0L, result.getLen()) : null;
                    return new LocatedFileStatus(result, locs);
                }
            }
        };
    }

	// 回忆下四种内部类: 成员内部类, 匿名内部类, 局部内部类, 静态内部类
    public RemoteIterator<LocatedFileStatus> listFiles(final Path f, final boolean recursive) throws FileNotFoundException, IOException {
        // 一样的思路
        return new RemoteIterator<LocatedFileStatus>() {
            // Stack是模拟堆栈的一个集合
            private Stack<RemoteIterator<LocatedFileStatus>> itors = new Stack();
            // 调用了上面提到的listLocatedStatus方法
            private RemoteIterator<LocatedFileStatus> curItor = FileSystem.this.listLocatedStatus(f);
            // 对应的文件类型是LocatedFileStatus
            private LocatedFileStatus curFile;

            public boolean hasNext() throws IOException {
                while(true) {
                    // curFile被赋值后才进行下一步.需要注意的是这里的this指的是本匿名内部类
                    if (this.curFile == null) {
                        // 调用的是listLocatedStatus方法返回的迭代器
                        if (this.curItor.hasNext()) {
                            this.handleFileStat((LocatedFileStatus)this.curItor.next());
                            continue;
                        }

                        // 只有当前使用的迭代器为空的时候,才能进行到这一步
                        // 如果集合中还有迭代器(不为空)
                        if (!this.itors.empty()) {
                            // 从集合中取出一个新的迭代器,继续用
                            this.curItor = (RemoteIterator)this.itors.pop();
                            continue;
                        }

                        return false;
                    }

                    return true;
                }
            }

            private void handleFileStat(LocatedFileStatus stat) throws IOException {
                // 如果是文件,就给curFile赋值
                if (stat.isFile()) {
                    this.curFile = stat;
                    // recursive是我们传入的参数.如果参数为真,就递归
                } else if (recursive) {
                    // 将当前正在进行的迭代先放到集合中
                    this.itors.push(this.curItor);
                    // 将当前迭代器更改为子目录的迭代器,开始递归
                    this.curItor = FileSystem.this.listLocatedStatus(stat.getPath());
                }

            }

            public LocatedFileStatus next() throws IOException {
                if (this.hasNext()) {
                    // 调用一次就重置curFile
                    LocatedFileStatus result = this.curFile;
                    this.curFile = null;
                    return result;
                } else {
                    throw new NoSuchElementException("No more entry in " + f);
                }
            }
        };
    }

猜你喜欢

转载自blog.csdn.net/IAmListening/article/details/89708639