翻译人员: 铁锚

翻译日期: 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. C程序练习

    1.编程从键盘任意输入两个时间(例如4时55分和1时25分),计算并输出这两个时间之间的间隔.要求不输出时间差的负号. #include<stdio.h> int main() { int ...

  2. CI数据库操作_查询构造器类

    =================数据库操作======================1.数据库配置: config/database.php 用户名 密码 数据库 2 加载数据库类:$this-& ...

  3. 安利三款提升幸福感的chrome插件

    谷歌访问助手 chrome浏览器一直是各大码农推荐的比较好用的浏览器,速度快.插件多. 但是由于众所周知的原因导致了谷歌账号同步.扩展商店访问慢甚至打不开的情况. 谷歌访问助手就是用来解决此问题的. ...

  4. NOIP2017D2T3 列队—Treap

    NOIP2017列队 Description Sylvia 是一个热爱学习的女孩子.  前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m ...

  5. 工作流引擎 Flowable 6.0.0.RC1 release,完全兼容Activi

    Flowable 6.0.0.RC1 release,第一个可流动的6引擎版本(6.0.0.RC1). Flowable 6.0.0.RC1 relase新增加的功能以及特色: 包重命名为org.Fl ...

  6. Android二维码扫描、生成

    Android二维码扫描.生成 现在使用二维码作为信息的载体已经越来越普及,那么二维码的生成以及扫描是如何实现的呢 google为我们提供了zxing开源库供我们使用 zxing GitHub源码地址 ...

  7. Android基础知识点-Manifest清单文件

    每个应用的根目录中都必须包含一个 AndroidManifest.xml 文件(且文件名精确无误). 清单文件向 Android 系统提供应用的必要信息,系统必须具有这些信息方可运行应用的任何代码. ...

  8. 27 自定义View 和案例

    有时安卓提供的View不满足我们的需求时可以创建一个类继承View或其子类重写方法 如 package com.qf.sxy.day28_customview.view; import android ...

  9. 关于在页面中针对不同版本的IE浏览器实现不同的JS或者CSS样式

    一般会用到<!--[if IE]>这里是正常的html代码<![endif]--> 条件注释只能在windows Internet Explorer(以下简称IE)下使用,因此 ...

  10. 2.cocos2dx 3.2中语法的不同之处,lambada表达式的使用和function和bind函数的使用

    1        打开建好的T32  Cocos2dx-3.2的一个项目 2        设置Cocos显示窗口的位置是在AppDelegate.cpp中: 3  设置自适应窗口大小的代码是在上面的 ...