当前位置: 首页 > 知识库问答 >
问题:

迭代/循环时添加HashMap和ArrayList

刘英彦
2023-03-14

我有一个游戏,每隔X秒它就会把内存中改变的值写回我的数据库。当编辑这些值保存的数据时,它们存储在容器(HashMaps和ArrayList)中。

为了简单起见,假设我只有一个容器可以写入数据库:

html" target="_blank">public static HashMap<String, String> dbEntitiesDeletesBacklog = new HashMap<String, String>();

我的DB写入循环:

Timer dbUpdateJob = new Timer();
dbUpdateJob.schedule(new TimerTask() {
    public void run() {
        long startTime = System.nanoTime();
        boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
        if (!updateEntitiesTableSuccess){
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
                logger.fatal(e.getMessage());
                System.exit(1);
            }
        } else { //everything saved to DB - commit time
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
                logger.fatal(e.getMessage());
                System.exit(1);
            }
        }
        logger.debug("Time to save to DB: " + (System.nanoTime() - startTime) / 1000000 + " milliseconds");
    }
}, 0, 10000); //TODO:: figure out the perfect saving delay

我的更新方法:

private boolean UpdateEntitiesTable() {
    Iterator<Entry<String, String>> it = dbEntitiesDeletesBacklog.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, String> pairs = it.next();
        String tmpEntityId = pairs.getKey();

        int deletedSuccess = UPDATE("DELETE" + 
                " FROM " + DB_NAME + ".entities" + 
                " WHERE entity_id=(?)", new String[]{tmpEntityId});
        if (deletedSuccess != 1) {
            logger.error("Entity " + tmpEntityId + " was unable to be deleted.");
            return false;
        }
        it.remove();
        dbEntitiesDeletesBacklog.remove(tmpEntityId);
    }

我是否需要在为dbEntitiesDeletesBacklog HashMap和本摘录中未包含的其他容器“保存到DB”时创建某种锁定机制?我想我需要这样做,因为它会创建迭代器,然后循环。如果在创建迭代器之后,在循环遍历条目之前添加了什么呢。很抱歉,这更多的是一个过程问题,而不是一个代码帮助问题(因为我包含了这么多示例代码),但我想确保很容易理解我试图做什么和问什么。

同样的问题也适用于我使用的其他容器:

public static ArrayList<String> dbCharacterDeletesBacklog =  new ArrayList<String>();

private boolean DeleteCharactersFromDB() {
    for (String deleteWho : dbCharacterDeletesBacklog){
        int deleteSuccess = MyDBSyncher.UPDATE("DELETE FROM " + DB_NAME + ".characters" +
                " WHERE name=(?)", 
                new String[]{deleteWho});

        if (deleteSuccess != 1) {
            logger.error("Character(deleteSuccess): " + deleteSuccess);
            return false;
        }
    }
    dbCharacterDeletesBacklog.clear();
    return true;
}

一如既往,非常感谢您在这方面的帮助。非常感谢!!

共有2个答案

岳英锐
2023-03-14

以下是我将使用的示例。我在这里发帖,希望它能帮助其他人解决类似的问题。

public class MyDBSyncher {

    public static boolean running = false;
    public static HashMap<String, String> dbEntitiesInsertsBacklog_A = new HashMap<String, String>();
    public static HashMap<String, String> dbEntitiesInsertsBacklog_B = new HashMap<String, String>();

    public MyDBSyncher(){
        Timer dbUpdateJob = new Timer();
        dbUpdateJob.schedule(new TimerTask() {
            public void run() {
                running = true;
                boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
                running = false;
            }
        }, 0, 10000); //TODO:: figure out the perfect saving delay
    }

    public HashMap getInsertableEntitiesHashMap(){
        if (running){
            return dbEntitiesInsertsBacklog_B;
        } else {
            return dbEntitiesInsertsBacklog_A;
        }
    }

