JAVA:为什么我对Hashmap中特定键的ArrayList值的更新会更改所有其他键值?

时间:2018-07-31 11:04:57

标签: java arraylist hashmap

我需要您的帮助,因为我不知道为什么我在哈希图中更新值(针对特定键)会更改几乎所有其他键的值... 您可以尝试以下相应的代码:

import edu.duke.*;
import java.util.*;
import java.io.*;

public class WordsInFiles {
    private HashMap<String, ArrayList> map;

    public WordsInFiles(){
        map = new HashMap<String, ArrayList>();
    }

    private void addWordsFromFile(File f) {
        FileResource fr = new FileResource(f);
        String fileName = f.getName();
        ArrayList<String> newWord = new ArrayList<String>();
        for (String s : fr.words()){
            if (!map.containsKey(s)) {
                newWord.clear();
                newWord.add(fileName);
                map.put(s, newWord);
                System.out.println("New:" + s + "\t" + newWord);
            } else {
                ArrayList<String> currArr = map.get(s);
                if (!currArr.contains(fileName)){
                    currArr.add(fileName);
                    newWord = currArr;
                    System.out.println("Update:" +s + "\t" + newWord);
                    map.put(s, newWord);
                }
            }
            System.out.println(map + "\n");
        }
    }

    public void tester() {
        System.out.println("\n ****** New Tester Instance ****** ");
        buildWordFileMap();
    }
}

通过在4个文件上运行测试器:brief1.txt,brief2.txt,brief3.txt和brief4.txt分别包含以下字符串:

  1. 猫很有趣又可爱
  2. 狗很傻
  3. 爱动物的猫和狗
  4. 爱鸟和猫

我得到:

 ****** New Tester Instance ****** 
New:cats    [brief1.txt]
{cats=[brief1.txt]}

New:are [brief1.txt]
{cats=[brief1.txt], are=[brief1.txt]}

New:funny   [brief1.txt]
{cats=[brief1.txt], are=[brief1.txt], funny=[brief1.txt]}

New:and [brief1.txt]
{cats=[brief1.txt], are=[brief1.txt], and=[brief1.txt], funny=[brief1.txt]}

New:cute    [brief1.txt]
{cats=[brief1.txt], are=[brief1.txt], and=[brief1.txt], cute=[brief1.txt], funny=[brief1.txt]}

New:dogs    [brief2.txt]
{cats=[brief1.txt], are=[brief1.txt], and=[brief1.txt], dogs=[brief2.txt], cute=[brief1.txt], funny=[brief1.txt]}

Update:are  [brief1.txt, brief2.txt]
{cats=[brief1.txt, brief2.txt], are=[brief1.txt, brief2.txt], and=[brief1.txt, brief2.txt], dogs=[brief2.txt], cute=[brief1.txt, brief2.txt], funny=[brief1.txt, brief2.txt]}

New:silly   [brief2.txt]
{cats=[brief2.txt], are=[brief2.txt], and=[brief2.txt], silly=[brief2.txt], dogs=[brief2.txt], cute=[brief2.txt], funny=[brief2.txt]}

New:love    [brief3.txt]
{love=[brief3.txt], cats=[brief2.txt], are=[brief2.txt], and=[brief2.txt], silly=[brief2.txt], dogs=[brief2.txt], cute=[brief2.txt], funny=[brief2.txt]}

New:animals [brief3.txt]
{love=[brief3.txt], cats=[brief2.txt], are=[brief2.txt], and=[brief2.txt], silly=[brief2.txt], dogs=[brief2.txt], animals=[brief3.txt], cute=[brief2.txt], funny=[brief2.txt]}

Update:cats [brief2.txt, brief3.txt]
{love=[brief3.txt], cats=[brief2.txt, brief3.txt], are=[brief2.txt, brief3.txt], and=[brief2.txt, brief3.txt], silly=[brief2.txt, brief3.txt], dogs=[brief2.txt], animals=[brief3.txt], cute=[brief2.txt, brief3.txt], funny=[brief2.txt, brief3.txt]}

{love=[brief3.txt], cats=[brief2.txt, brief3.txt], are=[brief2.txt, brief3.txt], and=[brief2.txt, brief3.txt], silly=[brief2.txt, brief3.txt], dogs=[brief2.txt], animals=[brief3.txt], cute=[brief2.txt, brief3.txt], funny=[brief2.txt, brief3.txt]}

Update:dogs [brief2.txt, brief3.txt]
{love=[brief3.txt], cats=[brief2.txt, brief3.txt], are=[brief2.txt, brief3.txt], and=[brief2.txt, brief3.txt], silly=[brief2.txt, brief3.txt], dogs=[brief2.txt, brief3.txt], animals=[brief3.txt], cute=[brief2.txt, brief3.txt], funny=[brief2.txt, brief3.txt]}

