Чи можна зробити а для кожного циклу в java у зворотному порядку?


148

Мені потрібно провести список у зворотному порядку за допомогою Java.

Тож, де це робиться вперед:

for(String string: stringList){
//...do something
}

Чи є спосіб ітерації stringList у зворотному порядку, використовуючи для кожного синтаксису?

Для наочності: я знаю, як скласти список у зворотному порядку, але хотів би знати (заради цікавості), як це зробити для кожного стилю.


5
Суть циклу "за кожного" полягає в тому, що вам просто потрібно виконати операцію над кожним елементом, і порядок не важливий. Бо кожен міг обробляти елементи в абсолютно випадковому порядку, і він все одно буде робити те, для чого призначений. Якщо вам потрібно обробити елементи певним чином, я б запропонував це зробити вручну.
muusbolla

Бібліотека колекцій Java. Насправді немає нічого спільного з мовою. Виною Джоша Блоха.
Том Хотін - тайклін

7
@muusbolla: Але як Список - це упорядкована колекція, напевно, її порядок буде шанований, незалежно? Тому для-кожен не буде обробляти елементи Списку у випадковому порядку.
Лі Ковальковський

5
@muusbolla це неправда. Можливо, у випадку Setпохідних колекцій. foreachгарантує ітерацію в порядку ітератора, повернутого з iterator()методу колекції. docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

Відповіді:


151

Метод Collections.reverse фактично повертає новий список з елементами оригінального списку, скопійованими в нього у зворотному порядку, тому це має O (n) продуктивність щодо розміру вихідного списку.

Як більш ефективне рішення, ви можете написати декоратор, який представляє зворотний вигляд Списку як Ітерабельного. Ітератор, повернений вашим декоратором, використовує ListIterator оформленого списку для переходу по елементах у зворотному порядку.

Наприклад:

public class Reversed<T> implements Iterable<T> {
    private final List<T> original;

    public Reversed(List<T> original) {
        this.original = original;
    }

    public Iterator<T> iterator() {
        final ListIterator<T> i = original.listIterator(original.size());

        return new Iterator<T>() {
            public boolean hasNext() { return i.hasPrevious(); }
            public T next() { return i.previous(); }
            public void remove() { i.remove(); }
        };
    }

    public static <T> Reversed<T> reversed(List<T> original) {
        return new Reversed<T>(original);
    }
}

І ви б використовували це так:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}

22
Це в основному те, що робить Iterables.reverse від Google, так :)
Джон Скіт

10
Я знаю, що є "правило", що ми повинні прийняти відповідь Йона :), але .. Я хочу прийняти цю (хоча вони по суті однакові), тому що це не вимагає від мене включення іншої сторонньої бібліотеки (навіть якщо дехто може стверджувати, що ця причина порушує одну з головних переваг ОО - повторне використання).
Рон Туффін

Невелика помилка: у public void remove () не повинно бути твердження про повернення, воно повинно бути просто: i.remove ();
Jesper

10
Collections.reverse () НЕ повертає поверненої копії, але діє у Переданому їй Переліку як параметр. Як і ваше рішення з ітератором. Дійсно елегантний.
er4z0r

96

Для переліку ви можете скористатися бібліотекою Google Guava :

for (String item : Lists.reverse(stringList))
{
    // ...
}

Зауважте, що це не такLists.reverse перетворює всю колекцію чи щось подібне - вона просто дозволяє ітерацію та випадковий доступ у зворотному порядку. Це ефективніше, ніж спочатку скасувати колекцію.

Щоб скасувати довільну ітерабельну мережу, вам доведеться прочитати все це, а потім "перетворити" назад.

(Якщо ви не використовуєте його, я ретельно рекомендую вам поглянути на гуави . Це відмінний матеріал.)


1
У нашій кодовій базі широко використовується узагальнена версія Commons Collection, випущена larvalabs ( larvalabs.com/collections ). Переглядаючи репортаж SVN для Apache Commons, зрозуміло, що більша частина роботи над випуском java 5 версії Commons Collections зроблена, вони просто її ще не випустили.
скафман

Мені це подобається. Якби це було не так корисно, я б назвав цю пробку.
geowa4

Цікаво, чому Джакарта ніколи не намагався оновлювати Apache Commons.
Урі

3
Вони оновили це, ось що я говорю. Вони просто не випустили його.
skaffman

23
Iterables.reverse застаріло, замість цього використовуйте Lists.reverse або ImmutableList.reverse.
Гарретт Холл

39

Список (на відміну від Набору) - це упорядкована колекція, і повторення над нею зберігає порядок за контрактом. Я б очікував, що Стек повториться у зворотному порядку, але, на жаль, це не так. Тож найпростішим рішенням, про який я можу придумати, є таке:

for (int i = stack.size() - 1; i >= 0; i--) {
    System.out.println(stack.get(i));
}

Я усвідомлюю, що це не циклічне рішення "для кожного". Я вважаю за краще використовувати цикл for, ніж представляти нову бібліотеку на зразок Google Collections.

