无法使用提取的Map键到String

时间:2016-02-28 21:59:53

标签: java list for-loop hashmap key

在我有公交车的应用程序上,每辆公交车都与多个车站相关联,并且在一天的时间内每个车站也有几个到达时间。

Map<String, Station> stationMap = new HashMap<>();


Map<String, String[]> busTimesForSingleStation = new HashMap<>(); // key is station's name and value would be an array of times in string.
List<Station> stationsOfBus = new ArrayList<>();
Map<List<Station>, String[]> allArriveTimes = new HashMap<>();

for (int i = 10; i <= 400; i += 10) {
    busTimesForSingleStation = generateTimeForStation(i);
    String key;
    for (Map.Entry<String, String[]> e : busTimesForSingleStation.entrySet()){
        key = e.getKey();
    }
    allArriveTimes.put(stationsOfBus.add(stationMap.get(key)),
                       busTimesForSingleStation.get(Integer.toString(i)));
}

第一个for循环旨在将多个站点(每个站点多次)添加到总线。但是,使用了内部for循环,因为我还不知道获取busTimesForSingleStation的密钥的任何其他方法。 (即使每次busTimesForSingleStation地图中只有一个条目。)

我的具体问题是倒数第二行使用的key变量显然不起作用,我从IDE中收到此错误:

  

变量&#39;键&#39;可能尚未初始化

2 个答案:

答案 0 :(得分:2)

我同意SimMac对问题的描述,但不同意他提出的建议。

简单地说String key = "";的问题是你使用这个值。也许这没关系 - stationMap.get(key)返回一个安全的“未找到”值 - 但更可能不会。假设它返回null:现在您将null添加到stationsOfBus,这可能会在运行时失败(最好的情况 - 但它可能会在很长一段时间内从此代码中失败),或者默默地继续通过你的程序传播废话(最糟糕的情况)。

修复编译器捕获的实际问题(地图可能为空)要比修复隐藏该问题的修复程序要好得多。

如果busTimesForSingleStation不能为空,则断言该事实:

if (!busTimesForSingleStation.isEmpty()) {
  String key = "";  // Now you know it will be overwritten.
  for (String maybeKey : busTimesForSingleStation.keySet()) {
    key = maybeKey;
  }
} else {
  throw new AssertionError("Should never happen!");
}

或者,如果 可能为空,请明确处理:

if (!busTimesForSingleStation.isEmpty()) {
  String key = "";  // Now you know it will be overwritten.
  for (String maybeKey : busTimesForSingleStation.keySet()) {
    key = maybeKey;
  }
} else {
  // Some logic to handle it correctly.
}

此问题的深层原因是您选择generateTimesForStation的返回类型无法准确编码您的意图:

  • 您的问题表明您希望返回的地图包含一个键/值对
  • 您的返回类型表示该方法的调用者应该返回零,一个或多个键/值对

显然,“一个k / v对”可以在包含“零个或多个k / v对”的数据结构中表示 - 但选择这样表示它意味着调用代码必须能够处理“零”和“或更多”案件。

例如,如果返回的数据结构 包含2个或更多个k / v对,并且您想要选择“最后”键,则必须担心迭代顺序那些对。如果类型系统可以保证它永远不会发生,则完全删除处理这种情况的要求。

如果您想要返回完全一个k / v对,有两个要突出显示的选项:

  • 您代码中最简单的更改是使用Map.Entry<String, String[]>而不是Map<String, String[]>Map.Entry只是您在循环中迭代的结构的元素之一。

    Map.Entry<String, String[]> entry = generateTimesForStation(i);
    String key = entry.getKey();
    // Do whatever with the key.
    
  • 编写一个自定义类型来表示k / v对,并返回它的实例。 Map.Entry(以及扩展名Map)的一个缺点是“key”和“value”没有附加语义含义。如果您有名为getStationNamegetTimes(或其他)的访问者,则可能更容易一目了然地阅读代码。精心挑选的班级名称也比Map.Entry<String, String[]>的通用汤更漂亮。

    BusTime entry = generateTimesForStation(i);
    String key = entry.getStationName();
    // Do whatever with the key - or maybe just refer to
    // `entry.getStationName()` directly.
    

无论你选择哪一个,你现在都避免使用未初始化的变量和循环,因为没有什么可以循环了。

如果你想返回零或一个k / v对,上面的两个选项都是有效的,但你应该将结果包装成类似Optional的类型,以强烈表明没有这样的k的可能性/ v对存在。您可以返回null来表明这一点,但我不鼓励这样做 - 请参阅Louis Wasserman's answer了解为什么Optional优先于null(他是Optional谈论Guava Optional,但Java 8 Optional<Map.Entry<String, String[]>> entry = generateTimesForStation(i); if (entry.isPresent()) { String key = entry.get().getKey(); // Do whatever with the key. } else { // Handle the "not found" case. } 的论点是相同的。)

generateTimesForStation

使用更具体的返回类型的方法也可能有助于实现 public static string[] SplitArrey(string[] ArrInput, int n_column) { string[] OutPut = new string[n_column]; int NItem = ArrInput.Length; // Numero elementi int ItemsForColum = NItem / n_column; // Elementi per arrey int _total = ItemsForColum * n_column; // Emelemti totali divisi int MissElement = NItem - _total; // Elementi mancanti int[] _Arr = new int[n_column]; for (int i = 0; i < n_column; i++) { int AddOne = (i < MissElement) ? 1 : 0; _Arr[i] = ItemsForColum + AddOne; } int offset = 0; for (int Row = 0; Row < n_column; Row++) { for (int i = 0; i < _Arr[Row]; i++) { OutPut[Row] += ArrInput[i + offset] + " "; // <- Here to change how the strings are linked } offset += _Arr[Row]; } return OutPut; } :必须返回一个(或者可能是零或一个)值的约束可以帮助您捕获控制流路径导致返回值不符合这些约束条件。

答案 1 :(得分:1)

问题是,编译器不知道第二个for循环是否至少运行一次(因为它可能无法知道 busTimesForSingleStation 是否为空)

要解决此问题,只需将String key;更改为String key = "";,以便编译器知道,即使 busTimesForSingleStation 为空,也不会成为空引用。< / p>