I am creating an application where I have to store all the departmental stores and want to cache it.
the hierarchy would be like:-
Country -> State -> City -> Area-> Stores
which data structure should I use So my application will work like if
I query for country and state, it should give me all the store
I query for country and state and city, it should give me all the stores in that area.
I just thought of Tree, but not able to convert this scenario into this data structure.
So just build the hierarchy, with each level containing a Map of the next level down:
class Store {
string name;
// other stuff
}
class Area {
string name;
Map<string, Store> stores;
// other stuff;
}
class City {
string name;
Map<string, Area> areas;
// other stuff
}
class State {
string name;
Map<string, City> cities;
// other stuff
}
class Country {
string name;
Map<string, State> states;
// other stuff
}
And you have a Map of countries at class scope:
Map<string, Country> countries;
If you want all the stores in a particular Country, State, and City, you first have to get a reference to the city:
Country country = countries[countryName];
State state = country.states[stateName];
City city = state.cities[cityName];
Now, for each area in the city, you visit each of the stores:
for (Map.Entry<String, Area> pair : city.areas.entrySet())
{
Area area = entry.getValue();
for (Map.Entry<String, Store> storePair : areas.stores.entrySet())
{
// Here, pair.getKey() is the store name
// and pair.getValue() is the Store object
}
}
This is really just a hierarchical tree implemented with nested dictionaries. Nothing fancy.
You state that you want to cache the information. If that is true, and you do not want to alter it, but only cache it, I would recommend to forego the tree idea and simply build two maps based on the key combinations you really need, i.e.:
one cache mapping a type 1 key (consisting of country and state) to a list of stores
one cache mapping a type 2 key (consisting of country, state and city) to a list of stores in the area
That is the point of a cache, really: You want to put all the logic (which store belongs to which combination of key values) into the construction of the cache (initial or on first request) and not start any kind of iteration every time you do a lookup.
Related
I have a map:
Map<Map<String,Map>, List<Map<String, String>>> map = new HashMap();
What is the easiest way to refactor the following data structure and create a class structure using OOP principles?
You should have a simple yet clear picture of what you want to achieve before starting your refactoring:
Can you break-down the data in your collection into business entities (orders, products, customers, ...) ?
What are the relationships between those data ? It could be a list of user linked to orders containing products.
Use the right Collection for the right usage. HashMap are key/value pair unordered collections. You access an element by its key (one key point to one value, no duplicate keys).
ArrayList are values only ordered collections. You access an element by its integer index.
Given the following collection:
HashMap<String, HashMap<String, String>> map = new HashMap<String, HashMap<String, String>>();
And its representation:
{
"Order-Ref-01": {
"id": "123456"
"client": "MrFooBar"
"destination": "USA"
"type": "Electronic"
},
"Order-Ref-02": {
//...
}
}
You could refactor it into a list of Orders because you decided fetching by key is not needed for example.
List<Order> orders = new ArrayList<>();
And you could create 2+ classes to serve as business entities:
class Client {
private String id;
private String name;
// ...
}
class Order {
private String id;
private Client client;
private String destination;
private String type;
// ...
}
It's all about breaking-down your data structure into smaller parts representing your business entities and their relationships.
I'm preparing for a Java exam and have one question that got me lots of tough time. Despite studying it hard I'm not able to find out what determines the order of the result.
Have a look, please:
class Country {
public enum Continent {
ASIA, EUROPE
}
String name;
Continent region;
public Country(String na, Continent reg) {
name = na;
region = reg;
}
public String getName() {
return name;
}
public Continent getRegion() {
return region;
}
}
public class OrderQuestion {
public static void main(String[] args) {
List<Country> couList = Arrays.asList(
new Country("Japan", Country.Continent.ASIA),
new Country("Italy", Country.Continent.EUROPE),
new Country("Germany", Country.Continent.EUROPE));
Map<Country.Continent, List<String>> regionNames = couList.stream()
.collect(Collectors.groupingBy(Country::getRegion,
Collectors.mapping(Country::getName, Collectors.toList())));
System.out.println(regionNames);
}
}
What is the result?
A. {EUROPE = [Italy, Germany], ASIA = [Japan]}
B. {ASIA = [Japan], EUROPE = [Italy, Germany]}
C. {EUROPE = [Germany, Italy], ASIA = [Japan]}
D. {EUROPE = [Germany], EUROPE = [Italy], ASIA = [Japan]}
and what most important what determines the specific result and not another?
We can eliminate D because keys in Map need to be unique which fails for EUROPE.
We can eliminate C because of order in [Germany, Italy]. Italy was placed before Germany in list, so it also has to be stored in that order in result list.
But how we should decide if we should eliminate B or A? Well, we cant.
Map doesn't guarantee specific order of key-value pairs. Some maps allow remembering order of placing key-value pairs like LinkedHashMap, some allow to order entries by keys like TreeMap, but this behaviour is not specified for Collectors.groupingBy.
It is confirmed by fact that this method is using HashMap, which orders key-value pairs based on hashCode() of key (Country.Continent enum here) and amount of pairs already held. Implementation of hashCode() for Enum is inherited from Object class which means it is based on memory location which can change for each time when we run JVM, so it is random value which prevents us from assuming any order (which confirms that it is unspecified).
So based on lack of specification about Map returned by groupingBy both orders of entries is possible so both A and B are possible answers.
I have a simple object like this
public class Person {
private int id;
private int age;
private String hobby;
//getters, setters
}
I want to group a list of Person by attributes
Output should be like this
Person count/Age/Hobby
2/18/Basket
5/20/football
With a chart for more understanding
X axis : hobby repartition
Y axis : count of person distribution
Colors represents age
I managed to group by one attribute using map, but I can't figure how to group by multiples attributes
//group only by age . I want to group by hobby too
personMapGroupped = new LinkedHashMap<String, List<Person>>();
for (Person person : listPerson) {
String key = person.getAge();
if (personMapGroupped.get(key) == null) {
personMapGroupped.put(key, new ArrayList<Person>());
}
personMapGroupped.get(key).add(person);
}
Then I retrieve the groupable object like this
for (Map.Entry<String, List<Person>> entry : personMapGroupped .entrySet()) {
String key = entry.getKey();// group by age
String value = entry.getValue(); // person count
// I want to retrieve the group by hobby here too...
}
Any advice would be appreciated.
Thank you very much
Implement methods for comparing people according to the different fields. For instance, if you want to group by age, add this method to Person:
public static Comparator<Person> getAgeComparator(){
return new Comparator<Person>() {
#Override
public int compare(Person o1, Person o2) {
return o1.age-o2.age;
}
};
}
Then you can simply call: Arrays.sort(people,Person.getAgeComparator()) or use the following code to sort a Collection:
List<Person> people = new ArrayList<>();
people.sort(Person.getAgeComparator());
To sort using more than one Comparator simultaneously, you first define a Comparator for each field (e.g. one for age and one for names). Then you can combine them using a ComparatorChain. You would use the ComparatorChain as follows:
ComparatorChain chain = new ComparatorChain();
chain.addComparator(Person.getNameComparator());
chain.addComparator(Person.getAgeComparator());
You could simply combine the attributes to a key.
for (Person person : listPerson) {
String key = person.getAge() + ";" + person.getHobby();
if (!personMapGrouped.contains(key)) {
personMapGrouped.put(key, new ArrayList<Person>());
}
personMapGrouped.get(key).add(person);
}
The count of entries is easy to determine by using personMapGrouped.get("18;Football").getSize().
I'm not sure about your requirements, but I'd probably use multiple maps (Google Guava's Multimap would make that easier btw) and sets, e.g. something like this:
//I'm using a HashMultimap since order of persons doesn't seem to be relevant and I want to prevent duplicates
Multimap<Integer, Person> personsByAge = HashMultimap.create();
//I'm using the hobby name here for simplicity, it's probably better to use some enum or Hobby object
Multimap<String, Person> personsByHobby = HashMultimap.create();
//fill the maps here by looping over the persons and adding them (no need to create the value sets manually
Since I use value sets Person needs a reasonable implementation of equals() and hashCode() which might make use of the id field. This also will help in querying.
Building subsets would be quite easy:
Set<Person> age18 = personsByAge.get(18);
Set<Person> basketballers = personsByHobby.get( "basketball" );
//making use of Guava again
Set<Person> basketballersAged18 = Sets.intersection( age18, basketballers );
Note that I made use of Google Guava here but you can achieve the same with some additional manual code (e.g. using Map<String, Set<Person>> and manually creating the value sets as well as using the Set.retainAll() method).
I have a table called person which stores the details of a person in the application. To map to my application, I have a class called Person with the corresponding set of properties mapping to columns of the table.
One of the attributes of a person is the hobbies of that person, which can be multiple. I have also a master table for hobbies called hobby.
Naturally it's a many-to-many relationship which should be modelled by using a third table, something like person_hobby. But my application use case only requires listing down the hobbies of a person on kind of his/her profile page. So instead of making a third table, I have put a comma separated string of hobbies which I can split up in the application and iterate over to show it on the page.
But I want in my pojo Person it should be modelled as a List<String> rather than a single String with comma separated values and have the getter and setter accordingly.
How should I go about mapping it in the right way.
One thing that I thought of was creating another property hobbiesStr in the Person class and map it to the column hobbies and duplicate setters and getters for a List<String>, so that the getter of List would manipulate the csv of string and create a list on the fly. But then how do I cope with hibernate using setter for the string and getter for the list?
You can map a comma separated list with the code below. I doubt you can map it also with your hobbies table. I mean you are not going to have a foreign key or anything that can automatically check if all the items in the list have an appropriate record in the hobbies table. Also if you create a record for a person and provide some hobbies from your hobbies table after saving the person record, nothing will prevent from removing all the records from your hobbies table even if you have a person who has those hobbies in his/her list.
private String hobbies;
public List<String> getHobbies() {
if (null == hobbies) {
return null;
}
String[] hobbiesArray = hobbies.split(",");
List<String> result = new ArrayList<>(hobbiesArray.length);
for(String hobby: hobbiesArray) {
result.add(hobby);
}
return result;
}
public void setHobbies(List<String> hobbies) {
if (null == hobbies) {
return;
}
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> iterator = hobbies.iterator();
while (iterator.hasNext()) {
stringBuilder.append(iterator.next());
if(!iterator.hasNext()) {
break;
}
stringBuilder.append(",");
}
this.hobbies = stringBuilder.toString();
}
Taking into consideration all the mess this approach can bring, I would think twice before I decide to go this way
I'm new to Java. I'm wondering what will be the best option to store 2D array with different type of data.
It will be table of countries, each has capital and is in cotinent. Then I have to store it this way:
ContinentID | Country name | Capital
What to choose?
You might want to consider making a Country class to hold this data, and then maintaining a list/array of instances of this class.
public class Country {
private int continentId;
private String name;
private String capital;
public Country(int continentId, String name, String capital) {
this.continentId = continentId;
this.name = name;
this.capital = capital;
}
// ...
}
You would then have something along the lines of
List<Country> countries = new ArrayList<Country>();
countries.add(new Country(123, "USA", "Washington DC"));
...
create a country class with the needed attributes, then create a list, type it as country:
list<Country> clist = new ArrayList<Country>();
or any list you want. Now just store country objects in the list.
If continent id is just a sequence and does not add any specific meaning, you might want to consider a HashMap with key as country name and value as Capital. If order is important, consider a LinkedHashMap.
If continent id does carry meaning, then you might want to consider moving all the variables to a class, say Country and hold it in a list. If you are planning to retrieve by country name and not iterate, you might want to consider storing the objects in a Hashmap, with key as your country name or capital or whatever suits your need. The reason for a HashMap and not a list is that membership check on a List gives linear performance as against a constant time access on a HashMap.
HashMap<Integer, HashMap<String, String>>();