此用例的Java集合

时间:2011-04-05 09:45:35

标签: java data-structures collections set hashcode

假设我们有一堆Car对象。

每辆车都有一些与众不同的特性,例如:制造商,型号,年份等(这些可用于创建不同的hashCodes)。

每辆车都有一个PurchaseOffer对象列表(PurchaseOffer对象包含定价\零售商信息)。

我们收到来自几个不同来源的汽车列表,每辆汽车都有一个PurchaseOffer。 事实是,这些列表可能会重叠 - Car可以出现在多个列表中。

我们希望将列表汇总到一个汽车集合中,其中每辆汽车都拥有所有遇到的PurchaseOffers。

我的问题是在此聚合过程中选择要收集的内容

感觉很自然地使用java.util.HashSet来保存我们的汽车,这样当经过不同的汽车列表时,我们可以检查汽车是否已经存在于已摊销的O(1)中, 但是 - 你无法从一个Set中检索一个元素(在我们的例子中 - 当我们遇到一个已经存在于Set中的Car时 - 我们本来希望根据它的标识hashCode从Set中检索那个Car并向其添加PurchaseOffers)

我可以使用HashMap,其中每个Car的hashCode映射到实际的Car对象,但它可能不是学校书籍解决方案,因为它不安全 - 我必须确保自己每个hashCode映射到一个Car hashCode - 可能存在不一致。 当然,可以制作一个保证这种一致性的指定数据结构 - 不应该已经存在吗?

有人能建议我追求的数据结构,还是指出设计错误? 感谢。

9 个答案:

答案 0 :(得分:6)

由于这是多对多关系,因此您需要一个双向多地图。 Car是第一个的关键,List of PurchaseOrder作为值。 PurchaseOrder是第二个的关键,以汽车列表作为值。

底层实现是两个HashMaps。

在其上放置API以获得所需的行为。或者看看Google Collections是否可以为您提供帮助。它是BiMap和两个MultiMaps的组合。

答案 1 :(得分:5)

我认为你确实需要(至少)HashMap<Car, List<PurchaseOffer>> ......正如@Andreas_D所建议的

您的反对意见认为每个Car已经有List<PurchaseOffer>HashMap中的列表是聚合列表,其中包含来自代表同一实体汽车的所有PurchaseOffer个对象的所有 Car个对象。

创建新列表的目的是避免更改原始Car对象上的原始列表。 (如果这不是问题,那么您可以从代表物理汽车的集合中选择一个Car实例,并将其他PurchaseOffer个对象合并到该列表中。)

我不完全确定为什么@duffymo建议在它们之间使用双向地图,但我认为这是因为来自不同来源的不同Car对象可能对同一辆实体车具有互补(或相互矛盾)的信息。通过保留所有实例,您可以避免丢弃信息。 (再次,如果您乐意放弃变异和/或丢弃信息,您可以尝试将每辆汽车的信息合并到一个Car对象中。


如果你真的不关心保存信息并且准备好合并的东西,那么以下方法可能会有效:

  HashMap<Car, Car> map = new HashMap<Car, Car>(...);
  for (Car car : carsToBeAggregated) {
      Car master = nap.get(car);
      if (master == null) {
          map.put(car, car);
      } else {
          master.offers.addAll(car.offers);
          // optionally, merge other Car information from car to master
      }
  }

您不应该尝试使用Car.hashCode()作为任何关键。 Hashcode值不是唯一标识符:两种不同的汽车最终会有相同的哈希码值。如果您尝试使用它们就好像它们是唯一标识符一样,您将遇到麻烦......

答案 2 :(得分:3)

基本数据结构应为HashMap<Car, List<PurchaseOffer>>。这允许存储和接收所选汽车的所有报价。

现在你可能必须为Car.equals()找到一个合适的实现,以确保来自不同来源的“汽车”真的是一样的。将equals()基于真实世界汽车的唯一标识符(VIN)怎么样?

答案 3 :(得分:1)

我更喜欢使用HashMap<Car, List<PurchaseOffer>>,如前所述(Andreas,Stephen),主要是如果Car对象持有PurchaseOffers列表。
否则,如果每辆车都有唯一的ID,我会考虑使用HashMap<Car, Car>或更好的IMO HashMap<ID, Car>

它可以简单地将Car的hashCode映射到Car,如问题中所述,因为不同的Cars可以具有相同的hashCode!

(无论如何,我会创建一个自己的类来存储和管理Cars。这将包含HashMap,或者哪个 - 所以很容易更改实现而无需更改其界面)

答案 4 :(得分:0)

创建扩展哈希的自定义类 设置,
覆盖方法包含(对象o)
检查os哈希码是否相同并返回结果,并将对象添加到集合中并且仅当它不包含该对象时

答案 5 :(得分:0)

定义新的自定义聚合类怎么样?定义哈希码,使得汽车的id充当密钥并相应地覆盖equals()。定义接受原车的自定义方法,并在列表上执行联合操作。最后将自定义对象存储在HashSet中,以实现持续的时间查找。

在纯粹的术语中,聚合是超出单个对象范围的行为。访客模式试图解决类似的问题。

或者如果你有一个sql数据存储区,使用group by的简单选择就可以了。

答案 6 :(得分:0)

    //alt. 1
    List<Offer> offers;
    List<Car> cars;
    Map<Car, List<Offer>> mapCarToOffers;
    Map<Offer, List<Car>> mapOfferToCars;
    public void List<Offer> getOffersForCar(Car aCar);
    public void List<Car> getCarsForOffer(Offer anOffer);

备选方案1 会使用hashCode()Car

Offer
    //alt. 2
    List<Offer> offers;
    List<Car> cars;
    Map<Integer, List<Offer>> mapCarIdToOffers;
    Map<Integer, List<Car>> mapOfferIdToCars;
    public void List<Offer> getOffersForCarId(int aCarId);
    public void List<Car> getCarsForOfferId(int anOfferId);

备选方案2 会使用hashCode()的{​​{1}}。这可以减轻您对“安全”的担忧,因为Integer对象的哈希码不应该在值唯一的位置重叠。这会产生额外的开销,即必须为每个IntegerCar对象维护唯一ID,但是,我猜您可能已经拥有了符合您业务要求的ID。 请注意,您可以选择使用其他类作为ID的Offer替代(例如int)。

对于这两种选择,请使用StringList实施ArrayList - 根据其他要求(例如插入频率/删除与查找。使用LinkedList实施Map - 请参阅上面有关如何使用哈希码的注释。


作为旁注,在我们的软件中,我们使用上述两者来表示类似类型的多对多数据。与您的用例非常相似。 两种选择都很有效。

答案 7 :(得分:0)

  

Welp,是的,HashMap<Car, List<PurchaseOffer>>如果不是因为这个事实将是完美的   每个Car都包含List<PurchaseOffer>作为属性。可以说组成了一个Car对象   两部分:识别部分(假设每辆车确实有一个独特的VIN),以及清单   PurchaseOffer秒。

在这种情况下,将Car类拆分为两个类 - 具有标识属性的CarType类,然后是列表部分(可能由Car一起使用)。然后使用Map<CarType, Lost<PurchaseOffer>作为数据结构(或MultiMap<CarType, PurchaseOffer>)。

答案 8 :(得分:-1)

为什么不为此使用对象数据库?您可以存储您想要的任何对象图,并且您将获得一个搜索API,您可以使用它来执行您想要的任何关系/检索机制。一个简单的集合可以工作,但听起来你想要一个比集合更复杂的关系。查看db4o(http://db4o.com) - 它对于这类事情非常强大。