2010-10-13 8 views
31

Necesito un Iterator<Character> desde un objeto String. ¿Hay alguna función disponible en Java que me proporcione esto o tengo que codificar la mía?Java: cómo obtener Iterator <Character> desde String

+0

Véase también http://stackoverflow.com/questions/1527856/how-can-i-iterate-through-the-unicode-codepoints-of-a-java-string – rogerdpack

Respuesta

29

Una opción es utilizar Guava:

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

Esto produce una lista inmutable de caracteres que está respaldada por la cadena dada (sin copia involucrada).

Si termina haciéndolo usted mismo, recomendaría no exponer la clase de implementación para el Iterator como lo hacen otros ejemplos. Me gustaría recomendar en lugar de hacer su propia clase de utilidad y la exposición de un método de fábrica estática:

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

Gracias por la pista. Entonces, supongo que la respuesta a mi pregunta es "No". – Albert

+0

@Albert: Sí, no creo que haya nada en la biblioteca estándar para hacer exactamente lo que quieres. Solo señalando que el código que hace lo que desea existe en una biblioteca sólida y bien probada. – ColinD

+0

@Albert: Por supuesto, puede recorrer el String char-by-char en, por ejemplo, el bucle 'for' utilizando el método' String # toCharArray() '. – Esko

0

No estoy seguro si hay una manera más directa pero podría hacer algo como;

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

arañazos que; Arrays.asList no hace lo que parece recordar que hace.

Edición 2: Parece que trabajó por última vez de esta manera en 1.4

+0

¿Pero no es esto terriblemente ineficiente? – Albert

+0

Sí, esto crearía un solo elemento 'Iterator '. – ColinD

+0

Ambos tienen razón. Lo he comprobado y gritado de vergüenza. Borrando el comentario. Mis excusas no es mi mejor día, es cierto. –

2
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()) 
+2

Eso no es un 'Iterator ' y realmente solo quiero tal cosa aquí. – Albert

+0

qué tal esto: http://www.java2s.com/Code/JavaAPI/java.util/implementsIteratorCharacter.htm – virgium03

+2

@ virgium03: Implementar tanto 'Iterable' como' Iterator' es una muy mala idea y nunca debería hacerse. – ColinD

0

El Iterator iterar sobre una colección o lo pone en práctica. La clase de cadena no implementa esta interfaz. Entonces no hay una forma directa.

Para iterar sobre una cadena, primero tendrá que crear una matriz de caracteres a partir de ella y luego de esta colección de caracteres una Colección.

+6

Es incorrecto decir que un 'iterador' solo itera sobre una colección. 'Iterator' es una interfaz simple que puede iterar sobre cualquier cosa que se implemente para iterar. – ColinD

+0

+1 Tienes razón que solo me ha perdido. –

17

No existe, pero es trivial para aplicar:

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

La aplicación es probablemente tan eficiente como se pone.

2

Respuesta corta: No, usted tiene que codificarlo.

Respuesta larga: lista y establece dos tienen un método para obtener un Iterator (hay algunas otras clases de colección, pero probablemente no es lo que buscas). Las interfaces List y Set son parte de Collections Framework que solo permiten agregar/eliminar/iterar Objetos como Carácter o Entero (no primitivos como char o int). Hay una función en Java 1.5 called auto-boxing que ocultará esta primitiva a la conversión de objeto, pero no la recomiendo y no proporcionará lo que desea en este caso.

Una alternativa sería la de envolver la cuerda en una clase de su propia que

implements Iterator<Character> 

pero que podría ser más trabajo de lo que vale.

Aquí es un fragmento de código para hacer lo que quiere:

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

'List' y' Set' están ** no ** implementando 'Iterator'. – whiskeysierra

+0

Están implementando la interfaz 'Iterable' (que proporciona una función' iterator() 'que devuelve un' Iterator') para que esté bien. Pero su código es ineficiente porque crea una copia completa de la cadena. – Albert

+0

@Willi, está en lo cierto, las interfaces List y Set son subinterfaces de Collection que contienen el método .interator(). Si observa de cerca esto se logra al extender clases abstractas que contienen subclases privadas que de hecho implementan Iterator, que itera sobre los datos dentro de la Lista o Conjunto. Debería haber indicado que tanto List como Set proporcionan un método para obtener un iterador. –

1

ninguna manera directa. No es difícil de codificar, sin embargo:

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

El robo de otra persona en otra respuesta, esta es probablemente la mejor aplicación directa (si no va a utilizar la guayaba).

/** 
* @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(); 
     } 
    }; 
} 
10
for (char c : myString.toCharArray()) { 

} 
+1

Eso funciona en dicho bucle, pero aún 'char []' no se puede asignar a 'Iterable '. –

+2

Además, como las cadenas son inmutables pero las matrices no lo son, debe crear una copia. – mhsmith

1

Esto se puede hacer con un poco de ayuda de Apache Commons Lang (si no desea utilizar la guayaba, y quieren un verdadero java.util.Iterator.

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

con Java 8 o más nuevo puede usar la instalación de flujo. Con el método chars() puede acceder a IntStream. El IntStream admite el método iterator() que devuelve un iterador OfInt. OfInt implementa Iterator<Integer>.

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

No es una solución perfecta, ya que preguntas para iterador < Carácter >.

Por cierto: con str.codePoints() también puede acceder a un punto de código IntStream.

+0

Menciona codePoints(). Un aspecto importante de codePoints() es que le permite tratar con caracteres sustitutos. Ver [esta respuesta] (http://stackoverflow.com/a/40444598/99717). –

0

Esto se siente sucio, pero se puede usar escáner con delimitador de cadena vacía:

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

escáner implementa Iterator, por lo scanner es ahora un Iterator de cadenas de longitud-1, que está cerca.

Continuar con la sucia, en Java 8 a continuación, puede hacer esto para iterar sucintamente por caracteres (muy?):

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

Para obtener más detalles sobre por qué () -> scanner obras (y por qué puede ser peligroso, aunque no en este caso de uso), vea Explain how this lambda can be assigned to an Iterable.

Cuestiones relacionadas