Sorting TreeMaps by value - java

I'm looking to sort a TreeMap of Customer objects by a specific Customer property.
The TreeMap is defined as so:
private TreeMap<Long,Customer> customerMap = new TreeMap<>();
Long is the type of ID for the customers stored.
I wrote a function to create a new TreeMap and pass a Comparator to its constructor which gets map entries, their values, that compares the specific field.
public Customer[] getCustomersByName() {
TreeMap<Long,Customer> sortByName = new TreeMap<> (
new Comparator<Map.Entry<Long,Customer>>() {
#Override public int compare(Map.Entry<Long,Customer> cus1, Map.Entry<Long,Customer> cus2) {
return cus1.getValue().getLastName().compareTo(cus2.getValue().getLastName());
}
}
);
sortByName.putAll(customerMap);
// sortByName to Customer[] and return.
}
This doesn't work and throws: Cannot infer type arguments for TreeMap<>Java(16778094) at Line 2.
Perhaps, the issue is that the Comparator takes <Map.Entry<K,V>> to compare a TreeMap<K,V> and that is the issue.
How would I fix this though to sort by values but keep the customerMap type as is?
I know that TreeMaps are only sorted by keys. Is there a better data structure for this job perhaps so that I could store a bunch of Customer objects and sort them by different Customer properties without the operation being too expensive (not polynomial preferably)?