    private boolean UpdateEntitiesTable() {
        Iterator<Entry<String, String>> it2 = getInsertableEntitiesHashMap().entrySet().iterator();
        while (it2.hasNext()) {
            Entry<String, String> pairs = it2.next();
            String tmpEntityId = pairs.getKey();

            //some DB updates here

            it2.remove();
            getInsertableEntitiesHashMap().remove(tmpEntityId);
        }
        return true;
    }
}
胡新
2023-03-14

如果同时访问地图,至少需要一个同步地图(通过Collections.synchronizedMap),否则可能会遇到不确定性行为。

除此之外,正如您所建议的,您还需要在迭代期间锁定地图。来自集合的javadoc。synchronizedMap()建议如下:

当用户迭代其任何集合视图时,必须手动同步返回的映射:

Map m = Collections.synchronizedMap(new HashMap());
      ...
Set s = m.keySet();  // Needn't be in synchronized block
      ...
synchronized(m) {  // Synchronizing on m, not s!
    Iterator i = s.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

不遵循此建议可能会导致不确定性行为。

或者,使用ConexttHashMap而不是常规的HashMap,以避免在迭代期间需要同步。对于游戏来说,这可能是一个更好的选择,因为你可以避免长时间锁定你的收藏。

可能更好的方法是,考虑旋转新集合,这样每次更新数据库时,都可以获取集合,并将其替换为一个新的空集合,所有新更新都将写入其中,从而避免在数据库写入时锁定集合。在这种情况下,集合将由某个容器管理,以允许此抓取和替换是线程安全的。

 类似资料:
  • 我有一个使用迭代器循环通过HashMap的程序,在循环中,我将添加到HashMap-这将导致一个并发修改异常。我已经看到ListIterator有一个add()函数来处理这个问题,但是Iterator没有。 HashMap是这样设置的- 还有像这样的迭代器- 对象(不是真实的名字)是一个类从我的程序。有人知道如何在循环或任何其他选项时添加到迭代器吗?

  • 本节我们探索的主题是迭代与循环。循环通常在计算机编程用用于自动执行重复性任务。 在Python中最常用的迭代形式就是for循环了。for循环允许你迭代出列表中所有的项,迭代出来后你可以做任何你想做的事情。 比如,我们创建了一个列表,并打印出其中所有元素的平方。 >>> for value in [0, 1, 2, 3, 4, 5]: ... print(value * value) ... 0 1

  • 问题内容: 我想知道为什么在 i 变量下面的代码中 仍然显示“ 5” 而不是显示 “ 1” 然后 “ 2” 然后 “ 3” ,依此类推?必须是一个范围问题,但由于在全局和dom范围中更改了i变量的范围,但仍然遇到相同的问题,因此我并没有真正了解它。当我提醒 我 的AJAX功能外,它工作得很好。 这是小提琴 编辑: 我选择了 @Tushar Gupta 解决方案,因为它最适合我的需要,但是又遇到了另

  • 2.3 循环与遍历 程序的比人肉强大的另一个特性就是可以任劳任怨地重复地做些单调无聊(或有聊)的 工作。本节介绍在 VimL 语言中,如何控制程序,命令其循环地按规则干活。 遍历集合变量 首先介绍的是如何依次访问列表如字典内的所有元素,毕竟在 2.1 节介绍的索引方法只 适于偶尔访问查看某个具体的元素。这里要用到的是for ... in 语法。 例如遍历列表: : let list = [0, 1

  • 迭代器无效是如何处理的,而不是循环? 例如,这段代码不起作用,因为迭代器在插入后无效: 但是,如果我用这个for循环替换while循环,它会正确编译和运行: 为什么for循环有效而while循环无效?

  • 问题内容: 此代码将导致无限循环的机会是什么? 实际上,这会导致无限循环。我的怀疑是因为我没有服用,是真的吗? 问题答案: 是。除非您不打电话,否则它将永远不会继续进行下一项。Beause 将返回您已在列表/集中添加的对象。