Остання ітерація посиленого циклу в java


140

Чи є спосіб визначити, чи петля повторюється останній раз. Мій код виглядає приблизно так:

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

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

Тепер справа в тому, що я не хочу додавати кому в останній ітерації. Тепер є спосіб визначити, чи це остання ітерація, чи я застряг з циклом for або використовую зовнішній лічильник для відстеження.


1
Так! Це смішно, я просто хотів задати точно таке ж питання!
PhiLho

Такий же питання повертається (і так буде). Тепер чому б ви хотіли створити цикл, якщо одному елементу потрібна інша обробка? stackoverflow.com/questions/156650 / ...
xtofl

Оскільки у вас є фіксований масив, для чого слід використовувати розширений? for (int i = 0; i <array.length; i ++, якщо (i <array.lenth),,,
AnthonyJClink

Відповіді:


223

Інша альтернатива - додавати кому до того, як ви додасте i, тільки не на першій ітерації. ( "" + iДо речі, не використовуйте , до речі, ви тут не дуже хочете конкатенації, а StringBuilder має ідеально хороший додаток (int) перевантаження.)

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

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

Приємно в тому, що він буде працювати з будь-яким Iterable- ви не завжди можете індексувати речі. ("Додайте кому, а потім видаліть її в кінці" - це приємна пропозиція, коли ви дійсно використовуєте StringBuilder - але це не працює для таких речей, як запис у потоки. Однак, можливо, це найкращий підхід для цієї точної проблеми. )


2
Хороший шаблон, але builder.length ()! = 0 крихкий - уявіть, що щось додається (умовно!) До буфера перед циклом. Замість цього використовуйте isFirstбулівський прапор. Бонус: теж швидше.
Джейсон Коен

2
@Jason: Я, звичайно, використовую шаблон "isFirst", де будівельник не буде порожнім під час першої ітерації. Однак, коли це не потрібно, це додає неабиякої кількості реалізованості в моєму досвіді.
Джон Скіт

3
@Liverpool (тощо): 1000 зайвих перевірок дуже, дуже малоймовірно, не матимуть істотного впливу на продуктивність. Я можу так само зазначити, що додатковий символ доданого рішення Діна може спричинити розширення StringBuilder, подвоївши розмір остаточного рядка на (продовження)
Джон Скіт,

2
зайвий буферний простір. Однак важливим моментом є читабельність. Мені здається, моя версія є більш читаною, ніж версія Діни. Якщо ви почуваєтесь навпаки, це добре. Я б лише розглядав вплив на продуктивність непотрібних "ifs" після того, як знайшов вузьке місце.
Джон Скіт

3
Крім того, моє рішення є більш загальноприйнятим, оскільки воно покладається лише на вміння писати. Ви можете прийняти моє рішення та змінити його на запис у потік тощо - там, де ви, можливо, не зможете потім повернути зайві дані. Мені подобаються візерунки, які зазвичай застосовуються.
Джон Скіт

145

Ще один спосіб зробити це:

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

Оновлення: для Java 8 тепер у вас є колектори


1
Довелося видалити мою подібну відповідь - це навчить мене не публікувати, перш ніж уважніше подивитися на інші відповіді.
Майкл Берр

1
Також зауважте, що це обробляє потенційну проблему, про яку Роберт Поулсон згадується в іншій темі коментарів - ця методика не залежить від того, що StringBuilder буде порожнім при додаванні значень масиву.
Майкл Берр

1
Хоча код є більш гладким / акуратним / веселим, він менш очевидний, ніж версія if. Завжди використовуйте більш очевидну / читану версію.
Білл К

8
@Bill: Це не чудово, як жорстке і швидке правило; бувають випадки, коли «розумне» рішення є «правильнішим» рішенням. Більш суттєво, чи справді ви, що цю версію важко читати? Так чи інакше, потребує технічного обслуговування, щоб перейти через це - я не думаю, що різниця є істотною.
Грег Кейс

Це працює, хоча ви пишете на інші ресурси, як-от письмові дзвінки файлової системи. Гарна ідея.
Tuxman

39

Завжди може бути легше додавати. А потім, коли ви закінчите з циклом, просто видаліть остаточний символ. Тон менше умовних, що теж.

Ви можете використовувати StringBuilder's deleteCharAt(int index)з індексомlength() - 1


5
найефективніше рішення цієї проблеми. +1.
Справжній червоний.

9
Можливо, це я, але мені дуже не подобається те, що ви видаляєте щось, що ви тільки що додали ...
Fortega

2
Фортега: Мені не подобається кожен раз перевіряти щось, що я зроблю в 99% разів. Здається більш логічним (і це швидше) застосувати рішення Діни або сблунді.
Буффало

1
На мою думку, найелегантніше рішення. Дякую!
Чарльз Морін

32

Можливо, ви використовуєте неправильний інструмент для роботи.

Це більш посібник, ніж те, що ви робите, але це набагато елегантніше, якщо не трохи "стара школа"

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


14

Ще одне рішення (можливо, найефективніше)

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

Це природне рішення, за винятком того, що масив не буде порожнім.
orcmid

5
@orcmid: Якщо масив порожній, то це все одно дає правильний вихід - порожній рядок. Я не впевнений, у чому твій погляд.
Едді

7

будьте простим і використовуйте стандарт для циклу:

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

або просто використовувати apache commons-lang StringUtils.join ()


6

Явні петлі завжди працюють краще, ніж неявні.

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

Ви повинні обернути все це в операторі if на випадок, якщо ви маєте справу з масивом нульової довжини.


Мені подобається такий підхід, оскільки він не потребує тестування кожної ітерації. Єдине, що я хотів би додати, це те, що будівельник може додавати цілі числа безпосередньо, тому немає необхідності використовувати "" + int, просто додавати (масив [0]).
Джош

Вам потрібен ще один, якщо навколо всього партії, щоб у масиві був принаймні один елемент.
Том Лейс

2
чому всі продовжують використовувати приклади зі строковим конкатенацією ВНУТЬ додавання? "," + x компілюється в новий StringBuilder (",") .append (x) .toString () ...
Джон Гарднер

@Josh та @John: Просто наслідуючи приклад n00b. Не хочу вводити занадто багато речей відразу. Однак ваша думка дуже гарна.
С.Лотт

@Tom: Правильно. Я думаю, я це вже сказав. Не потрібно редагувати.
С.Лотт

4

Якщо ви перетворите його в класичний цикл індексу, так.

Або ви можете просто видалити останню кому після її виконання. Так:

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

Мене просто використовую StringUtils.join()з commons-lang .


3

Вам потрібен роздільник класів .

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

Реалізація класу Separatorйде прямо. Він обертає рядок, який повертається при кожному дзвінку, toString()крім першого дзвінка, який повертає порожню рядок.


3

На основі java.util.Ab абстрактCollection.toString () він виходить рано, щоб уникнути роздільника.

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

Це ефективно та елегантно, але не так само собою зрозуміло, як деякі інші відповіді.


Ви забули включити раннє повернення, коли (! i.hasNext())це важлива частина надійності загального підходу. (Інші рішення тут обробляють порожні колекції витончено, тому і ваші повинні! :)
Майк Кларк

3

Ось рішення:

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

Шукайте першу ітерацію :)  


