Hashset作为可变副本传递

时间:2014-04-04 21:52:00

标签: java performance optimization collections

我正在尝试使用set作为缓冲区来保存一些值,将它们存储在地图中,然后重置自己以准备下一个数据流。以下是一些相关代码:

Map<Set<String>, Integer> map = new LinkedHashMap<Set<String>, Integer>();
        Set<String> set = new LinkedHashSet<String>();

        set.add("A");
        map.put(set, 1);

        for (Set<String> entry : map.keySet()) {
            for (String word : entry) {
                System.out.println("BEFORE CLEAR - "+word);
            }
        }

        set.clear();

        for (Set<String> entry : map.keySet()) {
            System.out.println("AFTER CLEAR - "+entry.size());
            for (String word : entry) {
                System.out.println("AFTER CLEAR - "+word);
            }
        }

我得到的输出是:

BEFORE CLEAR - A
AFTER CLEAR - 0

如果我将set.clear()更改为set = null,则输出为:

BEFORE CLEAR - A
AFTER CLEAR - 1
AFTER CLEAR - A

这意味着当我清除设置中的数据后将其放入地图时,地图不会收到新的副本 - 这就是为什么在清除集合时,地图中的副本也会被清除。 我对这种行为感到惊讶,因为作为Java开发人员,我们被迫使用不可变副本,这似乎在这里被违反了。我误解了什么吗?

我的问题是,这个问题有更好的解决方案吗?它适用于将set设置为null,我必须在while循环中每次使用新的LinkedHashSet()重新初始化。这对我来说很糟糕。

编辑:我没有设置设置为null的问题,我当然希望地图中的数据。我只是在创建set的新对象时遇到问题,而不是使用相同的对象。

2 个答案:

答案 0 :(得分:2)

如果你制作set = null,你将删除对一个集合的本地引用,但你的地图仍将保留对设置对象的引用。要从map中删除set,您需要map.remove(set) Java在将其作为参数传递时不进行深度集复制,唯一复制的是对象的引用。如果您希望您的设置保存所有数据,那么只是不清除它 - 当您需要另一个空数据时创建新设置。当你写一些像

这样的东西时,想想java中的对象就像被占用的内存一样
Set<String> set = new LinkedHashSet<String>();

第一部分Set<String> set是一个引用(类似于C中的指针)到那段内存,第二部分new LinkedHashSet<String>();实际上是分配这些内存。如果你清除那块内存中的东西(set.clear())你会删除写在那里的东西。如果您需要新的内存来编写新数据而不删除旧数据,则需要再次调用new LinkedHashSet<String>();

答案 1 :(得分:0)

首先,你是正确,而且有时麻烦,但Java中的Collections 确实可变

其次,我认为您确实需要复制Set的内容,将该内容放入Map,而不是将引用添加到Set map.put(set, 1); 1}},如果你想稍后清除它。因此,我建议你简单地替换:

map.put(new HashSet<>(set), 1);

由:

Set

在我看来,这很有道理,因为:

  1. 这是复制内容唯一临时Set
  2. 的最简单方法
  3. 您不必使用任何其他新{{1}}来履行第一个角色,您可以按照预期重复使用它。