开发者

Java: how to get Iterator<Character> from String [duplicate]

开发者 https://www.devze.com 2023-01-20 11:32 出处:网络
This question already has answers here: What is the easiest/best/most correct way to iterate through the characters of a string in Java?
This question already has answers here: What is the easiest/best/most correct way to iterate through the characters of a string in Java? (16 answers) Closed 4 years ago.

I need a Iterator<Character> from a String object. Is there any available function in Java that provides me this or do I have to code my开发者_JS百科 own?


One option is to use Guava:

ImmutableList<Character> chars = Lists.charactersOf(someString);
UnmodifiableListIterator<Character> iter = chars.listIterator();

This produces an immutable list of characters that is backed by the given string (no copying involved).

If you end up doing this yourself, though, I would recommend not exposing the implementation class for the Iterator as a number of other examples do. I'd recommend instead making your own utility class and exposing a static factory method:

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();
    }
  };
}


It doesn't exist, but it's trivial to implement:

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();
    }
}

The implementation is probably as efficient as it gets.


for (char c : myString.toCharArray()) {

}


Stealing from somebody else in another answer, this is probably the best direct implementation (if you're not going to use 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()) 


Short answer: No, you have to code it.

Long answer: List and Set both have a method for obtaining an Iterator (there are a few other collection classes, but probably not what your looking for). The List and Set interfaces are a part of the Collections Framework which only allow for adding/removing/iterating Objects like Character or Integer (not primitives like char or int). There is a feature in Java 1.5 called auto-boxing that will hide this primitive to Object conversion but I don't recommend it and it won't provide what you want in this case.

An alternative would be to wrap the String in a class of your own that

implements Iterator<Character>

but that might be more work than it is worth.

Here is a code snippet for doing what you want:

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();


No direct way. Not difficult to code, though:

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;
    }


This can be done with a little help from Apache Commons Lang (if you don't want to use Guava, and want a true java.util.Iterator.

private static Iterator<Character> iterator(String string) {
    return Arrays.asList(ArrayUtils.toObject(string.toCharArray())).iterator();
}


With java 8 or newer you can use the stream facility. With the chars() method you can access an IntStream. The IntStream supports the method iterator() that returns an OfInt iterator. OfInt implements Iterator<Integer>.

String str = "foobar";
OfInt ofit = str.chars().iterator();
Iterator<Integer> it = ofit;

It is not a perfect answer, since you asked for Iterator<Character>.

Btw: With str.codePoints() you can also access a code point IntStream.


Not sure if there is a more direct way but you could do something like;

Arrays.asList(string.toCharArray()).iterator();

Scratch that; Arrays.asList doesn't do what I seem to remember it doing.

Edit 2: Seems like it last worked this way in 1.4


The Iterator iterate over a collection or whatever implements it. String class does nost implement this interface. So there is no direct way.

To iterate over a string you will have to first create a char array from it and then from this char array a Collection.


This feels dirty, but you could use Scanner with empty string delimiter:

Scanner scanner = new java.util.Scanner(myInput).useDelimiter("");

Scanner implements Iterator, so scanner is now an Iterator of length-1 strings, which is close.

To continue with the (very?) dirty, in Java 8 you can then do this to succinctly iterate by chars:

for (String s: (Iterable<String>)() -> scanner) { 
  char c = s.charAt(0);
  System.out.println(c); 
}

For details on why () -> scanner works (and why it may be dangerous, though not in this use case), see Explain how this lambda can be assigned to an Iterable.

0

精彩评论

暂无评论...
验证码 换一张
取 消