如何将新元素插入有序列表并在Java中将所有其他对象向后移动?

huangapple go评论70阅读模式
英文:

How to insert a new element into a ordered list and shift all other objects in Java?

问题

假设我有这个简单的类:

public class User implements Comparable<User> {

  private String name;
  private Integer order;

  public User(String name, Integer order) {
    this.name = name;
    this.order = order;
  }

  //...省略了Getter和Setter方法

  @Override
  public int compareTo(User user) {
    return this.order.compareTo(user.getOrder());
  }
}

只有两个属性,用户的姓名和顺序。属性order将用于确定用户在列表中的位置。然后,我将创建一个无序的LinkedList()

private static List<User> unorderedList() {
    final List<User> users = new LinkedList<>();

    users.add(new User("Joe", 5));
    users.add(new User("John", 2));
    users.add(new User("Maria", 8));
    users.add(new User("Kevin", 7));
    users.add(new User("Sophia", 9));
    users.add(new User("James", 1));
    users.add(new User("Adrian", 3));

    return users;
}

现在,我需要创建并插入一个新的User到我的列表中:

User u = new User("David", 2);

但是,在调用add()方法之前,我需要做一些事情。

我必须对这个列表进行排序(这就是为什么我在User类上实现了Comparable接口),并且我必须更新属性order为顺序。因此,在将新的用户插入列表之前,我需要得到以下最终结果:

1 - James    // 顺序不变
2 - David    // 插入新用户
3 - John     // 原先是顺序2,变成了3
4 - Adrian   // 原先是顺序3,变成了4
5 - Joe      // 顺序不变
6 - Kevin    // 原先没有6。Kevin原先是7,所以变成了6
7 - Maria    // 原先是8
8 - Sophia   // 原先是9

编辑 1:我尝试过的方法

我尝试循环遍历列表,并将列表中的用户顺序与新用户的顺序进行比较。新用户在列表中具有优先位置。

final List<User> unorderedUsers = MainApp.unorderedList();
final AtomicInteger currentOrder = new AtomicInteger(1);
final User newUser = new User("David", 2);
Collections.sort(unorderedUsers);

unorderedUsers.forEach(user -> {
    if (user.getOrder().equals(newUser.getOrder())) {
        user.setOrder(user.getOrder() + 1);
    } else {
        user.setOrder(currentOrder.get());
    }

    currentOrder.incrementAndGet();
});

unorderedUsers.add(newUser);
Collections.sort(unorderedUsers);
System.out.println(String.format("Ordered with new User List -> %s", unorderedUsers));

然而,新用户的顺序重复了。我仍在尝试解决如何创建新的连续顺序。我的代码结果是:

'James', order=1
'David', order=2
'John', order=3
'Adrian', order=3
'Joe', order=4
'Kevin', order=5
'Maria', order=6
'Sophia', order=7

编辑 2 - 可能的解决方案

我不确定这个解决方案是否覆盖了所有情况,但我将其分为两个步骤。我知道这可能有点耗费资源,但这是我得到的方法。

private void insertNewUser(final List<User> usersList, final User newUser) {
  final AtomicBoolean canShift = new AtomicBoolean(false);
  usersList.forEach(user ->{
    if (user.getOrder().equals(newUser.getOrder()) || canShift.get()) {
      user.shiftOrder();
      canShift.set(true);
    }
  });

  usersList.add(newUser);
  Collections.sort(usersList);
}

private void organizeOrder(final List<User> usersList) {
  final AtomicInteger currentOrder = new AtomicInteger(1);
  usersList.forEach(user -> user.setOrder(currentOrder.getAndIncrement()));
}

然后只需调用:

this.insertNewUser(...)
this.organizeOrder(...)
英文:

Suppose I have this simple class:

public class User implements Comparable&lt;User&gt; {

  private String name;
  private Integer order;

  public User(String name, Integer order) {
    this.name = name;
    this.order = order;
  }

  //...Getters and Setters ommited
  
  @Override
  public int compareTo(User user) {
    return this.order.compareTo(user.getOrder());
  }
}

Just two attributes, the name of the user and the order. This attribute order will be used to know the position of the user inside a list. Then, I will create an unordered LinkedList() of them:

private static List&lt;User&gt; unorderedList() {
    final List&lt;User&gt; users = new LinkedList&lt;&gt;();
    
    users.add(new User(&quot;Joe&quot;, 5));
    users.add(new User(&quot;John&quot;, 2));
    users.add(new User(&quot;Maria&quot;, 8));
    users.add(new User(&quot;Kevin&quot;, 7));
    users.add(new User(&quot;Sophia&quot;, 9));
    users.add(new User(&quot;James&quot;, 1));
    users.add(new User(&quot;Adrian&quot;, 3));
    
    return users;
}

Now, I need to create and insert a new User in my list.

User u = new User(&quot;David&quot;, 2);

But, before I call the add() method, I have to do somethings.

