我是靠谱客的博主 平常鞋子,这篇文章主要介绍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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* 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); } } }; }

最后

以上就是平常鞋子最近收集整理的关于hadoop的FileSystem类中,遍历文件目录的三种方法(源码和区别)的全部内容,更多相关hadoop内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部