Переміщення елементів у списку ArrayList


77

Я бавився з ArrayLists. Я намагаюся досягти методу зробити щось подібне:

Item 1
Item 2
Item 3
Item 4

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

Item 1
Item 3
Item 2
Item 4

З мого незначного розуміння на даний момент, я хотів би щось подібне до:

IF arrayname index is not equal to 0
THEN move up
ELSE do nothing

Частина, з якою я борюся, - це частина "рухатися вгору". Будь-які поради та зразки коду, як цього можна досягти, дуже вдячні.

Відповіді:


131

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

Для обміну 2 елементами Collections.swap чудово підходить. Але якщо ми хочемо перемістити більше елементів, є краще рішення, яке передбачає творче використання Collections.sublist та Collections.rotate, про що я не думав, поки не побачив, як це описано тут:

http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#rotate%28java.util.List,%20int%29

Ось цитата, але перейдіть туди і також прочитайте все це для себе:

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

Collections.rotate(list.subList(j, k+1), -1);


3
У моєму додатку це обертання подспісок виявилося повільніше , ніж видалення / вставка-підхід , описаний тут: stackoverflow.com/a/4938696/1025391
moooeeeep

2
greater than or equal (>=)? про що <=?
користувач25,

68

Простий обмін набагато кращий для "переміщення чогось вгору" в ArrayList:

if(i > 0) {
    Item toMove = arrayList.get(i);
    arrayList.set(i, arrayList.get(i-1));
    arrayList.set(i-1, toMove);
}

Оскільки ArrayList використовує масив, якщо ви видалите елемент із ArrayList, він повинен «перекласти» всі елементи після цього елемента вгору, щоб заповнити пробіл у масиві. Якщо ви вставляєте елемент, він повинен перемістити всі елементи після цього елемента, щоб звільнити місце для його вставки. Ці зрушення можуть стати дуже дорогими, якщо ваш масив дуже великий. Оскільки ви знаєте, що в кінцевому підсумку ви хочете отримати однакову кількість елементів у списку, проведення такої заміни дозволяє дуже ефективно "перемістити" елемент в інше місце у списку.

Як зазначають Кріс Баклер та Міхал Кройцман, у класі Collections існує навіть зручний метод зменшити ці три рядки коду до одного:

Collections.swap(arrayList, i, i-1);

Це чудово, collection.swap здається ідеальним. Одна невелика проблема, яку я помітив, полягає в тому, що використання цієї щось у верхній частині списку викликає виняток за межі закону - він все ще працює так, як я хотів, але чи є спосіб зупинити його, викидаючи виняток за межі обмежень?
user319940

1
@ user319940 Привіт StriplingWarrior показав це у першому зразку коду. Індекс я повинен бути більше 0if(i > 0)
michal.kreuzman

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

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

1
@Javid Jamae: Це правда, що це не так просто, якщо вам потрібно перемістити елемент більше, ніж на один пробіл. Однак це все-таки набагато ефективніше, ніж видалення та повторне додавання елемента. Якщо ви багато рухаєтесь по предметах, я точно рекомендую LinkedList.
StriplingWarrior

31

ви можете спробувати цей простий код, Collections.swap (list, i, j) - це те, що ви шукаєте.

    List<String> list = new ArrayList<String>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");

    String toMoveUp = "3";
    while (list.indexOf(toMoveUp) != 0) {
        int i = list.indexOf(toMoveUp);
        Collections.swap(list, i, i - 1);
    }

    System.out.println(list);

25

Щоб рухатися вгору, видаліть, а потім додайте.

Щоб видалити - ArrayList.remove і призначити повернутий об'єкт змінній, а
потім додати цей об'єкт назад за необхідним індексом -ArrayList.add(int index, E element)

http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html#add(int , E)


2
Це єдине рішення, яке насправді працює, щоб змінити порядок елементів у ArrayList. Дякую!
mpemburn

2
Дійсно дуже елегантно!
geekQ

1
з видаленням він не рухається, він змінює положення двох об’єктів (поміняти місцями), рухається - переміщує один об’єкт між деякими іншими двома об’єктами
25

10

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

public static <T> void moveItem(int sourceIndex, int targetIndex, List<T> list) {
    if (sourceIndex <= targetIndex) {
        Collections.rotate(list.subList(sourceIndex, targetIndex + 1), -1);
    } else {
        Collections.rotate(list.subList(targetIndex, sourceIndex + 1), 1);
    }
}

5

Застосування рекурсії для впорядкування елементів у списку записів

public class ArrayListUtils {
            public static <T> void reArrange(List<T> list,int from, int to){
                if(from != to){
                     if(from > to)
                        reArrange(list,from -1, to);
                      else
                        reArrange(list,from +1, to);

                     Collections.swap(list, from, to);
                }
            }
    }

5

До Moveпункту у списку просто додайте:

// move item to index 0
Object object = ObjectList.get(index);
ObjectList.remove(index);
ObjectList.add(0,object);

До Swapдвох елементів у списку просто додайте:

// swap item 10 with 20
Collections.swap(ObjectList,10,20);

ObjectList.remove (index) повертає видалений об'єкт, тому ви можете позбутися рядка раніше. Просто зробіть це Object object = Objectlist.remove (index);
ДжошуаД

0

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

import java.util.List;

public class ListMoveUtil
{
    enum Position
    {
        BEFORE, AFTER
    };

    /**
     * Moves element `elementToMove` to be just before or just after `targetElement`.
     *
     * @param list
     * @param elementToMove
     * @param targetElement
     * @param pos
     */
    public static <T> void moveElementTo( List<T> list, T elementToMove, T targetElement, Position pos )
    {
        if ( elementToMove.equals( targetElement ) )
        {
            return;
        }
        int srcIndex = list.indexOf( elementToMove );
        int targetIndex = list.indexOf( targetElement );
        if ( srcIndex < 0 )
        {
            throw new IllegalArgumentException( "Element: " + elementToMove + " not in the list!" );
        }
        if ( targetIndex < 0 )
        {
            throw new IllegalArgumentException( "Element: " + targetElement + " not in the list!" );
        }
        list.remove( elementToMove );

        // if the element to move is after the targetelement in the list, just remove it
        // else the element to move is before the targetelement. When we removed it, the targetindex should be decreased by one
        if ( srcIndex < targetIndex )
        {
            targetIndex -= 1;
        }
        switch ( pos )
        {
            case AFTER:
                list.add( targetIndex + 1, elementToMove );
                break;
            case BEFORE:
                list.add( targetIndex, elementToMove );
                break;
        }
    }

0

Рішення Kotlin для переміщення елемента з "fromPos" в "toPos" і відповідного зсуву всіх інших елементів (корисно для переміщення елемента в шаховому макеті recyclerView в додатку Android)

            if(fromPos < toPos){                    
                val movingElement = myList[fromPos]
                //shifts all elements between fromPos and toPos 1 down
                for(i in fromPos+1..toPos){
                    myList[i-1] = myList[i]
                }
                //re-add element that was saved in the beginning
                myList[toPos] = movingElement
            }

            if(fromPos > toPos){
                val movingElement = myList[fromPos]
                //shifts elements between toPos and fromPos 1 up
                for(i in fromPos downTo toPos+1){
                    myList[i] = myList[i-1]
                }                    
                //re-add element that was saved in the beginning
                myList[toPos] = movingElement
            }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.