是否可以从另一个String的子集创建一个新的String而不进行任何数据复制?

时间:2014-10-07 22:51:54

标签: java string

我有(显然是错误的?)印象,Java子串(srcArray,startIndex,endIndex)方法没有分配新内存,而是重用现有的底层char []数组。由于String的不变性,这种方法似乎是可能的。

然而,在实际查看JDK源代码时,我们发现以下内容:

public String substring(int beginIndex, int endIndex) {

    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}

public String(char value[], int offset, int count) {


    this.value = Arrays.copyOfRange(value, offset, offset+count);

}

请注意复制/复制内容的copyOfRange。

这样做的动机是使用非常大的数组来表示线性代数矩阵的后备存储。我们没有办法接受复制它们。但是,我希望有一个自定义表示支持数组的部分,以方便编程。但是,如果不执行数据复制,那将是合理的。与String的搭配是我研究了String如何处理无数据副本,并发现它们(在后来的java版本中显然是......)确实执行了副本。

那么,实际上有没有办法实现零拷贝?

3 个答案:

答案 0 :(得分:17)

使用就是这样,但它被改变了; Java的设计者得出的结论是,它导致的问题多于解决的问题,特别是由于a)内存泄漏的风险,b)大多数substring调用是针对相对较小的范围而复制成本低的事实

您可以做的最接近的事情是,CharSequence无法复制String的子范围,您可以写CharBuffer.wrap(string).subSequence(from, to)

答案 1 :(得分:1)

java.nio.StringCharBuffer在子字符串上实现这样的“视图”,而不复制字符串的基础char []数组。为了访问String数组,它使用String.charAt函数以及内部偏移量(从String开头的字符偏移量)。

不幸的是,它是包私有的,不能直接使用。但是,超类java.nio.CharBuffer是公共的,您可以使用java.nio.StringCharBuffer所采用的相同方法来实现自己的StringCharBuffer实现。

请注意,如果您不执行此操作,而只是按照另一个答案的建议使用CharBuffer.wrap,您仍然会复制一次数组。

或者,您可能只需要从某个索引开始的字符流。在这种情况下,您可以使用类似以下内容:

IntStream
  .range(start, str.length())
  .map(i -> str.charAt(i))
  ...

获取字符流。请注意,这与执行str.chars().skip(start)不同,因为后者实际上会将光标前进1 start次。

答案 2 :(得分:0)

public class Dring {  // Double String
    double[] array;
    int length;
    int offset;
    // You probably want hash

    public Dring(double[] data) {
        array = new double[data.length];
        <copy from data to array using your favorite array copy method>
        length = data.length;
        offset = 0;
    }

    private Dring(int offsetParm, int lengthParm, double[] data) {
        array = data;
        length = lengthParm;
        offset = offsetParm
    } 

    public Dring subDring(int offsetParm, int lengthParm) {
         // Check parms
         return new Dring(offset + offsetParm, lengthParm, array);
    }

    public double doubleAt(int index) {
        // Check parm
        return array[offset + index];
    }
}