String.contains()的实现

前段时间有一个需求是, 有一个二进制文件, 在二进制文件中有一段是一张png图片. 现在已经有png文件二进制文件头和文件尾, 需要做的是在读取的byte[]数组中查找到这个文件头和文件尾的位置,并截出这段数组.

思前想后也想不到什么好的方法,因为文件可能会过大,byte数组是分批读入进来,没法直接全文去搜索, 况且数组过大的情况下这种搜索也太耗时间.

想到了String类底层也是个char数组, 有个方法是contains(), 可以接收另一个String类型的数据进行比较.
这就是两个char数组进行比较查找位置啊, 可能不是很符合我的需求,但是最近没什么事也就去看看他的源码吧.

// contains内部使用indexOf实现的
public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

// 只把主要的indexOf贴上来
public int indexOf(char[] source, int sourceOffset, int sourceCount,
                    char[] target, int targetOffset, int targetCount,
                    int fromIndex) {
    // 做一些判错处理
    if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
    }
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    // 如果被contains的字符串是空串,
    if (targetCount == 0) {
        return fromIndex;
    }

    // 获取target中开始匹配的位置, 一般target都是从第一个就开始匹配
    char first = target[targetOffset];
    // 这里很巧, 比如source长度是5, target长度是3 那么判断只要到index到2就够了,再往后匹配的话target长度就超过source了
    int max = sourceOffset + (sourceCount - targetCount);

    // 开始匹配, i的初始值是由source中开始位置和from开始的. 默认情况下就是从第一个开始
    for (int i = sourceOffset + fromIndex; i <= max; i++) {
        /* Look for first character. */
        // 获取source中第一个和target中第一个元素匹配的index
        if (source[i] != first) {
            // 如果不匹配index向后移, 直到找到第一个.
            while (++i <= max && source[i] != first);
            // 如果找不到, 因为是++i,所以i>max, 跳过下一句判断直接返回-1
        }

        /* Found first character, now look at the rest of v2 */
        if (i <= max) {
            // 因为i代表第一个元素匹配, 上面已经做过判断了. 所以直接从第二个元素开始比
            // source中索引
            int j = i + 1;
            // end为 target结束的位置, -1 是因为j其实是target中第二个元素
            int end = j + targetCount - 1;
            // 现在有两个指针 j,k 只要依次比较这两个指针所指的元素,相同就指针后移
            for (int k = targetOffset + 1; j < end && source[j]
                    == target[k]; j++, k++);

            // 如果找到了那么指针所指的位置应该和target结束的位置一致
            // 如果前一个循环提前跳出的话, 这里是不一致的, 那么应该进行下次循环查找后面的字符串
            if (j == end) {
                /* Found whole string. */
                return i - sourceOffset;
            }
        }
    }
    return -1;
}

看完之后比较失望, java中的String.contains()实现也是通过循环去比较的. 只是循环中一些写法比我想的好得多. 也不算全无收获,但至少没有解决我的疑问.


后来咨询了同事, 他是一次性把这个文件load到内存. 如果我分批load进来,需要解决的一个问题就是: 如果刚好文件头或文件尾从中间被截断了,需要对这种情况做处理.


   转载规则


《String.contains()的实现》 echi1995 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
JAVA内存模型 JAVA内存模型
JMM java内存模型多核CPU并发缓存架构CPU和主存之间会有高速缓存,这个缓存速度非常快,空间也非常小.在使用时,先把数据从主存存放到高速缓存,CPU使用时主要和高速缓存做交互. JAVA线程内存模型JAVA的线程内存模型跟CPU缓存
下一篇 
SpringBoot(一) SpringBoot(一)
SpringBoot(一)最开始使用spring时,配置bean使用的都是xml格式, 在xml中写标签,属性也要用ref去指定.后来接触到使用注解进行配置, 只要使用@Bean注解修饰这个方法,在扫包的时候扫到,该方法的返回值就会放到io
  目录