【问题标题】:Java Map returns null for a present keyJava Map 为当前键返回 null
【发布时间】:2012-08-08 09:50:02
【问题描述】:

我遇到了一个非常不寻常的问题。
基本上,我试图从字符串映射到字符串中获取与键关联的值。
我知道我正在使用的密钥存在;我使用的是我用来放入它的相同字符串!

我的代码中都打印了语句,这就是我遇到的情况...
这是我的字典characterDictionary

{thusChar=∴, spaceChar= , plusChar=+, equalsIndent=#, multiplyChar=×, equalsChar==, newlineChar=\n, divideChar=÷, subjectChar=:, variableIndent=@}

最后一个键“variableIndent”很麻烦!
这是代码...

System.out.println  (   characterDictionary.get("variableIndent") );

不恰当地输出:null

我已经检查、双重检查和三次检查我的代码。
"variableIndent" 键和 characterDictionary.get("variableIndent") 的字符串参数之间绝对没有区别,但它的行为就像这个键不存在一样。

我可以绝对保证这个键是存在的,并且两个字符串是相同的。
字典的所有其他元素(我检查过的元素;到目前为止大约 3 个)都正常检索。为什么带有“@”值的“variableIndent”会出现?

您可能会注意到字典包含非 ASCII 字符,例如“thusChar”。这有关系吗?

谢谢
(这似乎是一个非常简单和微不足道的问题,好像我犯了一个可怜的愚蠢的错误,但我就是无法解决它!)

编辑:

好的,这必须与编码有关。
我从字典中取出字符串键并将其与我的 get 参数进行比较。
打印时,它们是相同的,但 java 说它们不相等。
密钥字符串来自 UTF-8 编码的文本文件,而参数字符串来自 java Eclipse 文字。
但是字符是相同的。
是什么问题,我该如何解决?

谢谢!

编辑:

嗯,这就是幕后实际发生的事情。
我有一个 UTF-8 文本文件,其中包含以下内容...

variableIndent,@
equalsIndent,#
spaceChar, 
newlineChar,\n
multiplyChar,×
divideChar,÷
plusChar,+
equalsChar,=
subjectChar,:
thusChar,∴

我通过将文件的每一行作为ArrayList<String> 元素读取,通过传递文件的目录来“加载”该文件:

private static ArrayList<String> readLinesFile(String ResourceFile)  {
    ArrayList<String> Lines = new ArrayList<String>();
        try{
            InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
            BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
            String strLine;
            while ((strLine = br.readLine()) != null)  {
                Lines.add(strLine);  } 
            in.close();  }
        catch (Exception e)  {
            System.out.println("Error: " + e.getMessage());  } 
        return Lines;  }

到这里为止一切都很好。
然后我将这个 ArrayList 传递给一个函数,该函数通过“,”字符分割每个元素(使用个人包中的函数;这绝对不是问题),并将第一部分作为键添加到新的第二部分字典。

private static Map<String, String> generateBaseUnits_Characters_Prefixes(ArrayList<String> FileContent)  {
    Map<String, String> BaseUnitsCache = new HashMap<String, String>();
    ArrayList<String> currentLine;
    for (int i=0; i<FileContent.size(); i++)  {
        currentLine = MiscellaneousFunctions.splitString(FileContent.get(i), ",");
        BaseUnitsCache.put(currentLine.get(0), currentLine.get(1)); }
    return BaseUnitsCache;  }

这会产生导致所有麻烦的字典。
我有一组与文本文件中的字符名称相对应的 Key Literals,用于访问程序中的字典。

public static String variableIndentKey = "variableIndent";
public static String equalsIndentKey = "equalsIndent";
public static String spaceCharKey = "spaceChar";  
public static String newlineCharKey = "newlineChar";
public static String multiplyCharKey = "multiplyChar";
public static String divideCharKey = "divideChar";  
public static String plusCharKey = "plusChar";
public static String equalsCharKey = "equalsChar";  
public static String subjectCharKey = "subjectChar";
public static String thusCharKey = "thusChar";

问题来了:

文本文件的第一行在字典中'screw up'。 它被添加到字典中,并在 keySet 和字典的打印格式中正确显示,但尝试访问它会返回“null”。
(在这种情况下,它是 variableIndent。如果我将 variableIndent 放在文本文件中的其他位置,equalsIndent 搞砸了,等等)

发生了什么事!?
我有一个狡猾的功能吗?!
他们在其他所有方面都运作良好。

谢谢!

【问题讨论】:

  • 为了保证这个键存在,遍历这个映射中的所有键并打印它们的值。
  • 你能展示一些重现行为的东西吗?
  • 编辑了 OP。相信这是文本文件和 Eclipse 之间的编码问题。

标签: java string dictionary map null


【解决方案1】:

将包含键和值的 UTF-8 文本文件更改为没有 BOM 的 UTF-8。 共有三个字节(UTF-8 BOM 0xEF,0xBB,0xBF 在 "variableIndent" 之前) 见http://en.wikipedia.org/wiki/Byte_order_mark

【讨论】:

  • 我该怎么做呢?
  • @AntiEarth 这可能会有所帮助...无论如何,如果您找到解决方案,请通知我。我很好奇你将如何解决这种奇怪的问题。遗憾的是,无论如何都没有自动的方式来解决关于 SO 的问题。
  • 这已经解决了这个问题!我在记事本++中打开了我的文本文件,将编码更改为没有BOM的UTF-8,然后重新保存了文件。这些烦人的角色消失了,一切都恢复了正常。谢谢!
【解决方案2】:

如果你害怕编码问题(这似乎是这里的问题),你必须确保你的项目的编码设置为UTF-8。要检查它,请转到 Project 菜单,然后选择 Properties。可能需要将 文本文件编码 从 Inherited from container 更改为 Oher: UTF-8

【讨论】:

  • 完成。仍然没有成功。我完全被难住了。
  • @Anti Earth 我想你应该打印“两个”字符串的hashCode() - 来自地图的this1,以及你搜索的this1并以什么都没有结束。它们应该是相同的,如果它是相同的字符串。
  • 在其他答案中探索了这一点;它们不是同一个字符串。首先处理文本文件中的哪一行并首先添加到字典中,以某种方式以隐藏的方式“变异”。仍然不知道如何或为什么。
【解决方案3】:

嗯,试试这段代码,让我们知道输出

for (String key : characterDictionary.keySet() ) {
System.out.println(key);
}

for ( String value :  characterDictionary.values() ) {
System.out.println(value);
}

P.S:可能想要更改键和值的类型。

【讨论】:

  • thusChar spaceChar plusChar equalsIndent multiplyChar equalsChar newlineChar divideChar subjectChar variableIndent ∴ + # × = \n ÷ : @ 完全符合预期!我会尝试使用密钥 changd
  • Grr 以为我已经弄明白了。您是否有机会在创建和初始化字符字典的位置发布代码?
【解决方案4】:

我建议您在调试器中查看您的代码。我怀疑地图不是你想象的那样。

String data = "{thusChar=∴, spaceChar= , plusChar=+, equalsIndent=#, multiplyChar=×, equalsChar==, newlineChar=\\n, divideChar=÷, subjectChar=:, variableIndent=@}";
Map<String, String> map = new LinkedHashMap<String, String>();
for (String keyValue : data.substring(1, data.length() - 1).split(", ")) {
    String[] parts = keyValue.split("=", 2);
    map.put(parts[0], parts[1]);
}
System.out.println("variableIndent is " + map.get("variableIndent"));

打印

variableIndent is @

你可以试试

for(String key: map.keySet())
   System.out.println("'"+key+"'= "+map.get(key));

【讨论】:

  • 打印 KeySet 并收到 '[thusChar, spaceChar, plusChar, equalsIndent, multiplyChar, equalsChar, newlineChar, divideChar, subjectChar, variableIndent]'
【解决方案5】:

试试这个方法...

Map<String, String> map = new LinkedHashMap<String, String>();


for (Map.Entry<String, String> mp : map.entrySet()){


  System.out.println("Key: "+mp.key()+"::"+"Value: "+mp.value());


}

【讨论】:

    【解决方案6】:

    如果您认为这是编码问题,请尝试使用以下方法来比较实际内容,无论其外观如何。

    使用getBytes() 从两个字符串中获取所有字节(地图中的键,以及代码中的字符串),然后打印

    1. 字节数组的长度
    2. 每个字节为整数

    然后使用getChars() 从两个字符串中获取所有字符或使用charAt() 读取每个字符并打印

    1. 每个字符都是一个整数。

    在这两个检查之间,您应该能够找出两个字符串在字节级别上的不同之处。保存文件的字符集是什么?

    编辑

    我怀疑您对 DataInputStream 的使用导致正在读取的字节出现问题。

    InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
    DataInputStream in = new DataInputStream(fstream);
    BufferedReader br = new BufferedReader(new InputStreamReader(in));
    

    DataInputStream 的 Java 文档说“数据输入流允许应用程序以与机器无关的方式从底层输入流中读取原始 Java 数据类型。应用程序使用数据输出流来写入数据,这些数据以后可以通过一个数据输入流。”您在这里不是从 DataOutputStream 中读取,而是从常规文件中读取。 此外,您已经有一个 InputStream 供 InputStreamReader 使用。将您的代码更改为:

    InputStream fstream = FileManager.class.getResourceAsStream(ResourceFile);
    BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
    

    编辑 2

    另一件值得做的事情,因为你的文本文件中的字符串长了 3 个字节,所以正在改变这个

    BaseUnitsCache.put(currentLine.get(0), currentLine.get(1));
    

    到

    BaseUnitsCache.put(currentLine.get(0).trim(), currentLine.get(1).trim());
    

    【讨论】:

    • 我有一个问题的更新,让我认为编码可能不再相关:P
    • 当然! (对不起,我在任何事情上都特别慢)
    • 将字符串从文件转换为字节给你带来了什么?它有多少字节,第一个字节是多少?
    • 等等,你用 DataInputStream 做什么?为什么不完全摆脱它而只使用 BufferedReader?
    • 这是我的 Java 文件读取 kulfoto.com/pic/0001/0015/L80Cl14168.jpg 它适用于我程序中的所有其他文件 IO。如果您推荐,我会在此错误解决后更改它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    相关资源
    最近更新 更多