Update:love [brief3.txt, brief4.txt]
{love=[brief3.txt, brief4.txt], cats=[brief2.txt, brief3.txt], are=[brief2.txt, brief3.txt], and=[brief2.txt, brief3.txt], silly=[brief2.txt, brief3.txt], dogs=[brief2.txt, brief3.txt], animals=[brief3.txt, brief4.txt], cute=[brief2.txt, brief3.txt], funny=[brief2.txt, brief3.txt]}

New:birds   [brief4.txt]
{love=[brief4.txt], cats=[brief2.txt, brief3.txt], are=[brief2.txt, brief3.txt], and=[brief2.txt, brief3.txt], silly=[brief2.txt, brief3.txt], dogs=[brief2.txt, brief3.txt], animals=[brief4.txt], birds=[brief4.txt], cute=[brief2.txt, brief3.txt], funny=[brief2.txt, brief3.txt]}

Update:and  [brief2.txt, brief3.txt, brief4.txt]
{love=[brief4.txt], cats=[brief2.txt, brief3.txt, brief4.txt], are=[brief2.txt, brief3.txt, brief4.txt], and=[brief2.txt, brief3.txt, brief4.txt], silly=[brief2.txt, brief3.txt, brief4.txt], dogs=[brief2.txt, brief3.txt], animals=[brief4.txt], birds=[brief4.txt], cute=[brief2.txt, brief3.txt, brief4.txt], funny=[brief2.txt, brief3.txt, brief4.txt]}

{love=[brief4.txt], cats=[brief2.txt, brief3.txt, brief4.txt], are=[brief2.txt, brief3.txt, brief4.txt], and=[brief2.txt, brief3.txt, brief4.txt], silly=[brief2.txt, brief3.txt, brief4.txt], dogs=[brief2.txt, brief3.txt], animals=[brief4.txt], birds=[brief4.txt], cute=[brief2.txt, brief3.txt, brief4.txt], funny=[brief2.txt, brief3.txt, brief4.txt]}

我真的不明白为什么在“更新:是[brief1.txt,brief2.txt]”行上,代码为此ArrayList值更新了map的所有键。即使它将更新所有键的值:为什么除了狗以外的所有键?

预先感谢您的帮助!

3 个答案:

答案 0 :(得分:3)

这是因为您一直将相同的ArrayList实例放入地图中。

之后

map.put(s, newWord);

写:

newWord = new ArrayList<>();

或者更好的是,只需在该块中声明newWord

List<String> newWord = new ArrayList<>();
newWord.add(fileName);
map.put(s, newWord);

或者,一行:

map.put(s, new ArrayList<>(Arrays.asList(fileName));

但是我建议您的“列表”实际上是Set,因为您仅在不存在的情况下添加它们。如果要保留订单集,请使用LinkedHashSet作为值类型。

答案 1 :(得分:2)

您一次创建了newWord,然后将相同的List放置在map上。

ArrayList<String> newWord = new ArrayList<String>(); 

loop中,您将其输入为:

        if (!map.containsKey(s)) {
            newWord.clear();
            newWord.add(fileName);
            map.put(s, newWord); // your problem lies here
            // created just once but put in many times;
            System.out.println("New:" + s + "\t" + newWord);
        } 

您可以将该行替换为

map.put(s, new ArrayList<>());

答案 2 :(得分:2)

1。正确

您放置在地图中的newWord始终是相同的实例,因此每个键的值都具有相同的list,您可以对其进行更改并随处可见,因此您需要放置一个新的{每次{1}}:

list

2。改进

  • 不使用原始类型,使用泛型:for (String s : fr.words()){ if (!map.containsKey(s)) { ArrayList<String> newWord = new ArrayList<>(); newWord.add(fileName); map.put(s, newWord); } else { ArrayList<String> currArr = map.get(s); if (!currArr.contains(fileName)){ currArr.add(fileName); } } } ,并且在创建原始类型时,只需使用HashMap<String, ArrayList<String>>

  • 使用map = new HashMap<>而不是Set,它将自动避免重复,并且您不必检查List之前是否已经存在filename

  • 第一个addif,而check & addelse,您可以简化add部分,然后总是{{1 }}。对于check部分,您可以使用add

  • 中的check方法同时进行检查和放置

3.Shorten

  1. putIfAbsent()会尝试从密钥中获取值,如果没有该密钥,则将其插入新的Map
  2. 如果返回并准备使用该值(值为computeIfAbsent
  3. 只需将文件名HashSet设置为此值,就无需检查,因为我们使用的是HashSet,并且不可能重复
add