你了解Java中String的substring函数吗?
你了解Java中String的substring函数吗?
Java中的substring函数是我们经常使用的一个函数,用来截取当前字符串的子串,定义如下:
public final class String{ public String substring(int beginIndex); public String substring(int beginIndex, int endIndex); }
使用及声明都非常简单,但是你了解其中的细节吗?
我们再看一下substring的实现:
1 public String substring(int beginIndex, int endIndex) { 2 if (beginIndex < 0) { 3 throw new StringIndexOutOfBoundsException(beginIndex); 4 } 5 if (endIndex > count) { 6 throw new StringIndexOutOfBoundsException(endIndex); 7 } 8 if (beginIndex > endIndex) { 9 throw new StringIndexOutOfBoundsException(endIndex - beginIndex); 10 } 11 return ((beginIndex == 0) && (endIndex == count)) ? this : 12 new String(offset + beginIndex, endIndex - beginIndex, value); 13 }
在第12行返回了一个新的字符串,传入了三个参数:offset,count,以及原来String对象的value(char[])。
继续看第12行String的构造函数:
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
这是一个Package内部的方法,非public,注意他直接将传入的char[]给抓住了,定义了起始位置,以及长度。
我理解他设计的初衷是为了节省内存,新的字符串还依然抓着老的字符串value的引用,只是重新定义起始位置和长度。
再补充一下String类的字段声明,可以看得更清楚一点:
public final class String{ /** The value is used for character storage. */ private final char value[]; /** The offset is the first index of the storage that is used. */ private final int offset; /** The count is the number of characters in the String. */ private final int count; /** Cache the hash code for the string */ private int hash; // Default to 0 }
---------------------------------------------
华丽的分割线下是我的使用场景:
逐行读取一个非常大的文本文件,每一行的长度比较大,提取其中的小部分(使用了substring函数),大约每行提取10个字,行数很多(整个文本可能会有几十M或更多)。
读取文件完毕后我理解的内存消耗不会很大,但是完全出乎我的想象,内存消耗很大,甚至会outofmemory.
我的代码:
List<String> results = new ArrayList<String>(); InputStream stream = new FileInputStream(filePath); BufferedReader bufferredReader = new BufferedReader( new InputStreamReader(stream)); while (true) { String line = bufferredReader.readLine();
if (line == null) {
break;
}
results.add(line.substring(10, 20)); }
Why?在查看了String的substring函数以及多方Google之后,终于明白了。
原来新构建的String依然抓着每一行的文本,只是调整了offset和length,不是我们所理解的只抓着一个小文本,原来的长文本被GC回收这么回事。
怎么办呐?只需要改一句:
List<String> results = new ArrayList<String>(); InputStream stream = new FileInputStream(filePath); BufferedReader bufferredReader = new BufferedReader( new InputStreamReader(stream)); while (true) { String line = bufferredReader.readLine(); if (line == null) { break; } results.add(new String(line.substring(10, 20))); }
构建一个新的String,将字串传入,这样与原始字符串就没有关系了。所以在使用substring的时候,必须要注意使用场景。
最后再提醒一个,String的split函数返回的子串也是如此。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端