执行列表操作时出现异常

Exception when I do a list operation

我关注class

我正在监控队列。当一条消息入队时,我记录它,当一条消息出队时,我记录它。此代码适用于 75% correct.Sometimes 文件入队太多,但这不是主要问题案例。

现在让我们看一下下面的代码,首先我与队列建立连接,然后在我对队列进行排队时将消息 ID 添加到我的列表中。如果列表大于队列大小(我每次循环时都会计算),则需要进行出队。帮助列表首先复制数组列表并删除它在队列中遇到的所有项目。它没有遇到的项目来自队列,因此它们需要从列表中取出。如果列表不是更大,我会检查该 id 是否已经在列表中,如果不是,我会创建一个新队列。

package queueFeed;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;

import javax.jms.Message;
import javax.jms.QueueBrowser;
import javax.jms.Session;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;

public class QueueRunner {
    private static Logger log = Logger.getLogger(QueueRunner.class.getName());



    public void run() throws Exception {

        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
        javax.jms.Connection connection =  factory.createConnection();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        QueueBrowser browser = session.createBrowser(new ActiveMQQueue("KBC"));

        List<String> ids = new ArrayList<>();
        int queueSize = 0;
        int counter = 0;
        connection.start();
        while(true) {

        Enumeration enumeration = browser.getEnumeration();
            if(queueSize < ids.size())
            {
                List<String> checkList = ids;
                while(enumeration.hasMoreElements()){
                    Message message = (Message) enumeration.nextElement();
                    checkList.remove(message.getJMSMessageID());
                    counter++;
                }

                if(checkList.size() > 0 && ids.size() > 0)
                for(String notEncountered : checkList) {
                    ids.remove(notEncountered);
                    System.out.println("dequeued " + notEncountered);
                }
                queueSize = counter;
                counter = 0;
            }
            else {

                while(enumeration.hasMoreElements()){
                    counter++;
                Message message = (Message) enumeration.nextElement();
                String id = message.getJMSMessageID();
                if (!ids.contains(id)) {
                    ids.add(id);
                    System.out.println("enqueued message" + message.getJMSMessageID());
                    Thread.sleep(1000);
                }
            }
                queueSize = counter;
        counter = 0;
    }
}}}

当我出队时出现以下错误:

 Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at queueFeed.QueueRunner.run(QueueRunner.java:43)
    at dashboard.Main.main(Main.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

错误点在第 43 行,也就是 for(String notEncountered : helpList) 行...怎么了?

您无法使用基于范围的循环删除项目。使用迭代器。

Iterator<String> it = ids.iterator();
while(it.hasNext){
    if(checkList.contains(it.next()) it.remove();
}

而不是

for(String notEncountered : checkList) {
                    ids.remove(notEncountered);
                    System.out.println("dequeued " + notEncountered);
                }

如果您想在循环中修改列表,您要么必须

创建一个临时列表,您 运行 超过:

List<Integer> tmpList = copyOf(myList);

for(int n : tmpList){
    if(myList.contains(n) && SomethingIsTrue()){
        myList.remove(n);
    }
}

或者您必须 运行 向后浏览您的列表

for(int i = myList.size()-1; i >= 0; i--){
    if(SomethingIsTrue()){
        myList.remove(i);
    }
}

注意这是伪代码(有一些检查方法等)

运行 遍历 List 并对其进行修改,您可以在删除其他项目时更改其项目的索引。这就是为什么你得到 ConcurrentModificationException

在你的例子中你说:

List<String> checkList = ids;

所以checkListids是相等的。如果您从 ids 中删除某些内容,但 运行 覆盖 checkList,您仍然 运行 覆盖同一对象。您必须创建 ids 列表的副本才能获得临时行为。

在普通的 for 循环中,不可能从列表中删除项目。 您需要使用 Iterator 来执行此操作。

你用

替换你的普通 for 循环
if(checkList.size() > 0 && ids.size() > 0)
                {
                    Iterator<String> iterator = checkList.iterator();
                    while(iterator.hasNext()) {
                        String messageId = iterator.next();
                        System.out.println("dequeued message "+ messageId);
                        iterator.remove();
                    }
                }

这应该可以解决您的问题。