Set up a second TreeMap, using the customers last name as the key:
TreeMap<String,Customer> sortByName = new TreeMap<>();
TreeMap<Long,Customer> sortByID = new TreeMap<>();
----------------
sortByName.put(customer.getLastName(), customer);
sortByID.put(new Long(customer.getID()), customer);
----------------
return sortByName.values().toArray( new Customer[sortByName.size()] );
'''

This is rather easy with streams:
Customer[] cust =
customerMap.values()
.stream()
.sorted(Comparator.comparing(Customer::getName))
.toArray(Customer[]::new);
You only need to sort the values according to your example, so why bother sorting the TreeMap in reverse, when the only thing you care about is a sorted (by name) Customer[]?

Related

Have to display the number of times an element has been added in my map JAVA

I've created a TreeMap with products.
And I want to count the number of times they repeat themselves, but have no clue what to code. Any hints? (I expect no code, just suggestions)
private static Map<Integer, String> shoppingCart() {
Map<Integer, String> result = new TreeMap<>();
result.put(1, "sausage");
result.put(2, "sausage");
result.put(3, "soup");
result.put(4, "egg");
result.put(5, "egg");
result.put(6, "tomato");
result.put(7, "sausage");
return result;
}
I was thinking about adding a counting variable, but still it doesn't fix the repeating problem.
Maybe not the best approach, but without modifying what you already have, you could use another map to store the products as keys and the quantity as the value for those keys:
Map<Integer, String> result = shoppingCart();
Map<String, Integer> productQuantities = new HashMap<>();
result.values().forEach(value ->
productQuantities.put(value,productQuantities.getOrDefault(value, 0) + 1));
To print the resulting map:
productQuantities.forEach((key, value) -> System.out.println(key + ":" + value));
I created a TreeMap with products, and i want to count the number of times they repeat themselves
Probably a different type of Map with keys representing items and values representing the corresponding count would be more handy. Something like:
NavigableMap<String, Integer> countByItem
Note: in order to access methods of the TreeMap like ceilingKey(), floorKey(), higherEntry(), lowerEntry(), etc. which are not defined in the Map interface you need to use NavigableMap as a type.
And it might make sense to make the item to be a custom object, instead of being a String. That would guard you from making typo, and it provides a possibility to gives useful behavior to Item
public class Item {
private int id;
private String name;
// constructor, getters, equals/hashCode, ect.
}
That's how map of items can be updated using Java 8 method merge(), which expects a key, a value and a function responsible for merging the old value and the new one:
NavigableMap<Item, Integer> countByItem = new TreeMap<>(Comparator.comparingInt(Item::getId));
countByItem.merge(new Item(1, "sausage"),1, Integer::sum);
countByItem.merge(new Item(1, "sausage"),1, Integer::sum);
countByItem.merge(new Item(2, "soup"),1, Integer::sum);
If you don't feel very comfortable with Java 8 functions, instead of merge() you can use combination of methods put() & getOrDefault():
Item sausage = new Item(1, "sausage");
countByItem.put(sausage, countByItem.getOrDefault(sausage, 0) + 1);
I can only guess at your goal. In your Map <Integer, String>, what does the Integer represent? Product number? Quantity? Sequence number? Something else?
If the Integer represents quantity, you have it backwards. It should be Map <String, Integer>. In a Map<X,Y>, the X represents the key. A Map allows fast lookup by the key. The Y is the value -- the information you want to find for a particular key, if the key is in the Map.
For example, if you want to add "sausage", you want to check if it is in the Map. If it isn't, put it into the Map with quantity 1. If it is, retrieve it and update the quantity.
If the Integer represents a sequence number (1st item, 2nd item, 3rd item, ...), you don't need a Map. Consider using an array or a data structure that preserves order, such as a List.
However, using an array or List still leaves you with the problem of how find how many of each item are in the list, when duplicates are allowed, as they are in your problem. To solve that, consider a Map<String, Integer> where the Integer (map value) is the quantity, and the String (map key) is the product name.
If I were doing this, I'd create classes to allow me to glue together related information. Here is part of a hypothetical example, which might be more realistic than you need:
public class Product {
private int upc; // product code, often represented with bar code
private Decimal price;
private String description;
private String shortDescription;
private ProductClass prodClass; // department, taxable, etc.
// etc. -- add needed fields, or remove irrelevant
// constructors, getters, setters,
Override .equals and .hashcode in Product. You use the UPC for those.
If you use implements Comparable<Product>, you have the possibility of using binary search, or a search tree.
public class Receipt {
private Decimal total;
private Decimal taxableTotal;
private Map <Product,Integer> shoppingCart; // Product, Quantity
// etc.
When each item is scanned, you can lookup the Product in the Map, and add it if not found, or update the quantity if found, as in the previous answers.

Dynamic order by with java with list

I want to sort my list(taken from the db I can't sort using query), but I don't know for what parameters, the parameters are chosen by the user. The following code sorts by first and last name in desc, but how do I create a dynamic one? I have made many attempts, but I have failed.
myList.sort(Comparator.comparing(DTO::getName)
.thenComparing(DTO::getLastName).reversed());
To create a customized order depending on the user params, I suggest something like :
1- Create an enumeration of the possible sorting orders which will be populated by the parameters in the user query, say that, for example, I have 3 orders BY_ID, BY_NAME, BY_LAST_NAME.
2- Then create a list of this enumeration;
3- After that create a map, where the key is the enumeration above, and the value is a comparator.
4- populate the map.
5-Derive a reduced final comparator from both the list of orders you had in (2), and the map in (3).
6- Sort your list by that final comparator.
Putting all the steps together
The enumeration of the sort orders :
public enum SortOrder {
BY_ID,
BY_NAME,
BY_LAST_NAME;
}
The main test method :
public static void main(String[] args) {
/**dummy objects for test purposes**/
DTO firstDTO = new DTO();
firstDTO.setId(1);
firstDTO.setName("John");
firstDTO.setLastName("Doe");
DTO secondDTO = new DTO();
secondDTO.setId(2);
secondDTO.setName("John");
secondDTO.setLastName("Doe");
DTO thirdDTO = new DTO();
thirdDTO.setId(2); // the id is repeated
thirdDTO.setName("Alice");
thirdDTO.setLastName("Bob");
final List<SortOrder> sortOrders = createSortOrdersFromInputParams(/*the params passed as args*/);
final List<DTO> myList = Arrays.asList(firstDTO, secondDTO, thirdDTO); // your list that is supposed to come from a DB
final Map<SortOrder, Comparator<DTO>> map = new EnumMap<>(SortOrder.class);
final Comparator<DTO> defaultComparator = Comparator.nullsLast(Comparator.comparing(DTO::getId)); // you'all always need a default comparator , say by ID
map.put(SortOrder.BY_ID, defaultComparator);
map.put(SortOrder.BY_NAME, Comparator.nullsLast(Comparator.comparing(DTO::getName)));
map.put(SortOrder.BY_LAST_NAME, Comparator.nullsLast(Comparator.comparing(DTO::getLastName)));
final Comparator<DTO> finalComparator = sortOrders.stream()
.map(map::get)
.filter(Objects::nonNull)
.reduce(Comparator::thenComparing)
.orElse(defaultComparator);
myList.sort(finalComparator);
System.out.println(myList);
}
The method that turns parameters of the user input into sort orders
private static List<SortOrder> createSortOrdersFromInputParams(/*params passed as args*/) {
return Arrays.asList(SortOrder.BY_ID, SortOrder.BY_NAME, SortOrder.BY_LAST_NAME);
}
I hope this will respond to your case.

JAVA : Best performance-wise method to find an object stored in hashMap

I have a bunch of objects stored in hashMap<Long,Person> i need to find the person object with a specific attribute without knowing its ID.
for example the person class:
public person{
long id;
String firstName;
String lastName;
String userName;
String password;
String address;
..
(around 7-10 attributes in total)
}
lets say i want to find the object with username = "mike". Is there any method to find it without actually iterating on the whole hash map like this :
for (Map.Entry<Long,Person> entry : map.entrySet()) {
if(entry.getValue().getUserName().equalsIgnoreCase("mike"));
the answers i found here was pretty old.
If you want speed and are always looking for one specific attribute, your best bet is to create another 'cache' hash-map keyed with that attribute.
The memory taken up will be insignificant for less than a million entries and the hash-map lookup will be much much faster than any other solution.
Alternatively you could put all search attributes into a single map (ie. names, and ids). Prefix the keys with something unique if you're concerned with collisions. Something like:
String ID_PREFIX = "^!^ID^!^";
String USERNAME_PREFIX = "^!^USERNAME^!^";
String FIRSTNAME_PREFIX = "^!^FIRSTNAME^!^";
Map<String,Person> personMap = new HashMap<String,Person>();
//add a person
void addPersonToMap(Person person)
{
personMap.put(ID_PREFIX+person.id, person);
personMap.put(USERNAME_PREFIX+person.username, person);
personMap.put(FIRSTNAME_PREFIX+person.firstname, person);
}
//search person
Person findPersonByID(long id)
{
return personMap.get(ID_PREFIX+id);
}
Person findPersonByUsername(String username)
{
return personMap.get(USERNAME_PREFIX+username);
}
//or a more generic version:
//Person foundPerson = findPersonByAttribute(FIRSTNAME_PREFIX, "mike");
Person findPersonByAttribute(String attr, String attr_value)
{
return personMap.get(attr+attr_value);
}
The above assumes that each attribute is unique amongst all the Persons. This might be true for ID and username, but the question specifies firstname=mike which is unlikely to be unique.
In that case you want to abstract with a list, so it would be more like this:
Map<String,List<Person>> personMap = new HashMap<String,List<Person>>();
//add a person
void addPersonToMap(Person person)
{
insertPersonIntoMap(ID_PREFIX+person.id, person);
insertPersonIntoMap(USERNAME_PREFIX+person.username, person);
insertPersonIntoMap(FIRSTNAME_PREFIX+person.firstname, person);
}
//note that List contains no duplicates, so can be called multiple times for the same person.
void insertPersonIntoMap(String key, Person person)
{
List<Person> personsList = personMap.get(key);
if(personsList==null)
personsList = new ArrayList<Person>();
personsList.add(person);
personMap.put(key,personsList);
}
//we know id is unique, so we can just get the only person in the list
Person findPersonByID(long id)
{
List<Person> personList = personMap.get(ID_PREFIX+id);
if(personList!=null)
return personList.get(0);
return null;
}
//get list of persons with firstname
List<Person> findPersonsByFirstName(String firstname)
{
return personMap.get(FIRSTNAME_PREFIX+firstname);
}
At that point you're really getting into a grab-bag design but still very efficient if you're not expecting millions of entries.
The best performance-wise method I can think of is to have another HashMap, with the key being the attribute you want to search for, and the value being a list of objects.
For your example this would be HashMap<String, List<Person>>, with the key being the username. The downside is that you have to maintain two maps.
Note: I've used a List<Person> as the value because we cannot guarantee that username is unique among all users. The same applies for any other field.
For example, to add a Person to this new map you could do:
Map<String, List<Person>> peopleByUsername = new HashMap<>();
// ...
Person p = ...;
peopleByUsername.computeIfAbsent(
p.getUsername(),
k -> new ArrayList<>())
.add(p);
Then, to return all people whose username is i.e. joesmith:
List<Person> matching = peopleByUsername.get("joesmith");
Getting one or a few entries from a volatile map
If the map you're operating on can change often and you only want to get a few entries then iterating over the map's entries is ok since you'd need space and time to build other structures or sort the data as well.
Getting many entries from a volatile map
If you need to get many entries from that map you might get better performance by either sorting the entries first (e.g. build a list and sort that) and then using binary search. Alternatively you could build an intermediate map that uses the attribute(s) you need to search for as its key.
Note, however, that both approaches at least need time so this only yields better performance when you're looking for many entries.
Getting entries multiple times from a "persistent" map
If your map and its valuies doesn't change (or not that often) you could maintain a map attribute -> person. This would mean some effort for the initial setup and updating the additional map (unless your data doesn't change) as well as some memory overhead but speeds up lookups tremendously later on. This is a worthwhile approach when you'd do very little "writes" compared to how often you do lookups and if you can spare the memory overhead (depends on how big those maps would be and how much memory you have to spare).
Consider one hashmap per alternate key.
This will have "high" setup cost,
but will result in quick retrieval by alternate key.
Setup the hashmap using the Long key value.
Run through the hashmap Person objects and create a second hashmap (HashMap<String, Person>) for which username is the key.
Perhaps, fill both hashmaps at the same time.
In your case,
you will end up with something like HashMap<Long, Person> idKeyedMap and HashMap<String, Person> usernameKeyedMap.
You can also put all the key values in the same map,
if you define the map as Map<Object, Person>.
Then,
when you add the
(id, person) pair,
you need to also add the (username, person) pair.
Caveat, this is not a great technique.
What is the best way to solve the problem?
There are many ways to tackle this as you can see in the answers and comments.
How is the Map is being used (and perhaps how it is created). If the Map is built from a select statement with the long id value from a column from a table we might think we should use HashMap<Long, Person>.
Another way to look at the problem is to consider usernames should also be unique (i.e. no two persons should ever share the same username). So instead create the map as a HashMap<String, Person>. With username as the key and the Person object as the value.
Using the latter:
Map<String, Person> users = new HashMap<>();
users = retrieveUsersFromDatabase(); // perform db select and build map
String username = "mike";
users.get(username).
This will be the fastest way to retrieve the object you want to find in a Map containing Person objects as its values.
You can simply convert Hashmap to List using:
List list = new ArrayList(map.values());
Now, you can iterate through the list object easily. This way you can search Hashmap values on any property of Person class not just limiting to firstname.
Only downside is you will end up creating a list object. But using stream api you can further improve code to convert Hashmap to list and iterate in single operation saving space and improved performance with parallel streams.
Sorting and finding of value object can be done by designing and using an appropriate Comparator class.
Comparator Class : Designing a Comparator with respect to a specific attribute can be done as follows:
class UserComparator implements Comparator<Person>{
#Override
public int compare(Person p1, Person p2) {
return p1.userName.compareTo(p2.userName);
}
}
Usage : Comparator designed above can be used as follows:
HashMap<Long, Person> personMap = new HashMap<Long, Person>();
.
.
.
ArrayList<Person> pAL = new ArrayList<Person>(personMap.values()); //create list of values
Collections.sort(pAL,new UserComparator()); // sort the list using comparator
Person p = new Person(); // create a dummy object
p.userName="mike"; // Only set the username
int i= Collections.binarySearch(pAL,p,new UserComparator()); // search the list using comparator
if(i>=0){
Person p1 = pAL.get(Collections.binarySearch(pAL,p,new UserComparator())); //Obtain object if username is present
}else{
System.out.println("Insertion point: "+ i); // Returns a negative value if username is not present
}

Create lists with java

I am very new in java. I want to create a kind of complex array. I think its called list/collection/map...
My data looks like
-item
ref:GH987
size:22
date:1992
-item
ref:98KJ
size:27
date:2000
-item
ref:ZXJ212
size:24
date:1999
I do not prefer to create an item Class and store the 3 instance in an array.
I found something called Map but it really confused me and I don't understand how could I access the values after creating the data. Could you help me how to start with this ?
final Map<String, List<String>> data = new LinkedHashMap<String, List<String>>();
data.put("item", new LinkedList<String>());
You have to create an Item class, that's the whole point of OOP!
Very minimal example:
public class Item {
public String ref;
public int size;
public int date;
public Item(String ref, int size, int date) {
this.ref = ref;
this.size = size;
this.date = date;
}
}
Then it's just a List<Item> and you can access each part with myList.get(i).ref etc:
List<Item> l = new ArrayList<>();
l.add(new Item("GH987", 22, 1992));
l.add(new Item("98KJ", 27, 2000));
...
for (Item it : l)
System.out.println("Ref: "+item.ref+", size: "+item.size+", date: "+item.date);
Now, if you really want to use a Map to store each attribute, you have to think what would be your unique key. Let's suppose it's ref, which is a String:
Map<String,Integer> sizes = new LinkedHashMap<>(); // LinkedHashMap keeps the insert order
Map<String,Integer> dates = new LinkedHashMap<>();
sizes.put("GH987", 22);
dates.put("GH987", 1992);
sizes.put("98KJ", 27);
dates.put("98KJ", 2000);
then it's difficult to access all members as they're not bundled in a single instance:
String ref = "GH987";
System.out.println("Ref: "+ref+", size: "+sizes.get(ref)+", date: "+dates.get(ref))
Here you should realize that if a Map hasn't been updated, it will return null on the value and you'll have to handle consistency yourself. It is also a pain to create so many objects just to store single attributes, which in your case are Number subclasses (e.g. Integer) instead of native types, which are far more efficient.
So do yourself a favor, and create your Item class. Then you can use a Map to quickly access a particular item based on its key, which looks like the ref member:
myMap.put(ref, new Item(ref, size, date));
Item it = myMap.get(ref);
...
Yes you can choose Map with a class and having it's reference as a key for ex
Map<String,Item> map = new HashMap<>();
Assuming reference is unique. You can store values like
map.put(item.getReference(),item);

Which Map class should I use for data consisting of different types?

It seems that HashMap is limited to only one value, and I need a table of values like:
Joe(string) 25(Integer) 2.0(Double)
Steve(string) 41(Integer) 1.6(Double)
etc.
I want to store infomation similarly as in two-dimensional array, but I want it to have different variable types. I've look at various Map-implementing classes, but it seems that they only store value (assigned to a key), or two variables (I need at least three). What class should I use for this?
It sounds like you should be creating a separate class with a String field, an int field and a double field.
Then you can create a map with that as the value type, and whatever type you like as a key. For example:
Map<String, Person> map = new HashMap<>();
// What keys do you really want here?
map.put("foo", new Person("Joe", 25, 2.0));
map.put("bar", new Person("Steve", 41, 1.6));
Or it's possible that you don't even need a map at all at that point:
List<Person> list = new ArrayList<>();
list.add(new Person("Joe", 25, 2.0));
list.add(new Person("Steve", 41, 1.6));
Make class representing data you want to store, eg.
class Person {
String name;
//rest
}
and then make map like Map. Type of map is irrelevant
I would suggest that you create a simple class that stores the integer and double pair, which is then mapped to a String (I assume this is the desired outcome).
HashMap<String, Pair<Integer, Double>> map = new HashMap<String, Pair<Integer, Double>>;
map.put("Steve", new Pair<Integer, Double>(41, 1.6));
Where Pair is defined as
class Pair<T, K> {
public T val1;
public K val2;
public Pair(T val1, K val2){
this.val1 = val1;
this.val2 = val2;
}
}
There are a number of ways to do this.
The best way is the way that is suggested by Jon Skeet and #novy1234. Create a custom class that represents a person (or whatever the rows of the table are). Then use either a Map or a List of that class to represent the "table". (The Map allows you to select one of the fields / columns as a key ... if that is appropriate.)
So you might end up with a HashMap<String, Person> or an ArrayList<Person> ... where Person is your custom class.
A second way would be to represent each row as a Map<String,Object> so that (for example) "name" maps to "Joe", "age" maps to 25 and "height" maps to 2.0. (He is tall.) Then the table could be either a Map or a List of those maps.
A variation of the second way would be a Map<String, Map<String, Object>> where the keys of the outer map are each person's name, the keys of the inner map are the field names; e.g. "age" and "height".
However using a Map<String, Object> to represent a row is not a good Java solution when the set of columns is known. A custom class will use significantly less space than a Map (of any flavour), and a regular getter method is orders of magnitude faster that a Map.get(key) method. In addition, the Map.get(...) method is going to return you an Object that has to be cast to the expected type before it can be used. There is a risk that the typecast will fail at runtime, because you have (somehow) populated the row / map incorrectly.
You should only contemplate using a Map to represent a row in the table if the columns are not known at compile time, or if there are an unmanageably number of columns that are populated sparsely. (Neither is the case here ...)
So, which Map class should you use?
Your alternatives include HashMap, TreeMap, LinkedHashMap and ConcurrentHashMap. Each one has different properties and different target use-cases. However, if your table is small, and in the absence of specific requirements, it probably makes no real difference.
Make a node to store both the integer and double values?

Categories