为什么 char[] 的表现比 String 好?- Java

7
关于链接:文件 IO 调优 的最后一节“Further Tuning”中,作者建议使用char[]来避免为文件中的n行生成String对象,我需要了解如何

char[] arr = new char{'a','u','t','h', 'o', 'r'}

String s = "author"

在内存消耗或其他性能因素方面有何区别?String对象不是内部存储为字符数组吗?我感到很傻,因为我以前从未想过这个问题。 :-)


8
这篇文章已经超过10年了。这是否是你真正想要的? - Dmitry B.
原因在(非常古老的)文章中也有解释。 - Dave Newton
@DmitryBeransky:感谢指出这一点。但是,仍然建议使用char[],对吗? - name_masked
5个回答

9
在Oracle的JDK中,一个String有四个实例级字段:
  • 一个字符数组
  • 一个整数偏移量
  • 一个整数字符计数
  • 一个整数哈希值
这意味着每个String除了字符数组本身之外,还引入了一个额外的对象引用(String本身)和三个整数。(偏移量和字符计数存在是为了允许字符数组在通过String#substring()方法产生的String实例之间共享,这是一种设计选择,一些其他Java库实现者已经避免了。)除了额外的存储成本,还有一个更高级别的访问间接性,更不用说String保护其字符数组的边界检查了。
如果您只使用基本字符数组进行分配和消耗,那么可以节省空间。但在Java中这并不是惯用的方法;应该谨慎地添加注释来证明这个选择的合理性,最好提到从分析差异中获得的证据。

6
在你所提到的例子中,这是因为整个循环只分配了一个字符数组。它一遍又一遍地读取相同的数组,并在原地处理它。
与使用readLine相比,后者需要在每次迭代中创建新的String实例。每个String实例将包含几个int字段和一个包含实际数据的char[]的引用——因此每次迭代需要两个新实例。
通常我会认为这些差异微不足道(假设从磁盘读取数据),与读取数据涉及的IO相比 - 假设这是来自硬盘的数据 - 但我相信这就是作者试图表达的观点。

2

作者没有正确理解原因。在使用in.readLine()时,真正的开销是将char[]缓冲区复制成字符串时产生的。当处理大量数据时,额外的复制是最为致命的成本。

可以在JDK内部进行优化,以避免不必要的复制。


2
以下是一些理由,使得在Java中使用字符数组比String更好:
例如存储密码:
1)由于Java中的字符串是不可变的,如果将密码存储为纯文本,则它会一直存在内存中,直到垃圾收集器清除它,并且由于字符串在字符串池中用于可重用性,因此它有很高的机会长时间留在内存中,这会带来安全威胁。
任何可以访问内存转储的人都可以找到明文密码,这是您应始终使用加密密码而不是纯文本的另一个原因。
由于字符串是不可变的,因此不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果使用char[],则仍然可以将其所有元素设置为空或零。因此,将密码存储在字符数组中显然可以减轻窃取密码的安全风险。
2)Java本身推荐使用JPasswordField的getPassword()方法返回char[],而弃用getText()方法返回明文密码,原因是出于安全考虑。遵循Java团队的建议并遵守标准比违反标准更好。
3)如果使用String,则始终存在将明文文本打印到日志文件或控制台的风险,但如果使用Array,则不会打印数组内容,而是打印其内存位置。虽然这不是一个真正的原因,但仍然有道理。
对于这个简单的程序:
String strPassword="Unknown";
char[] charPassword= new char[]{'U','n','k','n','o','w','n'};
System.out.println("String password: " + strPassword);
System.out.println("Character password: " + charPassword);

输出:

String password: Unknown
Character password: [C@110b053

这就是为什么在Java中,字符数组比字符串更适合用于存储密码的全部内容。虽然使用char[]并不足以确保安全,你需要擦除内容来提高安全性。
希望这能有所帮助。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接