2008-11-12 26 views
126

¿Hay alguna manera de determinar si el ciclo se está iterando por última vez? Mi código es como la siguiente:Última iteración del bucle for mejorado en java

int[] array = {1, 2, 3...}; 
StringBuilder builder = new StringBuilder(); 

for(int i : array) 
{ 
    builder.append("" + i); 
    if(!lastiteration) 
     builder.append(","); 
} 

Ahora la cosa es que no quiero añadir la coma en la última iteración. Ahora hay una manera de determinar si es la última iteración o estoy atrapado con el ciclo for o usando un contador externo para realizar un seguimiento.

+0

Yeah! Es gracioso, ¡solo quería hacer exactamente la misma pregunta! – PhiLho

+0

El mismo tipo de pregunta regresa (y lo hará). Ahora, ¿por qué querría crear un bucle si un elemento necesita un tratamiento diferente? http://stackoverflow.com/questions/156650/does-the-last-element-in-a-loop-deserve-a-separate-treatment – xtofl

+0

Dado que tiene una matriz fija, ¿por qué usar la mejorada? for (int i = 0; i AnthonyJClink

Respuesta

39

Puede ser más fácil agregar siempre. Y luego, cuando hayas terminado con tu ciclo, simplemente elimina el personaje final. Tone menos condicionales de esa manera también.

Puede utilizar StringBuilder 's deleteCharAt(int index) con índice es length() - 1

+5

la solución más eficiente para este problema. +1. –

+9

Quizás soy yo, pero realmente no me gusta el hecho de que estás eliminando algo que acabas de agregar ... – Fortega

+2

Fortega: No me gusta revisar cada vez algo que voy a hacer el 99% de las veces. Parece más lógico (y es más rápido) aplicar Dinah's o Solución de sblundy – Buffalo

4

Si convierte a un bucle índice clásico, sí.

O simplemente podría eliminar la última coma después de que haya terminado. De este modo:

int[] array = {1, 2, 3...}; 
StringBuilder 

builder = new StringBuilder(); 

for(int i : array) 
{ 
    builder.append(i + ","); 
} 

if(builder.charAt((builder.length() - 1) == ',')) 
    builder.deleteCharAt(builder.length() - 1); 
Me

, sólo tiene que utilizar StringUtils.join() de commons-lang.

5

que sea sencillo y utilizar un estándar para el bucle:

for(int i = 0 ; i < array.length ; i ++){ 
    builder.append(array[i]); 
    if(i != array.length - 1){ 
     builder.append(','); 
    } 
} 

o simplemente usan Apache commons-lang StringUtils.join() bucles

6

explícitos siempre funcionan mejor que las implícitas.

builder.append("" + array[0]); 
for(int i = 1; i != array.length; i += 1) { 
    builder.append(", " + array[i]); 
} 

Usted debe envolver todo el asunto en una sentencia if en caso de que está tratando con una matriz de longitud cero.

+0

Me gusta este enfoque porque no hace innecesariamente realizar una prueba en cada iteración. Lo único que agregaría es que el constructor puede agregar enteros directamente para que no haya necesidad de usar "" + int, simplemente anexar (matriz [0]). – Josh

+0

Necesita una más si se trata de todo el lote para asegurarse de que la matriz tenga al menos un elemento. –

+2

¿por qué todos siguen usando ejemplos con cadenas concentradas? atenation DENTRO de append? el "," + x se compila en el nuevo StringBuilder (",") .append (x) .toString() ... –

214

Otra alternativa es agregar la coma antes de agregar i, pero no en la primera iteración de . (Por favor, no use "" + i, por cierto - que realmente no quiere concatenación aquí, y StringBuilder tiene un append en perfecto estado() Sobrecarga int.)

int[] array = {1, 2, 3...}; 
StringBuilder builder = new StringBuilder(); 

for (int i : array) { 
    if (builder.length() != 0) { 
     builder.append(","); 
    } 
    builder.append(i); 
} 

Lo bueno de esto es que lo hará trabaje con cualquier Iterable - no siempre puede indexar cosas. (La frase "agregue la coma y luego elimínela al final" es una buena sugerencia cuando realmente usa StringBuilder, pero no funciona para cosas como escribir en streams. Sin embargo, posiblemente sea el mejor enfoque para este problema exacto.)

+0

Lástima que solo puedo votar esta vez. – erickson

+0

Me gusta esta solución. Puede tener un pequeño costo de rendimiento, pero en la mayoría de los casos es casi cero. – PhiLho

+0

¡sí, las dos sugerencias merecen 2 votos! –

0

Otro enfoque es tener la longitud de la matriz (si está disponible) almacenada en una variable separada (más eficiente que volver a verificar la longitud cada vez). Luego puede comparar su índice con esa longitud para determinar si agrega o no la coma final.

EDITAR: Otra consideración es ponderar el costo de rendimiento de la eliminación de un carácter final (que puede causar una copia de cadena) en contra de tener un condicional se compruebe en cada iteración.

+0

Eliminar un carácter final no debería causar una copia de cadena. Los métodos delete/deleteCharAt de StringBuffer finalmente llaman al siguiente método dentro de la clase de sistema: matrices de origen y destino idénticas: Sistema -> public static void array (Objeto src, int srcPos, Objeto dest, int destPos, int longitud); – Buffalo

2

Aquí es una solución:

int[] array = {1, 2, 3...}; 
StringBuilder builder = new StringBuilder(); 
bool firstiteration=true; 

for(int i : array) 
{ 
    if(!firstiteration) 
     builder.append(","); 

    builder.append("" + i); 
    firstiteration=false; 
} 

Busca la primera iteración :)  