Collections.reverse () також виконує цю роботу, але він оновлює список на відміну від повернення копії у зворотному порядку.


3
Цей підхід може бути чудовим для списків, що базуються на масиві (наприклад, ArrayList), але він буде неоптимальним для пов'язаних списків, оскільки кожне отримання має пройти список від початку до кінця (або, можливо, від кінця до початку) для кожного отримання. Краще використовувати розумніший ітератор, як у рішенні Nat (оптимально для всіх реалізацій List).
Кріс

1
Далі він відхиляється від запиту в ОП, який прямо запитує for eachсинтаксис
Пол W

8

Це зіпсується з оригінальним списком, а також його потрібно викликати поза циклом. Крім того, ви не хочете виконувати реверс кожного разу, коли ви циклуєте - чи це правда, якщо один із Iterables.reverse ideasзастосованих?

Collections.reverse(stringList);

for(String string: stringList){
//...do something
}

5

AFAIK в стандартній бібліотеці не існує такого типу "reverse_iterator", який підтримує синтаксис "для кожного", який уже є синтаксичним цукром, який вони заносили пізно в мову.

Ви можете зробити щось на кшталт (елемент елемента: myList.clone (). Зворотний ()) і заплатити відповідну ціну.

Це також здається досить узгоджуваним із очевидним явищем, що не дає вам зручних способів робити дорогі операції - оскільки список, за визначенням, може мати складність O (N) випадкового доступу (ви можете реалізувати інтерфейс з односмуговою ланкою), зворотний ітерація може закінчитися O (N ^ 2). Звичайно, якщо у вас є ArrayList, ви не платите цю ціну.


Ви можете запустити ListIterator назад, який можна загорнути в Iterator.
Том Хотін - тайклін

@Tom: Добре. Однак, за допомогою ітератора ви все ще робите набридливий старий стиль для циклів, і ви все одно можете заплатити витрати, щоб дістатись до останнього елемента для початку ... Я додав кваліфікатора у своїй відповіді, хоча, дякую.
Урі

У Deque є зворотний ітератор.
Майкл Мансі

2

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

public static void main(String[] args) {        
    List<String> a = new ArrayList<String>();
    a.add("1");a.add("2");a.add("3");a.add("4");a.add("5");

    ListIterator<String> aIter=a.listIterator();        
    while(aIter.hasNext()) aIter.next();

    for (;aIter.hasPrevious();)
    {
        String aVal = aIter.previous();
        System.out.println(aVal);           
    }
}


1

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

Ви повинні мати можливість це зробити на Java, створивши власну реалізацію Iterable, яка поверне елементи у зворотному порядку.

Тоді ви будете інстанціювати обгортку (або викликати метод, що-у вас є), який би повернув Iterable реалізацію, яка перевертає елемент у для кожного циклу.



1

Якщо вам потрібно використати для кожного синтаксису поза полем, перейдіть у зворотному порядку.


1

Усі відповіді, наведені вище, виконують лише вимогу, будь то іншим способом або за допомогою зовнішнього коду;

Ось рішення, скопійоване з 4-го видання Thinking in Java , глава 11.13.1 AdapterMethodIdiom ;

Ось код:

// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
package holding;
import java.util.*;

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
  public ReversibleArrayList(Collection<T> c) { super(c); }
  public Iterable<T> reversed() {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return new Iterator<T>() {
          int current = size() - 1; //why this.size() or super.size() wrong?
          public boolean hasNext() { return current > -1; }
          public T next() { return get(current--); }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }
}   

public class AdapterMethodIdiom {
  public static void main(String[] args) {
    ReversibleArrayList<String> ral =
      new ReversibleArrayList<String>(
        Arrays.asList("To be or not to be".split(" ")));
    // Grabs the ordinary iterator via iterator():
    for(String s : ral)
      System.out.print(s + " ");
    System.out.println();
    // Hand it the Iterable of your choice
    for(String s : ral.reversed())
      System.out.print(s + " ");
  }
} /* Output:
To be or not to be
be to not or be To
*///:~

чому int current = size() - 1право? чому б не int current = this.size() - 1orint current = super.size() - 1
qiwen li

1

Робота навколо:

Collections.reverse(stringList).forEach(str -> ...);

Або з гуавою :

Lists.reverse(stringList).forEach(str -> ...);

0

Однозначно пізня відповідь на це питання. Однією з можливостей є використання ListIterator в циклі for. Це не так чисто, як синтаксис двокрапки, але це працює.

List<String> exampleList = new ArrayList<>();
exampleList.add("One");
exampleList.add("Two");
exampleList.add("Three");

//Forward iteration
for (String currentString : exampleList) {
    System.out.println(currentString); 
}

//Reverse iteration
for (ListIterator<String> itr = exampleList.listIterator(exampleList.size()); itr.hasPrevious(); /*no-op*/ ) {
    String currentString = itr.previous();
    System.out.println(currentString); 
}

Заслуга синтаксису ListIterator належить до "Способи перегляду списку на Java"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.