Java.io之CharArrayReader

CharArrayReader是字符数组输入流,与ByteArrayInputStream类似,只不过前者是字符后者是字节。CharArrayReader源码过于简单以至于让人怀疑其存在的必要,本文对此做一个解释。

CharArrayReader看起来多此一举,既然CharArray是数据源的形式,为何不直接操作CharArray而要使用CharArrayReader呢?

1
2
3
4
原因在于其一般不直接用,但可以配合其他流来用,主要在某些应用场景下用它更方便,
如果有人给你发一段字符流或本身就有一个字符数组,你想读取其中的一行数据,就可以用它。
reader相比较数组,它的优点是可以节约内存,因为它本身相当于一个指针,而不用存储全部数据。
但是作为代价,它牺牲了灵活性,通常来说,它是线性的,从前往后,它要随机访问就不如数组方便。

字段

1
2
3
4
5
6
7
8
//字符数组数据源
protected char buf[];
//当前读取位置
protected int pos;
//标记位置
protected int markedPos = 0;
//字符数组数据源有效字符数
protected int count;

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//将整个buf字符数组作为数据源
public CharArrayReader(char buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}

//将buf数组中从offset开始的length个字符作为数据源
public CharArrayReader(char buf[], int offset, int length) {
if ((offset < 0) || (offset > buf.length) || (length < 0) ||
((offset + length) < 0)) {
throw new IllegalArgumentException();
}
this.buf = buf;
this.pos = offset;
//如果offset+length > buf.length 那就只要offset到buf末尾的数据
this.count = Math.min(offset + length, buf.length);
this.markedPos = offset;
}

读取方法

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
//读取单个字符返回其int值
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (pos >= count)
return -1;
else
return buf[pos++];
}
}

//读取len个字符将其拷贝到buf[]中off开始的空间中
public int read(char b[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}

if (pos >= count) {
return -1;
}

int avail = count - pos;
if (len > avail) {
len = avail;
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
}

跳过方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//因为数据源就是字符数组 所以不存在阻塞与否的问题
//skip只需判断跳过字符数n是否超过数组长度即可
public long skip(long n) throws IOException {
synchronized (lock) {
ensureOpen();

long avail = count - pos;
if (n > avail) {
n = avail;
}
if (n < 0) {
return 0;
}
pos += n;
return n;
}
}

可获得方法

1
2
3
4
5
6
7
//与skip方法的解释类似 字符数组数据源不存在阻塞 所以只要没读取到末尾 可获取(ready)
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return (count - pos) > 0;
}
}

标记与重置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//两个方法需要配合使用
//mark记录下当前pos位置 reset将读取游标重置为mark记录到位置
public void mark(int readAheadLimit) throws IOException {
synchronized (lock) {
ensureOpen();
markedPos = pos;
}
}

public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
pos = markedPos;
}
}

讨论

not copied

1
2
3
4
/**
* Creates a CharArrayReader from the specified array of chars.
* @param buf Input buffer (not copied)
*/

在CharArrayReader源码中,其两种构造函数的注释都有not copied字样,说明CharArrayReader相当于游标或者指针,操作的是其形参char[] buf内存空间,而不是将其复制到CharArrayReader的数据源中,即CharArrayReader的数据源就是形参字符数组本身,区别于其他流中调用System.arraycopy()进行拷贝,所以CharArrayReader更节约内存。

Donate here.