0

dos caminos alternativos aquí:

1: Apache Commons String Utils

2: Mantener un booleano llamado first, establecido en verdadero. En cada iteración, si first es falso, agregue su coma; después de eso, configure first en falso.

+0

1) El enlace está muerto 2) Me tomó más tiempo leer la descripción en lugar del código :) – Buffalo

29

Quizás esté utilizando la herramienta incorrecta para el trabajo.

Esto es más manual que lo que está haciendo pero está en una forma más elegante si no un poco "vieja escuela"

StringBuffer buffer = new StringBuffer(); 
Iterator iter = s.iterator(); 
while (iter.hasNext()) { 
     buffer.append(iter.next()); 
     if (iter.hasNext()) { 
      buffer.append(delimiter); 
     } 
} 
14

Otra solución (tal vez el más eficiente)

int[] array = {1, 2, 3}; 
    StringBuilder builder = new StringBuilder(); 

    if (array.length != 0) { 
     builder.append(array[0]); 
     for (int i = 1; i < array.length; i++) 
     { 
      builder.append(","); 
      builder.append(array[i]); 
     } 
    } 
+0

Esta es una solución natural, excepto que depende de que la matriz no esté vacía. – orcmid

+5

@orcmid: si la matriz está vacía, esto todavía da la salida correcta, una cadena vacía. No estoy seguro de cuál es tu punto. – Eddie

139

otra manera de hacer esto:

String delim = ""; 
for (int i : ints) { 
    sb.append(delim).append(i); 
    delim = ","; 
} 

actualización: para Java 8, que ahora tienen Collectors

+1

Tuve que eliminar mi respuesta similar, eso me enseñará a no publicar antes de mirar más detenidamente otras respuestas. –

+1

También tenga en cuenta que esto maneja el posible problema que Robert Paulson mencionó en otro hilo de comentarios: esta técnica no depende de que StringBuilder esté vacío cuando se anexan los valores de la matriz. –

+1

Aunque el código es más resbaladizo/pulcro/divertido, es menos obvio que la versión if. Siempre use la versión más obvia/legible. –

0

Si solo está convirtiendo una matriz en una matriz delimitada por comas, muchos idiomas tienen una función de combinación para exactamente eso. Convierte una matriz en una cadena con un delimitador entre cada elemento.

3

Necesita Class Separator.

Separator s = new Separator(", "); 
for(int i : array) 
{ 
    builder.append(s).append(i); 
} 

La implementación de la clase Separator es sencilla. Envuelve una cadena que se devuelve en cada llamada de toString(), excepto la primera llamada, que devuelve una cadena vacía.

3

Basado en java.util.AbstractCollection.toString(), sale antes para evitar el delimitador.

StringBuffer buffer = new StringBuffer(); 
Iterator iter = s.iterator(); 
for (;;) { 
    buffer.append(iter.next()); 
    if (! iter.hasNext()) 
    break; 
    buffer.append(delimiter); 
} 

Es eficiente y elegante, pero no tan evidente como algunas de las otras respuestas.

+0

Olvidó incluir el retorno anticipado cuando '(! I.hasNext())', que es una parte importante de la solidez del enfoque general. (Las otras soluciones aquí manejan las colecciones vacías con gracia, ¡así que la tuya también! :) –

1

Otra opción más.

StringBuilder builder = new StringBuilder(); 
for(int i : array) 
    builder.append(',').append(i); 
String text = builder.toString(); 
if (text.startsWith(",")) text=text.substring(1); 
+0

Hice una variación en la otra pregunta – 13ren

15

Esto es casi una repetición de this StackOverflow question. Lo que quiere es StringUtils, y para llamar al join método.

StringUtils.join(strArr, ','); 
1

Muchas de las soluciones que aquí se describen son un poco exageradas, en mi humilde opinión, especialmente aquellas que dependen de bibliotecas externas. Hay una agradable y clara expresión idiomática para lograr una lista separada por comas que siempre he usado. Se basa en el operador condicional (?):

Editar: Solución original correcta, pero no óptima según los comentarios.Tratando una segunda vez:

int[] array = {1, 2, 3}; 
    StringBuilder builder = new StringBuilder(); 
    for (int i = 0 ; i < array.length; i++) 
      builder.append(i == 0 ? "" : ",").append(array[i]); 

