繁体   English   中英

上移,下移,指定位置

[英]Move up, move down, specify position

我知道这似乎很基本,但是由于某种原因,我只是无法解决解决该问题的算法,所以我将就这个问题与社区联系。

我有一堂课,看起来像这样:

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字段。

有人对此问题有好的解决方案吗? 谢谢您的帮助!

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

但是,如果您确实希望使用ArrayList实现,那么是的,您是正确的。 您最有可能想到的问题是,您必须遍历需要更改的项目,并用索引i-1的项目换出索引i的项目。 执行此操作时,将创建许多临时对象以处理每次交换。

您的代码看起来很笨拙,而且速度很慢(假设您要进行大量交换),但这是使用ArrayList的代价。 好处是您可以直接通过索引i访问项目,而不必从根对象开始遍历LinkedList中的i项目。

您说:“列表是根据MapElementModel.position排序的”,因此我的答案将基于此。 如果根据MapElementModel.id进行排序,则将是另一种算法。

ArrayList视为项目的有序集合。 而不是“存储” MapElementModel中的位置,只需让ArrayList元素的索引为其位置即可。 例如,如果您的ArrayList具有元素["Red", "Blue", "Green", "Yellow", "Pink", "Purple"] ,则“ Red”的位置为0,而“ Blue”的位置“为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中,我们要将“ Yellow”移动到位置5。请注意,列表中有六个元素,而位置5对应于第六个位置(如果数组索引从0开始),因此“ Yellow”将转到列表的末尾,在“紫色”之后。 因此,我们再次删除“黄色”:

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

为什么不实现通用的东西? 在这种情况下,我将使用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));
    }

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

            }
        }           

    }
}

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

  1. List ,依靠哪个list元素包含对象
  2. MapElementModel本身中,在position字段中

您应该考虑摆脱其中一种,以避免保持它们同步。 MapElementModel是否真的需要知道它在包含它的列表中的位置?


如果确实必须同时保留两者,则需要:

  • 从列表中删除元素
  • 将元素插入新位置
  • 用新位置更新元素的position字段
  • 更新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);
  }

这并不是特别有效,但这就是您选择的数据结构的局限性。

您想要的是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));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM