簡體   English   中英

將高於特定索引的ArrayList中的元素向下移動

[英]Move Elements In An ArrayList Down One That Are Higher Than Specific Index

我有一個服務器,當客戶機加入時,該服務器創建一個新線程並將其放入ArrayList(類型為EchoThread)中。 它的工作原理非常好-為每一個創建一個新的JLabel並立即更新它們在我的游戲中的位置,但是,如果一個離開(不是arraylist中的最后一個客戶端),那么我希望服務器移動所有客戶端(那個比剩下的客戶索引高1。 我嘗試了很多事情,有些工作了。 這是有人離開的方法,其中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);
    }

我還嘗試制作一個臨時數組列表,該數組將添加客戶端數組列表中除索引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);
    }
}

您是否嘗試過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;
}

API已經使用remove方法為您完成了此操作。

來自文檔:

公共E remove(int索引)

刪除此列表中指定位置的元素。 將所有后續元素向左移動(從其索引中減去一個)。

您必須確保以線程安全的方式調用removeClient。 我正在猜測,但是聽起來好像您是從每個EchoThread調用它的,沒有同步就不安全,因為(a) 線程可以踐踏彼此對列表的所有修改 (b) 無法保證線程甚至不會看到彼此對列表的修改

通常,您可以只在修改ArrayList的任何方法上添加synchronized修飾符 ,該方法既提供互斥(防止多個線程立即對其進行修改並破壞),又提供了對這些方法的調用之間的事前關系(確保線程看到對方對列表的更改)。 但是,這還不夠,因為您還使用該方法來修改Swing組件。 除少數例外,必須僅從單個事件分發線程訪問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);一樣簡單clients.remove(i); ,就像其他答復者所說的那樣。 您不需要額外的for循環,它將無法正常工作。

也許您的問題是,盡管列表確實將項目下移,但是您已為這些項目提供了自己的索引字段,因此在刪除項目后,其自身的索引字段不再與列表中的實際位置匹配? 您可以迭代列表中的項目,並重置每個項目的索引字段以匹配其實際位置:

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

(假設您在EchoThread對象上有一些字段index )。

但是,這可能不是最佳方法。 你不一定需要保持指數的跟蹤,因為你可以非常清楚刪除對象本身不知道指數:

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

如何更新您的其他列表, clientnamesclientpos 好了,您可以通過在第一個列表上調用indexOf來找到索引。 但這很丑。 您不應該真正擁有所有這些單獨的列表。 您最好具有一個類,該類封裝單個客戶端的各種不同字段,包括“名稱”和“ pos”字段,並列出這些對象。 您已經有了EchoClient對象,因此也許您可以將字段移至該對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM