Java Map返回一个存在的键的null值

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

我在我的代码中添加了许多打印语句,这就是我的情况...
这是我的字典characterDictionary

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

最后一个关键字“variableIndent”是麻烦的地方!
下面是代码...

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

出现了不恰当的输出:null

我仔细检查了代码,多次核对和三思,但是"variableIndent"这个键和characterDictionary.get("variableIndent")方法的字符串参数之间绝对没有任何区别,但某些情况下它的表现好像这个键并不存在。

我可以绝对保证这个键确实存在,并且这两个字符串是完全相同的。只有“variableIndent”与其“@”值有问题,而其他元素(目前为止我已经检查了大约3个)都可以正常检索。这是为什么呢?

你可能会注意到字典中包含非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;  }

这导致了一个问题的字典产生。
我有一组与文本文件中角色名称对应的关键字面值,用于在程序中访问字典。

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";

问题如下:

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

发生了什么!?
我的函数有问题吗?!
它们在其他所有方面都运行良好。

谢谢!


为了确保该键存在,迭代此映射中的所有键并打印它们的值。 - Andrew Logvinov
你能展示一些能够重现这种行为的代码吗? - assylias
修改了原帖。认为这是文本文件和Eclipse之间的编码问题。 - Anti Earth
6个回答

6

将包含键和值的UTF-8文本文件转换为无BOM的UTF-8格式。 在"variableIndent"之前有三个字节(UTF-8 BOM 0xEF,0xBB,0xBF)。 请参阅http://en.wikipedia.org/wiki/Byte_order_mark了解更多信息。


我该如何做到这件事? - Anti Earth
@AntiEarth 这可能会有所帮助...无论如何,如果您找到解决方案,请也通知我。我很好奇您将如何解决这样奇怪的问题。不幸的是,在SO上没有任何自动跟随问题的方式。 - dantuch
这个问题已经解决了!我在Notepad++中打开了我的文本文件,将编码更改为UTF-8 without BOM,并重新保存了文件。这些烦人的字符消失了,一切都恢复正常了。 谢谢! - Anti Earth

1

如果您担心编码问题(这似乎是问题所在),您必须确保项目的编码设置为UTF-8。要检查它,请转到项目菜单,然后选择属性。可能需要将文本文件编码从容器继承更改为其他:UTF-8


完成。仍然没有成功。我完全被难住了。 - Anti Earth
@Anti Earth 我认为你应该打印“both”字符串的hashCode() - 一个来自映射,另一个是你搜索并最终没有找到的。如果它们是相同的字符串,它们应该是相同的。 - dantuch
在其他答案中已经探讨过这个问题,它们不是同一个字符串。无论文本文件中的哪一行首先被处理并首先添加到字典中,都会以某种隐藏的方式“变异”。仍然不知道如何或为什么会这样。 - Anti Earth

0
我建议您在调试器中查看代码。我怀疑该地图与您想象的不同。
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]'。 - Anti Earth

0

嗯,尝试使用这段代码并告知我们输出结果

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

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

附注:也许需要更改键和值的类型。


thusChar spaceChar plusChar equalsIndent multiplyChar equalsChar newlineChar divideChar subjectChar variableIndent ∴ + # ×

\n ÷ : @完全符合预期!我将尝试使用更改后的密钥。
- Anti Earth
我以为我已经搞清楚了,可是还是有问题。你能否把你创建和初始化characterDictionary的代码发一下? - foklepoint

0
如果您认为这是一个编码问题,那么请尝试使用以下方法比较实际内容,而不考虑其外观。
使用 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));

Java中的DataInputStream文档说:“数据输入流以机器无关的方式允许应用程序从底层输入流读取原始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 - Anti Earth
当然可以!(抱歉,我在任何事情上都特别慢) - Anti Earth
将文件中的字符串转换为字节后,您得到了什么?它有多少个字节,第一个字节是什么? - Rajesh J Advani
你能给我提供一些示例吗?我非常想要一个字符串ArrayList的输出,但是由于我的学习能力和找不到正确方法,可能导致我使用了数据输入流:P - Anti Earth
我一直在清理误用DataInputStream读取文本的代码,因为有太多不好的例子,这种情况必须停止。 - Peter Lawrey
显示剩余4条评论

0

试着这样做...

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());


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接