gpt4 book ai didi

java - 将 ArrayList 中高于特定索引的元素向下移动一个

转载 作者:行者123 更新时间:2023-12-01 22:35:43 25 4
gpt4 key购买 nike

我有一个服务器,当客户端加入时,它会创建一个新线程并将其放入 ArrayList(EchoThread 类型)中。它工作得很好 - 为每个客户端创建一个新的 JLabel 并几乎立即更新它们在我的游戏中的位置,但是如果一个客户端离开(这不是数组列表中的最后一个客户端),那么我希望服务器移动所有客户端(即比离开的客户的索引高 1。我尝试了很多事情,有些部分有效。这是如果有人离开的方法,其中 i 等于离开的客户端的索引:

public static void removeClient(int i) throws Exception {

}

我尝试了一些方法,例如使用 for 循环将 arrayList 中的每个 EchoThread 向下移动 1,但它们似乎不起作用:

数组列表:

    static ArrayList<EchoThread> clients = new ArrayList<EchoThread>();

我尝试过的 for 循环:

    for(int ii = i+1; ii < clients.size()-1; ii++){
EchoThread e = clients.get(ii);
clients.set(ii-1, e);
}

我还尝试创建一个临时数组列表,获取添加的客户端数组列表中的所有元素(索引 i 除外),然后将客户端数组列表设置为等于临时数组列表,但仍然失败。

有人说我可以使用链表,但我不知道如何?

编辑:

p = a JPanel
f = a JFrame
clients = an arrayList<EchoThread>
clientinfo = a JLabel in EchoThread class
clientname = an arraylist of strings
clientpos = an arraylist of the positions of each client
info = a JLabel to show number of clients that are connected

方法:

public static void removeClient(int i) throws Exception {
p.remove(clients.get(i).clientinfo);
p.revalidate();
f.validate();
f.repaint();
clients.get(i).clientinfo.setVisible(false);
clients.remove(i);
clientnames.remove(i);
clientpos.remove(i);
info.setText("Clients Connected: " + clients.size());

for(int ii = i+1; ii < clients.size()-1; ii++){
EchoThread e = clients.get(ii);
clients.set(ii-1, e);
}
}

最佳答案

您必须确保以线程安全的方式调用removeClient。我猜,但听起来也许你是从每个 EchoThread 调用它,如果没有同步,这是不安全的,因为 (a) the threads can trample all over each other's modifications to the list ;和(b)there is no guarantee threads will even see each other's modifications to the list .

通常您只需添加 synchronized modifier在任何修改 ArrayList 的方法上,它提供了互斥(防止多个线程同时修改它并破坏它)和对这些方法的调用之间的发生之前关系(确保线程看到每个其他人对列表的更改)。然而,这在这里还不够,因为您还使用该方法来修改 Swing 组件。除了少数异常(exception),Swing 组件只能从单个 event dispatch thread 访问。 ,所以如果你想更新框架并且你在不同的线程上,你必须首先切换到事件调度线程。

在 Java 8+ 中,按如下方式修改 removeClient 方法的开头:

public static void removeClient(int i) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> removeClient(i));
return;
}

// ...

在旧版本的 Java 中:

public static void removeClient(final int i) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
removeClient(i);
}
});
return;
}

// ...

一旦您进入正确的线程,从列表中删除该项目并将其他项目向下移动列表实际上就像调用 clients.remove(i); 一样简单,就像其他回答者一样讲过了。您不需要额外的 for 循环,而且它不会工作。

也许您的问题是,虽然列表确实将项目向下移动,但您已经为这些项目提供了自己的索引字段,因此在删除项目后,其自己的索引字段不再与其在列表中的实际位置匹配?您可以迭代列表中的项目并重置每个项目的索引字段以匹配其实际位置:

for (int i = 0; i < clients.size(); i++)
clients.get(i).index = i;

(假设您的 EchoThread 对象上有一些字段 index)。

但是,这可能不是最好的方法。您不一定需要跟踪索引,因为您可以很好地 remove the object itself不知道索引:

EchoThread client = ...;
clients.remove(client);

如何更新您的其他列表、clientnamesclientpos?那么,你可以通过调用indexOf来找到索引。在第一个列表上。但这很丑陋。您实际上不应该拥有所有这些单独的列表。您最好有一个类来封装单个客户端的各种不同字段,包括“name”和“pos”字段,并列出这些对象。您已经有了 EchoClient 对象,因此也许您可以将字段移至该对象。

关于java - 将 ArrayList 中高于特定索引的元素向下移动一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26897223/

25 4 0