许多 Java 框架类实现Iterable
了 ,但String
没有。迭代 a 中的字符是有意义的String
,就像可以迭代常规数组中的项目一样。
String
有没有实施的原因Iterable
?
真的没有很好的答案。Java 中的迭代器特别适用于离散项(对象)的集合。您会认为String
实现的 aCharSequence
应该是离散字符的“集合”。相反,它被视为恰好由字符组成的单个实体。
在 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)。
编辑
根据对此答案的评论进行澄清。我试图解释为什么在String
. 我并不是想说这是不可能的。确实,我认为CharSequence
实施Iterable
.
String
提供CharSequence
,如果仅在概念上,它与 a 不同String
。AString
通常被认为是一个单一的实体,而CharSequence
恰恰是:一个字符序列。在字符序列(即 on )上有一个迭代器是有意义的CharSequence
,但不仅仅是在 aString
本身上。
正如 Foxfire 在评论中正确指出的那样,String
实现了CharSequence
接口,所以类型方面, aString
是 a CharSequence
。从语义上讲,在我看来它们是两个独立的东西——我可能在这里很迂腐,但当我想到 a 时,String
我通常认为它是一个恰好由字符组成的单一实体。1, 2, 3, 4
考虑数字序列和数字之间的区别1234
。abcd
现在考虑字符串和字符序列之间的区别a, b, c, d
。我试图指出这种差异。
在我看来,问为什么String
没有迭代器就像问为什么Integer
没有迭代器,这样你就可以迭代各个数字。
原因很简单:字符串类比 Iterable 更古老。
显然没有人想要将接口添加到 String (这有点奇怪,因为它确实实现了基于完全相同的想法的 CharSequence)。
但是,它会有些低效,因为 Iterable 返回一个 object。所以它必须包装每个返回的 Char。
编辑:作为比较:.Net 确实支持枚举 String,但是在 .Net Iterable 中也适用于本机类型,因此不需要包装,因为它在 Java 中是必需的。
对于它的价值,我的同事 Josh Bloch 强烈希望将此功能添加到 Java 7:
for (char c : aString) { ... }
和
for (int codePoint : aString) { ... }
这将是循环遍历字符和逻辑字符(代码点)的最简单方法。它不需要制作String
implement Iterable
,这将迫使拳击发生。
如果没有该语言功能,就不会有一个很好的答案来解决这个问题。他似乎非常乐观地认为他能做到这一点,但我不确定。
他们只是忘了这样做。
如上所述,使 String 实现 Iterable 的主要原因之一是启用简单的 for(each) 循环。因此,不让 String 实现 Iterable 的一个原因可能是天真的实现固有的低效率,因为它需要对结果进行装箱。但是,如果生成的迭代器(由 String.iterator() 返回)的实现是最终的,编译器可以对其进行特殊处理并生成免于装箱/拆箱的字节码。
如果你真的很想在这里迭代:
String str = "StackOverflow";
for (char c: str.toCharArray()){
//here you go
}
我不确定为什么这在 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
}
现在,对于这样一个简单的事情来说,这看起来很多,但它允许将字符串视为可迭代的字符数组,并与旨在处理事物集合(列表、集合等)的方法透明地工作。
Iterable
什么?Iterable<Integer>
最有意义,其中每个元素代表一个 Unicode 代码点。Iterable<Character>
当我们有时,即使是缓慢而毫无意义的toCharArray
。