2

While i was developing my Android application, my attention was caught by the backing array.

Multiple strings can share the same char[] because strings are immutable. The substring(int) method always returns a string that shares the backing array of its source string. Generally this is an optimization: fewer character arrays need to be allocated, and less copying is necessary. But this can also lead to unwanted heap retention. Taking a short substring of long string means that the long shared char[] won't be garbage until both strings are garbage. This typically happens when parsing small substrings out of a large input. To avoid this where necessary, call new String(longString.subString(...)). The string copy constructor always ensures that the backing array is no larger than necessary.

Then i have read many resources on the web. I know that Strings no longer shares the same backing array since jdk7u6, pointed by Mark Rotteveel.

Then i started trying to play with, and to test the existence of the shared backing array in my code:

String str = "1234567890";
System.out.println("str.substring(1): "    + (str == str.substring(1)));
System.out.println("str.substring(0, 9): " + (str == str.substring(0, 9)));

First i assume == is a swallow equal comparison (isn't it?) that compares their memory locations (like what in C).

If they do share the same backing array, and == is just comparing their memory locations, both statements should return true (or at least the last one returns true).

However, they are both false.

Well, i think my laptop is having Java 7 update 21. (shown in Win7 -> Control Panel -> Java Control Panel -> About) (and also in Eclipse -> Window -> Preference -> Java -> Compiler -> Compiler compliance level: {1.3 , 1.4 , 1.5 , 1.6 , 1.7} )

And i know 1.7 means Java 7. And i know Java 7 is too high that the shared backing array may already have taken away. That is why i have asked Eclipse to compile my project with 1.5 and 1.6.

Java project -> Properties -> Java Complier -> Compiler compliance level = 1.5

However, as i said, i am still getting false in those println()s.

Let me summarise my questions:

Q1) How to use Java codes to verify there is a use of underlying backing array? (other than making the OutOfMemoryError by millions of substring()s)

Q1) a. Is == sallow equal comparison?

Q1) b. Am i really using Java 5/6 to compile, if i set Compiler compliance level = 1.5/1.6?

Thanks for any input :-)

4

3 回答 3

3

即使 String 对象共享相同的支持字符数组,它们也会比较不同,因为 String 对象本身是不同的对象。

您可以使用反射获得支持数组。在我的系统上,后备阵列的名称是,value但不能保证它在您的系统上是相同的。

import java.lang.reflect.*;

public class Main
{
    static Field stringBackingField = null;

    public static void main(String[] args) throws Exception
    {
        String strA = "1234567890";
        String strB = "1234567890";
        char[] charA = getBackingArray(strA);
        char[] charB = getBackingArray(strB);
        char[] subA1 = getBackingArray(strA.substring(1));
        char[] subA2 = getBackingArray(strA.substring(0, 9));
        System.out.println("charA address: " + System.identityHashCode(charA));
        System.out.println("charB address: " + System.identityHashCode(charB));
        System.out.println("subA1 address: " + System.identityHashCode(subA1));
        System.out.println("subA2 address: " + System.identityHashCode(subA2));
        System.out.println("charA == charB: " + (charA == charB));
        System.out.println("charA == subA1: " + (charA == subA1));
        System.out.println("charA == subA2: " + (charA == subA2));
    }

    public static char[] getBackingArray(String s) throws Exception
    {
        if (stringBackingField == null)
        {
            stringBackingField = String.class.getDeclaredField("value");
            stringBackingField.setAccessible(true);
        }
        return (char[]) stringBackingField.get(s);
    }
}

Java 6 上的输出:

$ /usr/lib/jvm/java-6-openjdk-amd64/bin/java -version
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.3) (6b27-1.12.3-0ubuntu1~12.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
$ /usr/lib/jvm/java-6-openjdk-amd64/bin/java Main 
charA address: 1383884648
charB address: 1383884648
subA1 address: 1383884648
subA2 address: 1383884648
charA == charB: true
charA == subA1: true
charA == subA2: true

Java 7 上的输出:

$ /usr/lib/jvm/java-7-openjdk-amd64/bin/java -version
java version "1.7.0_15"
OpenJDK Runtime Environment (IcedTea7 2.3.7) (7u15-2.3.7-0ubuntu1~12.04.1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
$ /usr/lib/jvm/java-7-openjdk-amd64/bin/java Main
charA address: 1264720121
charB address: 1264720121
subA1 address: 357935641
subA2 address: 722623040
charA == charB: true
charA == subA1: false
charA == subA2: false

编辑:

要查找支持数组成员的名称,请使用打印所有成员

java.util.Arrays.toString(String.class.getDeclaredFields());

并寻找一个 type char[]

于 2013-04-19T21:41:33.717 回答
1

substring() 返回一个新的 String 对象。即使这个返回的新字符串将引用相同的后备数组,字符串本身也是与调用 substring() 方法的字符串不同的对象。因此,当您使用运算符 == 时,它将返回 false。

class MyClass {
    public Object myObject;

    public static void main(String args[]) {
        Object sharedObject = new Object();

        MyClass sampleA = new MyClass();
        sampleA.myObject = sharedObject;

        MyClass sampleB = new MyClass();
        sampleB.myObject = sharedObject;


        //Same object shared by different objects. Returns true.
        System.out.println(sampleA.sharedObject == sampleB.sharedObject);

        //They are different objects, as it is your str and str.substring(1).
        //So this will return false.
        System.out.println(sampleA == sampleB);
    }
}
于 2013-04-19T21:36:03.447 回答
0

1)我不明白你为什么需要找出这个。由于字符串是不可变的,因此一旦您对其进行操作,就会创建一个新字符串(因此不会影响具有相同“支持数组”的任何内容)。正如 Louis 所说,您并不打算仔细研究代码中字符串的底层工作。没有太多意义。

2) == 比较两个对象的指针。

3)这些语句是错误的是正确的,因为当您执行 substring 方法时,它正在创建一个新字符串,因此将有一个指向原始字符串的不同指针。

于 2013-04-19T21:40:55.447 回答