惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
A
Arctic Wolf
M
MIT News - Artificial intelligence
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
Martin Fowler
Martin Fowler
A
About on SuperTechFans
P
Palo Alto Networks Blog
Project Zero
Project Zero
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
大猫的无限游戏
大猫的无限游戏
Cisco Talos Blog
Cisco Talos Blog
P
Proofpoint News Feed
D
DataBreaches.Net
Cyberwarzone
Cyberwarzone
T
Tor Project blog
IT之家
IT之家
P
Proofpoint News Feed
Help Net Security
Help Net Security
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
Microsoft Azure Blog
Microsoft Azure Blog
V2EX - 技术
V2EX - 技术
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
B
Blog
N
News and Events Feed by Topic
The Cloudflare Blog
S
Schneier on Security
P
Privacy & Cybersecurity Law Blog
T
The Blog of Author Tim Ferriss
Recorded Future
Recorded Future
Last Week in AI
Last Week in AI
The Last Watchdog
The Last Watchdog
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LangChain Blog
I
InfoQ
F
Full Disclosure
The Register - Security
The Register - Security
阮一峰的网络日志
阮一峰的网络日志
H
Hacker News: Front Page
V
V2EX

博客园 - ted

Docker commands 低代码 产品的核心价值 如何判断需求的优先级 以组件为基础,基于功能扩展的前端架构 演进中的架构 康威定理 技术解耦和团队解耦 SSL的那些事——三 SSL的那些事_二 Xamarin 技术解析 Javascript Promises 介绍 Javascript的模块化编程 UI 设计概念介绍 Head First iOS Programming 一个Web页面的问题分析 QCon杭州2012技术开发大会感受 Ajax请求与浏览器缓存 Java中的一些基础概念
你了解Java中String的substring函数吗?
ted · 2012-07-31 · via 博客园 - ted

你了解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函数返回的子串也是如此。