3

Як зазначалося в інструментарії, у Java 8 зараз є колектори . Ось як виглядатиме код:

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

Я думаю, що це саме те, що ви шукаєте, і це шаблон, який ви можете використовувати для багатьох інших речей.


1

Ще один варіант.

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


1

Багато описаних тут рішень трохи перевершують ІМХО, особливо ті, які покладаються на зовнішні бібліотеки. Існує приємна чиста, чітка ідіома для досягнення списку, розділеного комами, який я завжди використовував. Він покладається на умовного (?) Оператора:

Редагувати : оригінальне рішення правильне, але неоптимальне відповідно до коментарів. Спробуйте вдруге:

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

Там ви переходите в чотири рядки коду, включаючи оголошення масиву та StringBuilder.


Але ви створюєте новий StringBuilder на кожній ітерації (завдяки оператору +).
Майкл Майерс

Так, якщо ви заглянете в байт-код, ви маєте рацію. Я не можу повірити, що таке просте запитання може стати таким складним. Цікаво, чи може тут компілятор оптимізувати.
Жульєн Частанг

За умови 2-го, сподіваємось, кращого рішення.
Жульєн Частанг

1

Ось показник SSCCE, який я застосував (пов'язаний з тим, що мені довелося реалізувати), з цими результатами:

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

На моєму прикладі , по крайней мере, пропустивши перевірку на кожній ітерації непомітно швидше , особливо для розсудливих обсягів даних, але це швидше.

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
Будь ласка, не скочуйте власні орієнтири та використовуйте власні мікроспрямовані джгути OpenJDK (jmh) . Це зігріє ваш код і уникне найпроблемніших підводних каменів.
Рене

0

Інший підхід полягає в тому, щоб довжина масиву (за наявності) зберігалася в окремій змінній (ефективніше, ніж щоразу перевіряти довжину). Потім ви можете порівняти свій індекс із цією довжиною, щоб визначити, чи слід додавати остаточну кому чи ні.

РЕДАКТУВАННЯ: Інший розгляд - зважування продуктивності вилучення остаточного символу (що може спричинити копію рядка) проти того, щоб умовна перевірка була під час кожної ітерації.


Видалення остаточного символу не повинно створювати копію рядка. Методи видалення / видаленняCharAt StringBuffer в кінцевому підсумку викликають наступний метод всередині масивів ідентичного джерела та призначення масиву System:> Система -> загальнодоступна статична масова анульована масив (Object src, int srcPos, dest Object, int destPos, int length);
Буффало

0

Якщо ви лише перетворюєте масив у масив з комою, багато мов мають функцію приєднання саме для цього. Він перетворює масив у рядок з роздільником між кожним елементом.


0

У цьому випадку насправді не потрібно знати, чи це останнє повторення. Є багато способів вирішити це. Один із способів:

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

0

Тут є два альтернативних шляхи:

1: Apache Commons String Utils

2: Тримайте булевий виклик first, встановлений на істину. У кожній ітерації firstдодайте кому , якщо вона помилкова; після цього встановити firstзначення false.


1) Посилання мертве 2) Мені знадобилося більше часу, щоб прочитати опис, а не код :)
Буффало

0

Оскільки його фіксований масив, було б простіше просто уникнути розширеного для ... Якщо Об'єкт - це колекція, ітератор буде простішим.

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(",");
   }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.