Java collection for this use case

Say we have a bunch of car objects.

Each car has some distinctive properties, for example. manufacturer, model, year, etc. (they can be used to create separate hash codes).

Each car has a list of PurchaseOffer objects (the PurchaseOffer object contains pricing / retail information).

We get Car Listings from several different sources, each car with one PurchaseOffer. The fact is that these lists may overlap - a car may appear in several lists.

We want to combine the lists into a single set of vehicles in which each vehicle contains all the PurchaseOffers found in it.

My problem is choosing which collection to use in this aggregation process:

It turns out to be natural to use java.util.HashSet to store our cars, thus, by going through different lists of cars, we can check whether the car exists in Set in depreciated O (1), however - you cannot get an element from the set (in our case - when we meet a car that already exists in Set - we would like to get this car from the set based on its identification hash code and add PurchaseOffers to it).

I can use a HashMap where each Car hashCode maps to a real Car object, but this is probably not a school book solution, because it is unsafe - I need to make sure that every hash code displays the car with what hashCode - there may be inconsistency. Of course, you can create a specific data structure that guarantees this consistency. Could it already exist?

Can someone suggest the data structure that I am, or indicate a design error? Thanks.

+7
source share
9 answers

I think you really need (at least) a HashMap<Car, List<PurchaseOffer>> ... as suggested by @Andreas_D

Your objection that each Car already has a List<PurchaseOffer> is next to the dot. A list in a HashMap is a cumulative list containing all PurchaseOffer objects from all Car objects that designate the same physical vehicle.

The point of creating a new list is to avoid changing the original lists on the original Car objects. (If this was not a problem, you can select one Car instance from the set representing the physical vehicle and combine the PurchaseOffer objects from the others into this list.)

I'm not quite sure why @duffymo proposed a bidirectional map between them, but I think this is because different Car objects from different sources may have additional (or conflicting) information for the same physical car. By storing all instances, you avoid dropping information. (Once again, if you are happy to refuse the mutant and / or throw away the information, you can try to combine the information about each individual car into one Car object.


If you really didn’t care about preserving the information and were ready to combine the material willingly or not, then probably the following approach:

  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 } } 

You should not try to use Car.hashCode() as a key for anything. Hashcode values ​​are not unique identifiers: there is a clear possibility that two different machines will have the same hash value. If you try to use them as if they were unique identifiers, you run into problems ...

+5
source

Since this is a many-to-many relationship, you need a bi-directional multi-card. The car is the key to the first, with a list of PurchaseOrder as a value. BuyOrder is the key for the second, with a list of cars as a value.

The implementation is based on two HashMaps.

Place the API on top of it to get the behavior you need. Or see if Google Collections can help you. This is a combination of BiMap and two MultiMaps.

+6
source

The main data structure should be HashMap<Car, List<PurchaseOffer>> . This allows you to store and receive all offers for one selected car.

Now you may have to find a suitable implementation for Car.equals() to ensure that the “cars” coming from different sources are actually the same. What about basing equals() on a unique identifier for a real-world car (VIN)?

+3
source

I would prefer to use HashMap<Car, List<PurchaseOffer>> , as suggested earlier (Andreas, Stephen), mainly if the Car object does not hold the BuyOffers list.
Otherwise, I would consider using a HashMap<Car, Car> or, better, IMO, a HashMap<ID, Car> if there is a unique identifier for each car.

It can not just map Car hashCode to a car, as mentioned in the question, as individual cars can have the same hash code!

(In any case, I would create my own class for storing and managing machines. This will contain a HashMap, or depending on what is easy to change without changing the interface)

+1
source

create a custom tout class that extends the Set hash; the override method contains (Object o). Check if the os hash code is the same or not and return the result according to and add the object to the set and only if it does not contain this object

0
source

What about defining a new custom aggregation class? Define the hash code so that the car identifier acts as a key and changes equalities () accordingly. Define a custom method for accepting your original car and perform the merge operation in the lists. Finally, save your custom objects in a HashSet to achieve a constant time search.

In purist terms, aggregation is behavior that goes beyond a single entity. The visitor pattern is trying to solve a similar problem.

Alternatively, if you have a sql data store, a simple selection using a group will do the trick.

0
source
  //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); 

Alternative 1 will use hashCode() of Car and 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); 

Alternative 2 will use hashCode() of Integer . This will save you from worrying about “security,” because hash codes for Integer objects should not overlap where values ​​are unique. This leads to the additional overhead of having to maintain unique identifiers for each Car and Offer object, however I assume that you probably already have those of your business requirements.
Note. You can use other classes as an alternative to int for an identifier (e.g. String ).

For both alternatives, implement a List using an ArrayList or LinkedList - which is best for you to determine based on other requirements, such as insert / delete frequency and search. Contribute Map with HashMap - see comments above on how hash codes are used.


As a side note in our software, we use both of the above representations to represent these many-to-many data types. Very similar to your use case. Both alternatives work very well.

0
source

Welp, yes, HashMap<Car, List<PurchaseOffer>> would be ideal if it weren't for the fact that each Car contains a List<PurchaseOffer> as a property. We can say that the Car object is composed of two parts: the identification part (let each car really has a unique VIN), and the PurchaseOffer s list.

In this case, divide the Car class into two classes - the CarType class with identifying attributes, and then part of the list (perhaps both are used together by Car ). Then use Map<CarType, Lost<PurchaseOffer> for your data structure (or MultiMap<CarType, PurchaseOffer> ).

0
source

Why not use an object database for this? You can store any graph of objects that you need, and you will get a search API with which you could use any relationship / search mechanism that you wanted. A simple collection might work, but it looks like you need a more complex relationship than a collection. Look at db4o (http://db4o.com) - it is very powerful for this kind of thing.

-one
source

All Articles