我需要从一个 String
对象中获得一个 Iterator<Character>
。Java中是否有提供此功能的可用函数,还是我必须自己编写代码?
一个选项是使用Guava:
ImmutableList<Character> chars = Lists.charactersOf(someString);
UnmodifiableListIterator<Character> iter = chars.listIterator();
这将产生一个由给定字符串支持的不可变字符列表(无需复制)。
如果您最终需要自己完成此操作,我建议不要像其他示例一样公开Iterator
的实现类。相反,我建议创建自己的实用程序类并公开一个静态工厂方法:
public static Iterator<Character> stringIterator(final String string) {
// Ensure the error is found as soon as possible.
if (string == null)
throw new NullPointerException();
return new Iterator<Character>() {
private int index = 0;
public boolean hasNext() {
return index < string.length();
}
public Character next() {
/*
* Throw NoSuchElementException as defined by the Iterator contract,
* not IndexOutOfBoundsException.
*/
if (!hasNext())
throw new NoSuchElementException();
return string.charAt(index++);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
Iterator<Character>
。 - ColinDIterator
不是 Iterable
(而且 Character
不是 CharSequence
)。 - ColinD这个功能不存在,但实现它非常简单:
class CharacterIterator implements Iterator<Character> {
private final String str;
private int pos = 0;
public CharacterIterator(String str) {
this.str = str;
}
public boolean hasNext() {
return pos < str.length();
}
public Character next() {
return str.charAt(pos++);
}
public void remove() {
throw new UnsupportedOperationException();
}
}
这个实现可能已经是尽可能高效的了。
for (char c : myString.toCharArray()) {
}
char[]
仍然无法分配给Iterable<Character>
。 - O. R. Mapper从其他答案中借鉴,这可能是最好的直接实现(如果您不打算使用guava)。
/**
* @param string
* @return list of characters in the string
*/
public static List<Character> characters(final String string) {
return new AbstractList<Character>() {
@Override
public Character get(int index) {
return string.charAt(index);
}
@Override
public int size() {
return string.length();
}
};
}
CharacterIterator it = new StringCharacterIterator("abcd");
// Iterate over the characters in the forward direction
for (char ch=it.first(); ch != CharacterIterator.DONE; ch=it.next())
// Iterate over the characters in the backward direction
for (char ch=it.last(); ch != CharacterIterator.DONE; ch=it.previous())
Iterator<Character>
,而我只想在这里使用这样的东西。 - AlbertIterable
和Iterator
两个接口是一个非常糟糕的想法,不应该这样做。 - ColinDiterator()
方法中返回this
是完全错误的,会违反契约。 - whiskeysierra简短回答:不行,你必须编码。
详细回答: List和Set都有一个用于获取Iterator的方法(还有一些其他的集合类,但可能不是你要找的)。List和Set接口是集合框架的一部分,只允许添加/删除/迭代对象,如Character或Integer(而不是char或int等基本类型)。Java 1.5中有一个名为自动装箱的特性,它将隐藏基本类型到对象的转换,但我不建议使用它,在这种情况下也无法提供你想要的功能。
另一种选择是在自己的类中包装字符串。
implements Iterator<Character>
但这可能需要比它值得的更多的工作。
这里有一个代码片段可以实现你想要的:
String s = "";
List<Character> list = new ArrayList<Character>(s.length());
for (int i = 0; i < s.length(); i++) {
// note that Character.valueOf() is preferred to new Character()
// you can omit the Character.valueOf() method
// and Java 1.5+ will auto-box the primitive into an Object
list.add(Character.valueOf(s.charAt(i)));
}
Iterator<Character> iterator = list.iterator();
List
和Set
没有实现Iterator
。 - whiskeysierraIterator
合约明确规定不支持remove()
。此外,没有其他替代方案可以使用。 - Albert使用Java 8或更新版本,您可以使用流式处理功能。通过chars()
方法,您可以访问IntStream
。 IntStream
支持iterator()
方法,该方法返回一个OfInt
迭代器。OfInt
实现了Iterator<Integer>
。
String str = "foobar";
OfInt ofit = str.chars().iterator();
Iterator<Integer> it = ofit;
这不是一个完美的答案,因为你要求的是Iterator<Character>。
顺便说一句:使用str.codePoints(),你也可以访问code point IntStream。
没有直接的方法。不过编码并不难:
public static Iterator<Character> gimmeIterator(final String x) {
Iterator<Character> it = new Iterator<Character>() {
String str = x == null ? "" : x;
int pos = -1; // last read
public boolean hasNext() { return(pos+1 < str.length()); }
public Character next() { pos++; return str.charAt(pos); }
public void remove() {
throw new UnsupportedOperationException("remove unsupported for this iterator");
}
};
return it;
}
如果你不想使用Guava,而是想要一个真正的java.util.Iterator
,那么可以借助于Apache Commons Lang来实现这一点。
private static Iterator<Character> iterator(String string) {
return Arrays.asList(ArrayUtils.toObject(string.toCharArray())).iterator();
}
Scanner scanner = new java.util.Scanner(myInput).useDelimiter("");
Scanner实现了Iterator
,所以scanner
现在是一个长度为1的字符串Iterator
,非常接近。
继续讲述(非常?)糟糕的事情,在Java 8中,您可以这样做来简洁地迭代字符:
for (String s: (Iterable<String>)() -> scanner) {
char c = s.charAt(0);
System.out.println(c);
}
关于为什么() -> scanner
有效(以及为什么它可能是危险的,尽管在这种情况下不是),请参见解释如何将此lambda分配给Iterable。