在文件比较大时,我们不能一次将整个文件读取到内存中,需要按行读并按行处理,但是这样带来的问题在于将读取文件的代码与处理数据的代码混在了一起:
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
String line;
while ((line = in.readLine()) != null) {
doProcessLine(line);
if (!doNext(line)) {//省略
break;
}
}
}
可以使用Guava将它分开,例如:
CharStreams.readLine(reader, new LineProcessor<T>() {
public boolean processLine(String line) throws IOException {
if (!doNext(line)) {
return false;//不需要再继续读
}
...
}
public T getResult() {
return xxx;
}
});
这样代码比较繁琐,不自然。
更自然的方式:
for (String line : IOStreams.lines(reader)) {
if (!doNext(line)) {
break;//停止读,因为是lazy的,当前行处理结束前,下一行不会被读入到内存
}
...
}
实现方式:
先定义两个基础接口CloseableIterable和CloseableIterator:
import java.io.Closeable;
/**
* 可被关闭的{@link Iterable}对象,可用于基于某种资源(如ResultSet)的迭代。
* <p>
* <pre>
* {@code
* try (CloseableIterable<E> iter = xxx) {
* for (E elem : iter) {
* ……
* }
* }
* }
* </pre>
*
* @author gaohang on 15/9/21.
*/
public interface CloseableIterable<E> extends Iterable<E>, Closeable {
/**
* 关闭资源
*/
void close();
}
import java.io.Closeable;
import java.util.Iterator;
/**
* @author gaohang on 15/9/21.
* @see CloseableIterable
*/
public interface CloseableIterator<E> extends Iterator<E>, Closeable {
void close();
}
最后实现IOStreams类:
import cn.yxffcode.freetookit.collection.ImmutableIterator;
import cn.yxffcode.freetookit.lang.CloseableIterable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* 输入输出流相关的工具
*
* @author gaohang on 15/10/23.
*/
public final class IOStreams {
private IOStreams() {
}
/**
* 按行读取
* <p>
* 使用lazy的读,只有在返回的Iterable对象上迭代一次才会读一行
* <p>
* 当读取的文件比较大时,不合适一次将事个文件的行读入内存,需要逐行读并处理。在使用guava的CharStreams时比较繁锁,例如:
* <pre>
* CharStreams.readLine(reader, new LineProcessor<T>() {
* public boolean processLine(String line) throws IOException {
* if (line ....) {
* return false;//不需要再继续读
* }
* ...
* }
* public T getResult() {
* return xxx;
* }
* });
* </pre>
* 换成此方法,代码如下:
* <pre>
* for (String line : IOStreams.lines(reader)) {
* if (line...) {
* break;//停止读,因为是lazy的,当前行处理结束前,下一行不会被读入到内存
* }
* ...
* }
* </pre>
*/
public static Iterable<String> lines(final BufferedReader reader) {
checkNotNull(reader);
return new Iterable<String>() {
@Override public Iterator<String> iterator() {
return new ImmutableIterator<String>() {
private String line;
@Override public boolean hasNext() {
try {
return (line = reader.readLine()) != null;
} catch (IOException e) {
throw new IOReaderException(e);
}
}
@Override public String next() {
return line;
}
};
}
};
}
public static CloseableIterable<String> lines(final File src) {
return new CloseableIterable<String>() {
private BufferedReader in;
@Override public void close() {
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new IOReaderException(e);
}
}
}
@Override public Iterator<String> iterator() {
try {
in = new BufferedReader(new FileReader(src));
return lines(in).iterator();
} catch (FileNotFoundException e) {
throw new IOReaderException(e);
}
}
};
}
public static BufferedReader toBufferedReader(InputStream in) {
return new BufferedReader(new InputStreamReader(in));
}
public static BufferedReader openClasspath(String classpath) {
return toBufferedReader(IOStreams.class.getResourceAsStream(classpath));
}
}