翻译人员: 铁锚

翻译日期: 2013年11月2日

原文链接: The substring() Method in JDK 6 and JDK 7

 

在JDK6与JDK7这两个版本中,substring(int beginIndex, int endIndex)方法是不同的. 了解两个版本间的区别可以让你更好地使用它们. 为简单起见,本文中以 substring() 表示 substring(int beginIndex, int endIndex).



1. substring()功能简介

String对象的substring(int beginIndex, int endIndex)方法返回此对象的一个子串,从beginIndex 开始,一直到 endIndex-1 结束,共 (endIndex - beginIndex)个字符。

新手提示: 

    1.1 String 的索引和数组一样,都是从0开始.

    1.2 注意,方法名字是substring(),全小写.

    1.3 有个重载方法是substring(int beginIndex),从beginIndex索引处开始,取得子字符串.

String x = "abcdef";
int begin=1;
int end=3;
x = x.substring(begin, end);
System.out.println(x);

执行结果(包含索引为 begin,直到 end-1 的字符):

bc

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

你应该知道,因为 x 是不可变的,当 指定 x 等于 x.substring(begin, end)时,实际上 x 指向了一个全新的字符串,如下图所示:

图1

然而,这幅图并不是完全正确的,堆内存中所真正发生的事也不是这么简单.那么,在JDK6和JDK7之间 substring()的调用到底有些什么区别呢?



3. JDK 6中的substring()方法

String实际上是一个字符数组.在 JDK6中, String对象主要包含3个属性域:

private final char value[];
private final int offset;
private final int count;

他们用于存储实际的字符数组,数组的第一个索引,以及String的字符个数.

当调用 substring() 方法时,创建了一个新的String对象,但是string的value[] 属性域仍然指向堆内存中的原来的那个数组。区别就是 两个对象的 count 和 offset 这两个值不同了。 如下图所示:

图2

要解释这个问题,下面是最关键部分的代码:

// JDK6,包级私有构造,共享 value数组提升速度
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    // ... 检查边界的代码
    // 如果范围和自己一模一样,则返回自身,否则用value字符数组构造一个新的对象
    return ((beginIndex == 0) && (endIndex == count)) ? this :
        new String(offset + beginIndex, endIndex - beginIndex, value);
}

4. JDK 6中substring()引起的问题

如果有一个"非常"长的字符串,但每次使用substring()时只想要很小的一部分,那么将会引起另一个性能问题: 虽然你只需要很小的一部分,但是持有了整个value[]的引用,从而导致大量内存被占用。

要解决这个问题,在JDK6中可以让其指向一个真正的子字符串,示例代码:

x = x.substring(begin, end) + "";

5. JDK 7中的substring()方法

在JDK 7 中这个问题得到改进, substring()方法真实地在堆内存中创建了另一个字符数组.

图3
// JDK 7, 权限变为 public
public String(char value[], int offset, int count) {
    // ... 检查边界..
    // value 数组拷贝
    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);
}

参考:

1. Changes to substring

2. Java 6 vs Java 7 when implementation matters



相关阅读:

1. Top 10 questions about Java String.

2. Java method for spliting a camelcase string

3. Java: Convert File to Char Array

4. Count Number of Statements in a Java Method By Using Eclipse JDT ASTParser

JDK6和JDK7中String的substring()方法及其差异的更多相关文章

  1. JDK6与JDK7中String类subString()方法的区别

    1.subString()方法的作用 subString(int beginIndex, int endIndex)方法的返回的是以beginIndex开始到 endIndex-1结束的某个调用字符串 ...

  2. 菜鸟译文(三)——JDK6和JDK7中substring()方法的对比

    substring(int beginIndex, int endIndex)方法在JDK6和JDK7中是不同的.了解他们的区别可以让我们更好的使用这个方法.方便起见,以下用substring() 代 ...

  3. [转]JDK6和JDK7中的substring()方法

    substring(int beginIndex, int endIndex)在JDK6与JDK7中的实现方式不一样,理解他们的差异有助于更好的使用它们.为了简单起见,下面所说的substring() ...

  4. JDK6和JDK7中的substring()方法

    substring(int beginIndex, int endIndex)在JDK6与JDK7中的实现方式不一样,理解他们的差异有助于更好的使用它们.为了简单起见,下面所说的substring() ...

  5. JavaScript中String对象的方法介绍

    1.字符方法 1.1 charAt() 方法,返回字符串中指定位置的字符. var question = "Do you like JavaScript?"; alert(ques ...

  6. Java中String类的方法及说明

    String : 字符串类型 一.      String sc_sub = new String(c,3,2);    //      String sb_copy = new String(sb) ...

  7. 【转载】Java中String类的方法及说明

    转载自:http://www.cnblogs.com/YSO1983/archive/2009/12/07/1618564.html String : 字符串类型 一.      String sc_ ...

  8. JS中String添加trim()方法

    这么牛的JS竟然还要自己封装trim方法. 下面利用prototype和正则表达式的添加方式添加trim(): <script language="javascript"&g ...

  9. JavaScript中String.prototype.replace() 方法的使用

    摘抄于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace ...

随机推荐

  1. LeeCode

    No1. Given an array of integers, return indices of the two numbers such that they add up to a specif ...

  2. c++指针函数的使用——回调函数

    /* 函数指针 函数也是有地址的 所谓函数指针,就是指向函数的指针,函数指针也是一个变量,可以指向不同的函数.同时通过函数指针可以调用其指向函数,从而使函数的调用更加灵活. 函数指针的用途 */ #i ...

  3. dict的操作和三级菜单

    dict的基本操作 # Author:nadech info = { "stu001":"sjp", "stu002":"cxx& ...

  4. Jmeter(三)_配置元件

    HTTP Cookie Manager 用来存储浏览器产生的用户信息 Clear Cookies each Iteration:每次迭代请求,清空cookies,GUI中定义的任何cookie都不会被 ...

  5. django之允许外部机器访问

    开开启django时,使用0.0.0.0:xxxx,作为ip和端口例如: python3 manage.py runserver 0.0.0.0:9000 然后在settings里修改ALLOWED_ ...

  6. 粗浅看Struts2和Hibernate框架

    ----------------------------------------------------------------------------------------------[版权申明: ...

  7. Swift基础之CoreData的使用

    以前使用过OC版本的CoreData应该很好理解Swift方式,所以这里简单的展示一下,增删改查的方法使用,同时给大家说一下创建步骤,方便大家的使用,转载请注明出处,谢谢~ 步骤一:创建一个Swift ...

  8. MyBatis 查询映射自定义枚举

    背景                  MyBatis查询若想映射枚举类型,则需要从 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中选一个来使用         ...

  9. Java经典设计模式之十一种行为型模式(附实例和详解)

    Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:Java经典设计模式之 ...

  10. Redis之(四)事务

    5.1开始事务 MULTI 命令的执行标记着事务的开始: 当客户端处于非事务状态下时, 所有发送给服务器端的命令都会立即被服务器执行. Redis 的事务不可嵌套, 当客户端已经处于事务状态, 而客户 ...