I have to order this list (that's why I implemented Comparable on User class) and I have to update the attribute order to be sequential. So when I insert my new User in List, I need to have a final result like this:

1 - James    // No Change
2 - David    // New user inserted
3 - John     // Was order 2, become 3
4 - Adrian   // Was order 3, become 4
5 - Joe      // No Change
6 - Kevin    // There was no 6. Kevin was 7 so he become 6
7 - Maria    // Was 8
8 - Sophia - // Was 9

EDIT 1: What I tried

I tried to loop the list and compare the users order in list with new users order. The newUser have priority in position inside list.

final List&lt;User&gt; unorderedUsers = MainApp.unorderedList();
final AtomicInteger currentOrder = new AtomicInteger(1);
final User newUser = new User(&quot;David&quot;, 2);
Collections.sort(unorderedUsers);

unorderedUsers.forEach(user -&gt; {
    if (user.getOrder().equals(newUser.getOrder())) {
        user.setOrder(user.getOrder() + 1);
    } else {
        user.setOrder(currentOrder.get());
    }

    currentOrder.incrementAndGet();
});

unorderedUsers.add(newUser);
Collections.sort(unorderedUsers);
System.out.println(String.format(&quot;Ordered with new User List -&gt; %s&quot;, unorderedUsers));

However, the new user order is getting duplicated. I'm still trying to solve how to do a new sequential order. The result of my code is:

&#39;James&#39;, order=1
&#39;David&#39;, order=2
&#39;John&#39;, order=3
&#39;Adrian&#39;, order=3
&#39;Joe&#39;, order=4
&#39;Kevin&#39;, order=5
&#39;Maria&#39;, order=6
&#39;Sophia&#39;, order=7

EDIT 2 - Possible Found Solution
I don't know if this solution covers all scenarios, but I separate in two steps. I know it's a bit expensive, but it's what I got.

private void insertNewUser(final List &lt;User&gt; usersList, final User newUser) {
  final AtomicBoolean canShift = new AtomicBoolean(false);
  usersList.forEach(user -&gt;{
    if (user.getOrder().equals(newUser.getOrder()) || canShift.get()) {
      user.shiftOrder();
      canShift.set(true);
    }
  });

  usersList.add(newUser);
  Collections.sort(usersList);
}

private void organizeOrder(final List &lt;User&gt; usersList) {
  final AtomicInteger currentOrder = new AtomicInteger(1);
  usersList.forEach(user -&gt; user.setOrder(currentOrder.getAndIncrement()));
}

Then just call:

this.insertNewUser(...)
this.organizeOrder(...)

答案1

得分: 1

你没有更新那些订单高于新用户订单(2)的用户。排序不会更新你的对象值。你可以尝试一下。

Collections.sort(users);
users.add(newUser.getOrder() - 1, newUser);
AtomicInteger index = new AtomicInteger(1);
users.forEach(
    user -> user.setOrder(index.getAndIncrement())
);
英文:

You are not updating Users having order higher than order (2) of new user. Sorting does not update your objects value. You can try that.

Collections.sort(users);
users.add( newUser.getOrder()-1 , newUser );
AtomicInteger index = new AtomicInteger(1);
users.forEach(
   user -&gt;  user.setOrder( index.getAndIncrement() )
);

答案2

得分: 1

你不需要实现Comparable接口。你可以使用Comparator进行排序:

users.addFirst(newUser);
// 我们在这里使用addFirst,因为我们希望确保新用户出现在可能具有相同顺序的用户之前

AtomicInteger inc = new AtomicInteger();
users.stream()
    .sorted(Comparator.comparing(User::getOrder))
    .map(user -> {
        user.setOrder(inc.incrementAndGet());
        return user;
    })
    .collect(Collectors.toList();

在上面的代码中,发生了以下情况:首先,创建了一个AtomicInteger,它帮助我们为用户提供新的顺序号。然后,我们按照User的序列号对流进行排序。然后,我们从AtomicInteger获取一个新的序列号,并将order属性设置为该数字。最后,我们将其收集到一个列表中。


addFirst() 确保将新用户添加到列表的开头。这是因为在最终排序的列表中,我们希望新用户出现在具有相同顺序的任何其他用户之前。由于排序是*稳定排序*,具有相等序列号的元素以与未排序流相同的顺序出现。

作为替代,你可以使用以下方法代替addFirst()

newUser.setOrder(newUser.getOrder() - 1);
users.add(newUser);
英文:

You don't have to implement the Comparable interface. You can sort using a Comparator:

users.addFirst(newUser);
// We&#39;re using addFirst here, because we want to make sure
// that the new user comes BEFORE possible users with the
// same order

AtomicInteger inc = new AtomicInteger();
users.stream()
    .sorted(Comparator.comparing(User::getOrder))
    .map(user -&gt; {
        user.setOrder(inc.incrementAndGet());
        return user;
    })
    .collect(Collectors.toList();

In the above code, this is what happens: first, an AtomicInteger is created, which helps us to provide the users with new order numbers. Then, we sort the stream by the sequence number of the User. Then, we get a new sequence number from the AtomicInteger, and we set the order property with this number. Finally, we collect this into a list.


addFirst() makes sure that the new user is added at the beginning of the list. This is because in the finally sorted list, we want the new user to appear before any other user with the same order. Because the sorting is a stable sort, elements with equal sequence numbers appear in the same order as the unsorted stream.

As an alternative, instead of addFirst(), you could use

newUser.setOrder(newUser.getOrder() - 1);
users.add(newUser);

instead.

huangapple
  • 本文由 发表于 2020年10月1日 04:04:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/64144936.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定