我知道这看起来应该是基本的,但出于某种原因,我无法绕过解决这个问题的算法,所以我要在这个问题上与社区联系。
我有一个看起来像这样的课程:
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字段。
有没有人有这个问题的好方法?谢谢你的帮助!
答案 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"]
让我们考虑一些测试用例:
在第一种情况下,我们将“黄色”移动到“红色”前面。所以删除像这样的“黄色”
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,如果我们用
插入位置5list.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)
您真正的问题是您有冗余信息需要保持同步。 “位置”存储在两个地方:
List
中,凭借哪个列表元素包含对象MapElementModel
本身的position
字段你应该考虑摆脱其中的一个,以避免让它们保持同步。 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));