我有一个服务器,当客户端加入并将其放入ArrayList(类型为EchoThread)时,它会创建一个新的Thread。它工作得很好 - 为每个人创建一个新的JLabel并立即在我的游戏中更新他们的位置,但是如果一个人离开(这不是arraylist中的最后一个客户端)那么我希望服务器移动所有的客户端(那个高于留下的客户的索引。我尝试过很多东西,有些是部分工作的。如果有人离开,这是方法,其中i等于离开的客户的索引:
public static void removeClient(int i) throws Exception {
}
我尝试了一些方法,例如使用for循环将arrayList中的每个EchoThread向下移动一个,但它们似乎不起作用:
ArrayList:
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);
}
我还尝试创建一个临时的arraylist,它会添加客户端arraylist中的所有元素,除了索引i,然后将客户端arraylist设置为等于临时arraylist,但它仍然失败。
有人说我可以使用链表,但我不知道怎么做?
编辑:
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);
}
}
答案 0 :(得分:1)
你试过clients.remove(i);
这是ArrayList#remove
的实施。如您所见,它使用System.arraycopy来向下滑动列表的尾部,以便您拥有一个不间断的列表。
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
答案 1 :(得分:1)
API已使用remove
方法为您执行此操作。
取自文档:
public E remove(int index)
删除此列表中指定位置的元素。将任何后续元素向左移位(从索引中减去一个)。
答案 2 :(得分:1)
您必须确保以线程安全的方式调用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。
通常你可以在任何修改ArrayList的方法上添加synchronized
modifier,它提供互斥(防止多个线程一次修改它并破坏它)和发生在之前调用这些方法之间的关系(确保线程看到彼此对列表的更改)。但是,这在这里还不够,因为您也在使用该方法来修改Swing组件。除少数例外情况外,必须仅从单个event dispatch thread访问Swing组件,因此如果要更新帧并且您位于不同的线程上,则必须先切换到事件派发线程。
在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-loop而且它不会工作。
也许您的问题是虽然列表确实将项目向下移动,但您已经为这些项目提供了自己的索引字段,因此在删除项目后,其自己的索引字段不再与其在列表中的实际位置相匹配?您可以迭代列表中的项目并重置每个项目的索引字段以匹配其实际位置:
for (int i = 0; i < clients.size(); i++)
clients.get(i).index = i;
(假设您的EchoThread对象上有一些字段index
)。
但是,这可能不是最好的方法。您不一定需要来跟踪索引,因为您可以在不知道索引的情况下完全remove the object itself:
EchoThread client = ...;
clients.remove(client);
如何更新其他列表clientnames
,clientpos
?好吧,你可以通过在第一个列表上调用indexOf
来找出索引。但那很难看。你不应该真的拥有所有这些单独的列表。您最好应该有一个类,它封装了单个客户端的各种不同字段,包括&#34; name&#34;和&#34; pos&#34;字段,并列出这些对象。你已经有了EchoClient对象,所以也许你可以将字段移动到那个。