什么是用于存储嵌套项(如州中的城市)的良好Java数据结构?

3
我刚刚开始学习Java,正在寻求关于存储嵌套数据的好方法的建议。例如,我希望存储可通过在给定州中查找城市来访问的城市人口数据。(注意:最终,每个城市都会存储其他数据,这只是入门的第一次尝试。)
目前我使用的方法是拥有一个StateList对象,其中包含一个HashMap,该HashMap通过字符串键(即HashMap)存储State对象。每个State对象都包含其自己的HashMap,以城市名称为键(i.e. HashMap)。
我想要实现的大致情况如下:
// TestPopulation.java
public class TestPopulation {

  public static void main(String [] args) {

    // build the stateList Object
    StateList sl = new StateList();

    // get a test state
    State stateAl = sl.getState("AL");

    // make sure it's there.
    if(stateAl != null) {

      // add a city
      stateAl.addCity("Abbeville");

      // now grab the city
      City cityAbbevilleAl = stateAl.getCity("Abbeville");

      cityAbbevilleAl.setPopulation(2987);

      System.out.print("The city has a pop of: ");
      System.out.println(Integer.toString(cityAbbevilleAl.getPopulation()));

    }

    // otherwise, print an error
    else {
      System.out.println("That was an invalid state");
    } 
  }
}

// StateList.java

import java.util.*;

public class StateList {

  // define hash map to hold the states
  private HashMap<String, State> theStates = new HashMap<String, State>();

  // setup constructor that loads the states
  public StateList() {

    String[] stateCodes = {"AL","AK","AZ","AR","CA","CO"}; // etc...

    for (String s : stateCodes) {
      State newState = new State(s);
      theStates.put(s, newState);
    }
  }

  // define method for getting a state
  public State getState(String stateCode) {
    if(theStates.containsKey(stateCode)) {
      return theStates.get(stateCode);
    }
    else {
      return null;
    } 
  }
}

// State.java

import java.util.*;

public class State {

  // Setup the state code
  String stateCode;

  // HashMap for cities
  HashMap<String, City> cities = new HashMap<String, City>();

  // define the constructor
  public State(String newStateCode) {
    System.out.println("Creating State: " + newStateCode);
    stateCode = newStateCode;
  }

  // define the method for adding a city
  public void addCity(String newCityName) {
    City newCityObj = new City(newCityName);
    cities.put(newCityName, newCityObj); 
  }

  // define the method for getting a city
  public City getCity(String cityName) {
    if(cities.containsKey(cityName)) {
      return cities.get(cityName);
    }
    else {
      return null;
    } 
  }
}

// City.java

public class City {

  // Define the instance vars
  String cityName;
  int cityPop;

  // setup the constructor
  public City(String newCityName) {
    cityName = newCityName;
    System.out.println("Created City: " + newCityName);
  }

  public void setPopulation(int newPop) {
    cityPop = newPop;
  }

  public int getPopulation() {
    return cityPop;
  }
}

这对我有用,但我想知道是否有我没有遇到的问题,或者是否有其他/更好的方法来做同样的事情。

(附注:编辑后将setPop()和getPop()分别更改为setPopulation()和getPopulation()以避免混淆)

(顺便说一句,我知道需要添加更多的错误检查,但现在,我专注于尝试找出一个好的数据结构。)


你的解决方案看起来很合理。州拥有城市,因此将城市作为州的0到n属性是完全合理的。除此之外,算法或性能问题可能会影响解决方案,但在你的情况下似乎不是这样。 - RobertB
1
@user432209 继承什么?城市不是州的子类,它是一个组成成员。 - RobertB
1
对的,它不是城市是州;而是州有城市。组合是正确的关系。 - duffymo
你可以使用关系型数据库来实现。 - Enrique
@Enrique - 对于这个项目,我将无法访问数据库,但你说得对,我尝试做的事情的概念完全适合在数据库中实现。 - Alan W. Smith
4个回答

1

查看来自Guava集合的Multimap数据结构。这不是您解决方案的完整答案,但会简化到一定程度。但美妙之处在于现在您可以使用MapMaker来缓存您对城市的“人口查询”。

Multimap<String, City> stateToCities = ArrayListMultimap.create();

stateToCities.put("GA",new City("Atlanta",100000));
stateToCities.put("GA",new City("Cumming",50000));

谢谢。我会研究一下。如果我正确地阅读了文档,这意味着在“State”级别上不会有一个对象,只有一个键。虽然现在这可以满足我的需求,但我有预感将来我会需要该级别的对象。我仍然会收藏Multimap,因为我知道有些情况下它会完美地工作。 - Alan W. Smith
1
您可以保留State对象不变,并通过覆盖equals()方法来检查与2个字符状态缩写的相等性,仍然可以将其用作键。 - Aravind Yarram
我只学了几周的Java,还没有涉及到覆盖方法。 (我知道你可以这样做,并且知道如何做,但还没有尝试过。)现在我将继续使用对象树,并回头看看这个问题,当我对所有内容有更好的掌握时再来处理它。 - Alan W. Smith

1
如果你真的需要这些聚合(具有状态的StaleList,具有城市的States),那么这是正确的实现方式。它可能不是最直接的方法,但它是最面向对象的方法。因此,以极简主义为代价,您确实获得了内聚性、耦合性、可维护性和所有那些花哨的软件工程形容词。对于小型项目,这些特征不应该被强制执行。但对于大型软件项目,它们是有意义的,并且可以避免真正糟糕的代码(但不能保证真正好的代码)。
您还可以使用一些第三方库(例如Pangea答案中的库)来帮助保持代码简洁。
参见:
1:http://en.wikipedia.org/wiki/Cohesion_(computer_science)
2:http://en.wikipedia.org/wiki/Coupling_(computer_science)
3:http://en.wikipedia.org/wiki/Maintainability

由于我刚开始学习,我试图倾向于更面向对象的方法。(我第一次尝试这个时,是在另一个极端,只有一个类中的HashMap of HashMaps。)我仍处于学习规则之前的阶段。感谢您确认我的理解与核心概念一致。 - Alan W. Smith

0

我会考虑使用一个类来管理一个名为States的列表,其中包含二维数组中的城市和人口成员变量。

其他想法: cityAbbevilleAl没有检查是否等于null。 起初,我将getPop误读为一种流行方法而不是人口。


我已经在原始帖子中添加了一条注释,最终我需要的不仅仅是城市的人口。因此,我认为这指向了使用哈希表将每个城市存储为对象。-- 随着我的进展,我将添加更多的错误检查(例如,对城市对象的空值检查),但目前我只是刚刚开始。另外,我已经将getPop()和setPop()更新为getPopulation()和setPopulation()以增加清晰度。感谢您指出混淆之处。 - Alan W. Smith

0

请拼出单词“population”。按键成本很低。你已经让一个回答者感到困惑了;其他人刚开始也可能不理解。

人口数量可以是负数吗?如果不行,我建议在setPopulation()函数的合同中进行检查。


断言关键字的最佳用例是负面检查。 - Aravind Yarram
我已更新原帖以拼写完整单词。同意这样做会更容易。在最终版本中,我还有更多的检查工作要做,并将添加负面检查。在放置所有验证之前,我想确保核心结构正确。 - Alan W. Smith

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