Hay que ir, en 4 líneas de código, incluyendo la declaración de la matriz y el StringBuilder.

+0

Pero estás creando un nuevo StringBuilder en cada iteración (gracias al operador +). –

+0

Sí, si miras en el bytecode, tienes razón. No puedo creer que una pregunta tan simple pueda ser tan complicada. Me pregunto si el compilador puede optimizar aquí. –

+0

Proporcionado la segunda solución, con suerte mejor. –

0

En este caso, realmente no hay necesidad de saber si es la última repetición. Hay muchas maneras en que podemos resolver esto. Una forma sería:

String del = null; 
for(int i : array) 
{ 
    if (del != null) 
     builder.append(del); 
    else 
     del = ","; 
    builder.append(i); 
} 
1

Aquí es un punto de referencia SSCCE Corrí (relacionado con lo que tenía que poner en práctica) con estos resultados:

elapsed time with checks at every iteration: 12055(ms) 
elapsed time with deletion at the end: 11977(ms) 

En mi ejemplo, al menos, saltarse la comprobación en cada iteración no es notablemente más rápido, especialmente para los volúmenes de datos, pero es más rápido.

import java.util.ArrayList; 
import java.util.List; 


public class TestCommas { 

    public static String GetUrlsIn(int aProjectID, List<String> aUrls, boolean aPreferChecks) 
    { 

    if (aPreferChecks) { 

     StringBuffer sql = new StringBuffer("select * from mytable_" + aProjectID + " WHERE hash IN "); 

     StringBuffer inHashes = new StringBuffer("("); 
     StringBuffer inURLs = new StringBuffer("("); 

     if (aUrls.size() > 0) 
     { 

     for (String url : aUrls) 
     { 

     if (inHashes.length() > 0) { 
     inHashes.append(","); 
     inURLs.append(","); 
     } 

     inHashes.append(url.hashCode()); 

     inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"");//.append(","); 

     } 

     } 

     inHashes.append(")"); 
     inURLs.append(")"); 

     return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString(); 
    } 

    else { 

     StringBuffer sql = new StringBuffer("select * from mytable" + aProjectID + " WHERE hash IN "); 

     StringBuffer inHashes = new StringBuffer("("); 
     StringBuffer inURLs = new StringBuffer("("); 

     if (aUrls.size() > 0) 
     { 

     for (String url : aUrls) 
     { 
     inHashes.append(url.hashCode()).append(","); 

     inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"").append(","); 
     } 

     } 

     inHashes.deleteCharAt(inHashes.length()-1); 
     inURLs.deleteCharAt(inURLs.length()-1); 

     inHashes.append(")"); 
     inURLs.append(")"); 

     return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString(); 
    } 

    } 

    public static void main(String[] args) { 
     List<String> urls = new ArrayList<String>(); 

    for (int i = 0; i < 10000; i++) { 
     urls.add("http://www.google.com/" + System.currentTimeMillis()); 
     urls.add("http://www.yahoo.com/" + System.currentTimeMillis()); 
     urls.add("http://www.bing.com/" + System.currentTimeMillis()); 
    } 


    long startTime = System.currentTimeMillis(); 
    for (int i = 0; i < 300; i++) { 
     GetUrlsIn(5, urls, true); 
    } 
    long endTime = System.currentTimeMillis(); 
    System.out.println("elapsed time with checks at every iteration: " + (endTime-startTime) + "(ms)"); 

    startTime = System.currentTimeMillis(); 
    for (int i = 0; i < 300; i++) { 
     GetUrlsIn(5, urls, false); 
    } 
    endTime = System.currentTimeMillis(); 
    System.out.println("elapsed time with deletion at the end: " + (endTime-startTime) + "(ms)"); 
    } 
} 
+1

No distribuya sus propios puntos de referencia y utilice [el arnés micro de benchmarking propio de OpenJDK (jmh)] (http://openjdk.java.net/projects/code-tools/jmh/). Calentará tu código y evitará las trampas más problemáticas. –

1

Como se ha mencionado conjunto de herramientas, en Java 8 ahora tenemos Collectors. Esto es lo que el código se vería así:

String joined = array.stream().map(Object::toString).collect(Collectors.joining(", ")); 

Creo que hace exactamente lo que está buscando, y es un patrón que se podría utilizar para muchas otras cosas.

0

Dado que es una matriz fija, sería más fácil simplemente evitar la mejorada para ... Si el Objeto es una colección, un iterador sería más fácil.

int nums[] = getNumbersArray(); 
StringBuilder builder = new StringBuilder(); 

// non enhanced version 
for(int i = 0; i < nums.length; i++){ 
    builder.append(nums[i]); 
    if(i < nums.length - 1){ 
     builder.append(","); 
    } 
} 

//using iterator 
Iterator<int> numIter = Arrays.asList(nums).iterator(); 

while(numIter.hasNext()){ 
    int num = numIter.next(); 
    builder.append(num); 
    if(numIter.hasNext()){ 
     builder.append(","); 
    } 
} 
Cuestiones relacionadas