首页 > Java, 挨踢(IT) > 简易Java(07):substring()方法在JDK6和JDK7中的异同

简易Java(07):substring()方法在JDK6和JDK7中的异同

2014年5月21日 发表评论 阅读评论 1,023 人阅读    

看到substring(),D瓜哥就想起来去年面试的惨痛精力。连续两次被问到substring()会造成什么问题;第一次被问到时,确实不知道会造成什么问题,面试结束后就查了查资料。但是,没有认真看。没想到接下来的面试又问到,由于没有看,还是没回答上来,结果面试就惨遭失败!

没想到,这次又遇到了substring()。所以,这篇文章D瓜哥必须好好翻译!

substring()会造成什么问题,请看参考资料

另外,需要提前说明一点,本节内容是针对Oracle JDK来说明的,其他JDK的实现也许可能不同。请读者自己查看相关文档以及源代码。

substring(int beginIndex, int endIndex)方法,在JDK6和JDK7中的实现是不一样的。了解实现的不同之处,可以帮助我们更好地使用这个方法。为了简单起见,下文中用substring()指代substring(int beginIndex, int endIndex)

1、substring()的作用是啥?

substring(int beginIndex, int endIndex)方法将返还字符串,这个字符串是原字符串从beginIndex开始,到endIndex-1的子串。

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */

String x = "abcd";
x = x.substring(1, 3);
System.out.println(x);

输出为:

bc

2、当substring()被调用时,背后发生了什么?

我们在上一篇文章“图解Java字符串的不可变性 ”讲到,字符串x是不可变的。所以,当x被重新赋值为x.substring(1, 3)时,它指向了一个全新的字符串。如下图所示:

执行全新字符串

尽管,这个图并不能完全正确地说明堆栈中的确实发生的变化。不过,却可以帮助我们说明,当substring()被调用时,在JDK6和JDK7下究竟有什么不同。

3、JDK6中的substring()

字符串是通过字符数组的方式来实现的。在JDK6中,String类包含三个属性:char value[]int offsetint count,它们分别用于存储真实的字符数组、数组的开始下标以及字符串中的字符数量。

当调用substring()时,它将创建一个新的字符串对象,但是字符串的值还是指向堆栈中同一个数组。两个字符串对象不同的只是它们的字符数量以及开始下标。如下图所示:

JDK6中的substring方法

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */

// JDK 6
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    // 边界检查
    return new String(offset + beginIndex, endIndex - beginIndex, value);
}

4、JDK6的substring()导致的问题

如果你有一个非常长的字符串,但是你每次调用substring(),只需要其中的很小一部分。那么,这将导致一个性能问题,尽管你仅仅需要很小的一部分,但是,却保存了所有的字符。对于JDK6,使用下面的代码,可以解决这个问题,可以使得对象x确实指向一个真正的子字符串:

x = x.substring(m, n) + "";

5、JDK7中的substring()

上面提到的问题,在JDK7中已经改正。在JDK7中,substring()方法确实会在堆栈中创建一个新的数组。如下图所示:

JDK7中的substring方法

D瓜哥注:

这个图片有部分地方是错误的:int countint offset已经不是Oracle JDK7中String的属性。大家可以从String的源代码中确认!

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */

// JDK 7
String(char value[], int offset, int count) {
    // 边界检查
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

public String substring(int beginIndex, int endIndex) {
    // 边界检查
    int subLen = endIndex - beginIndex;
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
}

《Simple Java》是一本讲解Java面试题的书。讲解也有不少独特之处,为了面试,《简易Java》走起!

参考资料



作 者: D瓜哥,https://www.diguage.com/
原文链接:https://wordpress.diguage.com/archives/93.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分类: Java, 挨踢(IT) 标签: , , ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.