向上移动,向下移动,指定位置

时间:2015-01-12 16:22:16

标签: java list sorting collections

我知道这看起来应该是基本的,但出于某种原因,我无法绕过解决这个问题的算法,所以我要在这个问题上与社区联系。

我有一个看起来像这样的课程:

class MapElementModel {
    int id;
    String name;
    int position;
    //other fields ommitted for space reasons

    //getters and setters
}

现在我将MapElementModel存储在标准ArrayList中。用户想要在列表中向上移动元素,在列表中向下移动元素,或者为MapElementModel指定新位置。该列表基于MapElementModel.position。

进行排序

所以基本上,如果用户将项目14移动到位置3,该项目需要插入位置3,则位置字段需要更改为3,并且所有后续位置都需要相应更改。

同样,用户可以点击"向上移动"按钮并将位置9处的项目移动到位置8.同样,所有剩余的MapElementModel.position项目都需要相应地更改其位置字段。

最后,用户可以点击"向下移动"按钮并在列表中向下移动项目。同样,必须更新所有MapElementModel.position字段。

有没有人有这个问题的好方法?谢谢你的帮助!

6 个答案:

答案 0 :(得分:3)

最简单的解决方案是放弃使用ArrayList来存储它们。相反,请考虑使用LinkedList。当您想要交换对象并通过从邻居到邻居遍历列表时,链表是一种非常好的数据结构。删除对象,添加对象或交换两个对象会自动更新所有"索引"因为你不再拥有索引,而只是与根项目保持距离。

但是,如果你想继续使用ArrayList实现,那么是的,你是对的。您最有可能遇到的问题是,您必须循环遍历必须更改的项目,并将索引i处的项目替换为索引i-1处的项目。执行此操作时,您将创建许多临时对象以处理每个交换。

你的代码看起来很笨重而且会很慢(假设你正在进行大量的交换),但这是你使用ArrayList的代价。好处是您可以通过索引i直接访问项目,而无需从根对象开始循环链接列表中的i项。

答案 1 :(得分:3)

你说,“列表是根据MapElementModel.position排序的”,所以我的回答将基于此。如果它是基于MapElementModel.id排序的,那么这将是一个不同的算法。

ArrayList视为您订购的商品集合。不要将位置“存储”在MapElementModel中,只需让ArrayList中元素的索引成为它的位置即可。例如,如果您的ArrayList具有元素["Red", "Blue", "Green", "Yellow", "Pink", "Purple"],则“红色”的位置为0,“蓝色”的位置为1,“绿色”的位置为2,因此上...

我假设你并不关心效率 - 即你没有处理10亿件物品的清单。

现在,要移动项目,我们的算法只是将其从列表中删除并再次将其插入正确的位置。假设我们再次列出了这个列表:

["Red", "Blue", "Green", "Yellow", "Pink", "Purple"]

让我们考虑一些测试用例:

  • 将位置3的项目移至位置0
  • 将位置3的项目移至位置5
  • 将位置3的项目移动到位置3(多余的情况)

在第一种情况下,我们将“黄色”移动到“红色”前面。所以删除像这样的“黄色”

String removed = list.remove( 3 );

现在我们想把它插回到位置0.看起来我们可以做到

list.add( 0 , removed );

简单,对吧?删除给定索引处的元素,将其插入所需索引。让我们看看它是否适用于第二个测试用例。

在第2种情况下,我们要将“黄色”移动到位置5.请注意,列表中有6个元素,位置5对应于第6个位置(如果我们的数组索引从0开始),那么“黄色”在“紫色”之后会进入列表的末尾。所以,我们再次删除“黄色”:

String removed = list.remove( 3 );

但现在看,黄色之后的所有东西都向下移动了1:

["Red, "Blue", "Green", "Pink", "Purple"]

方便地,“Purple”的索引是4,如果我们用

插入位置5
list.add( 5 , removed );

我们得到了

["Red, "Blue", "Green", "Pink", "Purple"]

查看此算法是否适用于将黄色置于位置3(冗余的情况)。

看起来我们的算法如下工作:在给定位置移除,插入目标位置。看起来我们可以编写这样的算法:

public void moveItem( int idxToMove , int targetIdx ) {
    String removed = list.remove( idxToMove );
    list.add( targetIdx , removed );
}

如果用户想要将位置3上的项目向上移动1,请调用

moveItem( 3 , 3+1 );

如果用户想要将位置3中的项目向下移动到列表中的1个位置,则调用

moveItem( 3 , 3-1 );

如果用户想要将位置0的项目移动到列表中的1个位置,您会怎么做?

如果用户想要将位置5的项目移动到位置2的项目,则调用

moveItem( 5 , 2 );

现在,您可以为MapElementModel的ArrayList实现此算法。如果您确实需要MapElementModel对象的position字段是正确的,那么您只需通过ArrayList并更新它。 ArrayList中元素的位置是元素的位置。

public void moveItem( int idxToMove , int targetIdx ) {
    //move the item as I did with Strings

    for ( int i=0 ; i<list.size() ; i++ ) {
        list.get( i ).position = i;
    }
}

如果您需要移动具有指定ID的项目,您可以在ArrayList中找到它然后移动它:

public void moveItemById( int itemId , int newPosition ) {
    int positionOfItem = -1;
    for ( int i=0 ; i<list.size() ; i++ ) {
        if ( list.get( i ).id == itemId ) {
            positionOfItem = i;
            break;
        }
    }
    if ( positionOfItem != -1 ) {
        moveItem( positionOfItem , newPosition );
    }
}

答案 2 :(得分:0)

为什么不实现 generic ?在这种情况下,我将使用LinkedList,但您可以改为扩展ArrayList。

public class MovableList<T> extends LinkedList<T> {

    public MovableList() {
        super();
    }

    public MovableList(Collection<T> c) {
        super(c);
    }

    /** Returns removed elements */
    public Collection<T> remove(int[] indexes) {
        Collection<T> removeList=new ArrayList(indexes.length);
        for(int index: indexes){
            removeList.add(get(index));
        }
        removeAll(removeList);
        return removeList;
    }

    /** Returns inserted elements */
    public Collection<T> insert(List<T> from, int[] indexes, int position) {
        Collection<T> insertList=new ArrayList(indexes.length);
        Arrays.sort(indexes);
        for(int index: indexes){
            insertList.add(from.get(index));
        }
        addAll(position, insertList);
        return insertList;
    }

    public void moveTo(int[] indexes, int position) {
        Arrays.sort(indexes);
        addAll(position, remove(indexes));
    }

}

答案 3 :(得分:0)

public void shift(List<String> currentList, String element, int places) {
    int fromPos = -1;
    int toPos = 0; 

    if (places != 0) {

        //Creates a new list
        List<String> newList = new ArrayList<>();

        // get curren position
        fromPos = currentList.indexOf(element);

        if (fromPos >= 0) {
            // If was found, calculates new position
            toPos = fromPos - places;               

            if (toPos >= 0 && toPos < currentList.size()) {
                // new position is in range

                if (places > 0) {
                    //move forward
                    if (toPos == 0) {
                        // move at the head
                        newList.offer(element);
                        newList.addAll(currentList.subList(0, fromPos));
                        newList.addAll(currentList.subList(fromPos + 1, currentList.size()));
                    } else {
                        // some places forward
                        newList.addAll(currentList.subList(0, toPos));
                        newList.offer(element);
                        newList.addAll(currentList.subList(toPos, fromPos));
                        newList.addAll(currentList.subList(fromPos + 1, currentList.size()));
                    }                       
                } else {
                    //move backward
                    if (toPos == (currentList.size() - 1)) {
                        // move at the end of the list
                        newList.addAll(currentList.subList(0, fromPos));
                        newList.addAll(currentList.subList(fromPos + 1, currentList.size()));
                        newList.offer(element);
                    } else {
                        // move some places backward
                        newList.addAll(currentList.subList(0, fromPos));
                        newList.addAll(currentList.subList(fromPos + 1, toPos + 1));
                        newList.offer(element);
                        newList.addAll(currentList.subList(toPos + 1, currentList.size()));
                    }                       
                }

                // replace old list
                currentList = newList;                      
                newList = null;                 

            }
        }           

    }
}

答案 4 :(得分:0)

您真正的问题是您有冗余信息需要保持同步。 “位置”存储在两个地方:

  1. List中,凭借哪个列表元素包含对象
  2. MapElementModel本身的position字段
  3. 你应该考虑摆脱其中的一个,以避免让它们保持同步。 MapElementModel是否真的需要知道自己在包含它的列表中的位置?

    如果你真的必须同时保留两者,你将需要:

    • 从列表中删除元素
    • 将元素插入新位置
    • 使用新位置
    • 更新元素的position字段
    • 更新位置也已更改的所有元素的position字段

    所以:

      // remove the element from the list
      oldPosition = element.getPosition();
      list.remove(oldPosition);
    
      // insert the element in its new position
      list.add(newPosition, element);
    
      // the elements with new positions are those with positions
      // greater than or equal to the new position or the old position,
      // whichever is lower
    
      for(int i = Math.min(newPosition,oldPosition); i<list.size(); i++) {
           list.get(i).setPosition(i);
      }
    

    这不是特别有效,但这是您选择的数据结构的限制。

答案 5 :(得分:0)

你想要的是ArrayList中非常昂贵的操作。它需要将列表开头和C位置之间的每个元素向下移动一个。

但是,如果你真的想这样做:

int index = url.indexOf(itemToMove);
url.remove(index);
url.add(0, itemToMove);

如果您经常这样做,并且随机访问频率较低,您可以考虑切换到另一个List实施,例如LinkedList。如果您对元素的顺序如此关注,您还应该考虑列表是否是正确的数据结构。

另一方面,如果它只是移动一行

向上:

int index = items.indexOf(myObject);
Collections.swap(items, index, Math.max (0, index - 1));

Donwn:

int index = items.indexOf(myObject);
Collections.swap(items, index, Math.min (items.size () - 1, index + 1));