为啥Java中的String类没有实现Iterable?
Posted
技术标签:
【中文标题】为啥Java中的String类没有实现Iterable?【英文标题】:Why doesn't the String class in Java implement Iterable?为什么Java中的String类没有实现Iterable? 【发布时间】:2011-02-15 21:10:14 【问题描述】:许多 Java 框架类实现 Iterable
,但 String
没有。迭代 String
中的字符是有意义的,就像可以迭代常规数组中的项目一样。
String
没有实现Iterable
有什么原因吗?
【问题讨论】:
遍历字符串的 char 数组的问题在哪里? (strInput.ToCharArray) Tim: String#toCharArray 创建一个包含字符串字符副本的数组。即使它有效,它也会增加不必要的开销来迭代字符。 @jambjoIterator<Character>
开销会更少???
@Tom:视情况而定,Iterator我不确定为什么在 2020 年仍未实现,我的猜测是字符串在 Java 中得到了很多特殊处理(编译器重载了 +
运算符以用于字符串连接、字符串文字、字符串常量存储在一个公共池中,等等),这个功能可能比看起来更难实现(或者它可能会搞砸太多事情,从实现者的角度来看是值得付出努力的)。
另一方面,实现与此接近的东西并不是太多的工作。我想在我的一个项目中使用它,所以我编写了这些简单的类:
public class CharIterable implements Iterable<Character>
public CharIterable(CharSequence seq)
this.seq = seq;
@Override
public Iterator<Character> iterator()
return new CharIterator(seq);
private final CharSequence seq;
public class CharIterator implements Iterator<Character>
public CharIterator(CharSequence sequence)
this.sequence = sequence;
@Override
public synchronized boolean hasNext()
return position < sequence.length();
@Override
public synchronized Character next()
return sequence.charAt(position++);
/**
* Character sequence to iterate over
*/
private final CharSequence sequence;
/**
* Current position of iterator which is the position of the item that
* will be returned by @link #next().
*/
private int position = 0;
有了这些我可以做到:
for (Character c: new CharIterable("This is a test"))
\\ do something with c
现在对于这样一个简单的事情来说,这看起来很多,但它允许将字符串视为可迭代的字符数组,并与旨在处理事物集合(列表、集合等)的方法透明地工作。
【讨论】:
【参考方案2】:确实没有一个好的答案。 Java 中的迭代器特别适用于离散项(对象)的集合。您会认为实现CharSequence
的String
应该是离散字符的“集合”。相反,它被视为恰好由字符组成的单个实体。
在 Java 中,迭代器似乎只真正应用于集合而不是字符串。没有理由这样做(据我所知,您可能不得不与 Gosling 或 API 编写者交谈);这似乎是惯例或设计决定。事实上,没有什么阻止 CharSequence
实现Iterable
。
也就是说,您可以像这样遍历字符串中的字符:
for (int i = 0; i < str.length(); i++)
System.out.println(str.charAt(i));
或者:
for(char c : str.toCharArray())
System.out.println(c);
或者:
"Java 8".chars().forEach(System.out::println);
还请注意,您不能就地修改字符串的字符,因为字符串是不可变的。 String 的可变伴侣是 StringBuilder(或旧的 StringBuffer)。
编辑
根据这个答案的 cmets 进行澄清。我试图解释一个可能的理由为什么String
上没有迭代器。我并不是想说这是不可能的。事实上,我认为CharSequence
实现Iterable
是有意义的。
String
提供CharSequence
,如果只是在概念上,它与String
不同。 String
通常被认为是一个单一的实体,而 CharSequence
正是这样:一个字符序列。在字符序列(即CharSequence
)上使用迭代器是有意义的,但不仅仅是在String
本身上。
正如 Foxfire 在 cmets 中正确指出的那样,String
实现了 CharSequence
接口,因此在类型方面,String
是 CharSequence
。从语义上讲,在我看来它们是两个独立的东西——我可能在这里很迂腐,但是当我想到 String
时,我通常认为它是一个恰好由字符组成的单一实体。考虑数字序列1, 2, 3, 4
和数字1234
之间的区别。现在考虑字符串abcd
和字符序列a, b, c, d
之间的区别。我试图指出这种差异。
在我看来,问为什么String
没有迭代器就像问为什么Integer
没有迭代器以便您可以迭代各个数字。
【讨论】:
当然将字符串视为字母的集合并非完全没有先例,并且在“有意义”的情况下争论它似乎有点虚假。 “字符串并不是真正的离散字符的“集合”。”。嗯,它是。事实上,它甚至实现了 CharSequence,这正是:离散字符的有序集合! 你可以在 C# 中做foreach (char c in s)
,很漂亮!
@Vivin:CharSequence 是一个 INTERFACE(完全是 Iterable)。因此,实现接口的是 String 本身。它不是从字符串创建的。
@Vivin:那么恕我直言,您应该尝试将原始问题回答为:“为什么 CharSequence 不实现 Iterable”。 (这当然在技术上仍然意味着“为什么 String 不实现 Iterable”)【参考方案3】:
使 String 实现 Iterable 的主要原因之一是启用简单的 for(each) 循环,如上所述。因此,不让 String 实现 Iterable 的一个原因可能是天真的实现固有的低效率,因为它需要对结果进行装箱。但是,如果生成的迭代器(由 String.iterator() 返回)的实现是最终的,编译器可以对其进行特殊处理并生成免于装箱/拆箱的字节码。
【讨论】:
【参考方案4】:他们只是忘了这样做。
【讨论】:
你有这个断言的证据吗?在我看来,这似乎更可能是因为 String 早于 Iterable 接口(Strings presumable 可以追溯到 Java 1.0,Iterable 可以追溯到 Java 1.5),并且一旦语言说明符习惯于不将 String 视为集合之一,他们继续这样对待它。【参考方案5】:无论如何,我的同事 Josh Bloch 强烈希望将此功能添加到 Java 7:
for (char c : aString) ...
和
for (int codePoint : aString) ...
这将是循环遍历字符和逻辑字符(代码点)的最简单方法。它不需要让String
实现Iterable
,这将强制发生拳击。
如果没有该语言功能,就不会为这个问题提供真正好的答案。而且他似乎很乐观地认为他能做到这一点,但我不确定。
【讨论】:
可惜没有成为 Java 7 的项目代币。 如果他们有朝一日计划这样做,请确保它适用于任何继承CharSequence
的对象,而不是仅适用于 String
。
@akuhn 既没有进入 Java 8 也没有进入 Java 9... RIP。
@JoaaoVerona 不是直接的,就像让 CharSequence 或 String 实现 Iterable 一样。但是 Java 8 使用(默认)方法 chars()
和 codePoints()
扩展了 CharSequence 接口,它们返回一个 IntStream。该接口有一个forEach(IntConsumer action)
方法,这是下一个最好的方法。你可以写"test".chars().forEach(c -> ...)
,它与for循环没有太大区别。我怀疑没有 String 或 CharSequence 实现 Iterable 的一个原因是您可以迭代其字符或其代码点。一个重要的区别。【参考方案6】:
Iterable
是什么? Iterable<Integer>
最有意义,其中每个元素代表一个 Unicode 代码点。当我们有toCharArray
时,即使Iterable<Character>
也会变得缓慢且毫无意义。
【讨论】:
【参考方案7】:原因很简单:字符串类比Iterable要老很多。
显然没有人愿意将接口添加到 String(这有点奇怪,因为它确实实现了基于完全相同的想法的 CharSequence)。
但是它会因为 Iterable 返回一个对象而有些不便。所以它必须包装每个返回的 Char。
编辑:作为比较:.Net 确实支持枚举 String,但在 .Net 中 Iterable 也适用于本机类型,因此不需要像 Java 中那样需要包装。
【讨论】:
“将 Iterable 添加到 String 类使其性能不佳”,有道理;但是没有人因为它太旧而将 Itreable 添加到 String 类中,这似乎有点奇怪。你能再解释一下吗? 字符串早在 Iterable 之前就已经存在了。因此,您必须稍后添加接口。虽然这是可能的,但在某些极端情况下,它可能是一个突破性的变化。考虑到 String 的使用频率,这可能被认为是有风险的。这只是猜测。我不知道这些考虑是否真的影响了这个决定。但这似乎很有可能。 我不认为将Iterable
(或任何类型)添加到String
是一项重大更改。你不能继承String
(谢天谢地)。
@Tom:在 99.9% 的情况下肯定不会。但是很容易构建可能会崩溃的案例(例如,反映在接口上)。考虑到基本上每个应用程序都在某个地方使用字符串,这仍然可能是一个原因。
任何被破坏的代码都应该被破坏。我认为我可以肯定地说这不是考虑的原因。【参考方案8】:
如果你真的想在这里迭代:
String str = "***";
for (char c: str.toCharArray())
//here you go
【讨论】:
-1 抱歉,我看不出这个答案与所提问题有什么关系。 一个问题可能是 toCharArray 创建了一个新数组。所以这是非常低效的。 @Helper:字符串是不可变的。但是返回的数组不是。并且更改数组不得影响字符串。所以它确实制作了一个完整的副本。 +1 - 对于小字符串,创建一个 char[] 与创建一个迭代器大致一样昂贵 - 它是一个对象分配(以及少量的内存初始化和复制)。随着字符串变长,内存初始化/复制开销变得很大,但仍远不及对每个字符进行装箱。以上是关于为啥Java中的String类没有实现Iterable?的主要内容,如果未能解决你的问题,请参考以下文章
JAVA中,为啥object对象中的equals方法比较的是同一,而String对象比较的是相等?
java中的string类中内容一旦被初始化就不能改变为啥还有replace方法呢?
为啥 java String类 中的valueOf(Object obj) 空时返回的是"null"而不是null
为啥包含 main 方法的类没有实例化并且在 Java 中仍然可以?