I have a nested Map being returned by the Guava Library's Table structure that is templated as follows:
Map<ComplexID1, Map<ComplexID2, MyObject>>
where ComplexID1 is my row key, ComplexID2 is my column key and MyObject holds my metadata. One of the attributes of my metadata is a JODA timestamp.
I need to sort this whole structure chronologically (or reverse chronologically) for display, newest objects created at the top, going backwards.
I was unable to find any information to sort this data structure. Could someone please provide some pointers?
Further, I tried to have the MyObject class extend Comparable and override compareTo on the JODA datetime object because I was trying to use Collections.Sort(). Unfortunately, that approach does not seem to work for me.
The solution pointed to below solves the problem perfectly. The code snippet is taken from there. Pasting it here for reference, credit to the original poster (question was under a different title, hence could not find it).
Sorting Guava tables in descending order based on values
Ordering<Table.Cell<String, FlowId, VersionData>> comparator =
new Ordering<Table.Cell<String, FlowId, VersionData>>() {
public int compare(
Table.Cell<String, FlowId, VersionData> cell1,
Table.Cell<String, FlowId, VersionData> cell2) {
return cell1.getValue().compareTo(cell2.getValue());
}
};
ImmutableTable.Builder<String, FlowId, VersionData>
sortedBuilder = ImmutableTable.builder();
for (Table.Cell<String, FlowId, VersionData> cell :
comparator.reverse().sortedCopy(tableBackedVersionStore.cellSet()))
{
sortedBuilder.put(cell);
}
return sortedBuilder.build();
Java8 solution:
List l = table.rowMap()
.entrySet()
.stream()
.flatMap(row -> row.getValue().entrySet().stream()) //Flat row map
.sorted((col1, col2) -> col2.getValue().compareTo(col1.getValue()))
.collect(Collectors.toList());
Related
I have a HashMap with key of type Double and my custom object as value.
It looks like this:
private static Map<Double, Incident> incidentHash = new HashMap<>();
The Incident object has following attributes: String date, String address, String incidentType.
Now I have a String date that I get from the user as input and I want to check if there exists any incident in the HashMap with that user inputted date. There can be many Incidents in the HashMap with the given date but as long as there's at least one Incident with the given date, I can do *
something.
I can just iterate over all the values in the HashMap and check if a given date exists but I was wondering if there is any better and more efficient way possible without modifying the data structure.
Given your HashMap, NO, there is not another way of doing so without iterating that HashMap.
As for changing the structure, you could do as Map<String, List<Incident>> that way you would have a date as key and a List of incidents for that date, given your requirement: There can be many Incidents in the HashMap with the given date.
So this would be a O(1)
//considering that the key is added when you have at least one incident
if (yourHash.get("yourDateStringWhatEverTheFormatIs") != null)
You can use streams API (from Java8) as shown in the below code with inline comments:
String userInput="10-APR-2017";
Optional<Map.Entry<Double, Incident>> matchedEntry =
incidentHash.entrySet().stream().
//filter with the condition to match
filter(element -> element.getValue().getDate().equals(userInput)).findAny();
//if the entry is found, do your logic
matchedEntry.ifPresent(value -> {
//do something here
});
If you are looking for something prior to JDK1.8, you can refer the below code:
String userInput="10-APR-2017";
Set<Map.Entry<Double, Incident>> entries = incidentHash.entrySet();
Map.Entry<Double, Incident> matchedEntry = null;
for(Iterator<Map.Entry<Double, Incident>> iterator = entries.iterator();
iterator.hasNext();) {
Map.Entry<Double, Incident> temp = iterator.next();
if(temp.getValue().getDate().equals(userInput)) {
matchedEntry = temp;
break;
}
}
You can use a TreeMap with your custom Comparator. In your Comparator compare the values of dates.
You would have to iterate through the map until you find a data that matches. Since you only need to know if any occurrences exist you can simply exit the loop when you find a match instead of iterating the rest of the map.
You can only keep a second Hash/TreeMap that matches the attribute to the object, so you can also check this attibute qickly. But you have to curate one such map for each attribute you want to access quickly. This makes it a bit more complex and use more memory, but can be much much faster.
If this is not an option the stream API referenced in other answers is a nice and tidy way to iterate over all objects to search for an attribute.
private static Map<Double, Incident> incidentHash = new HashMap<>();
private static Map<String, List<Incident>> incidentsPerDayMap = new HashMap<>();
Given that you don't want to iterate the Map and currently it's the only way to get the required value, I would recommend recomment another Map that contains Date as key and List<Incident> as value. It can be a TreeMap, e.g.:
Map<Date, List<Incident>> incidents = new TreeMap<>();
You can put the entry in this Map whenever an entry is added into the original Map, e.g.:
Incident incident = ;// incident object
Date date; //Date
incidents.computeIfAbsent(date, t -> new ArrayList<>()).add(incident);
Once the user inputs the Date, you can get all the incidents belonging to this date just by incidents.get(). Although that will give you a list and you still need to iterate over it, it will contain a lot less elements and get method in TreeMap will guarantee you log n complexity as it is sorted. So, your search operation will be much more efficient.
I'm relatively new to Java and I have a question about what type of data structure would be best for my case. I have a set of data which are essentially key-value pairs, however each value may correspond to multiple keys and each key may correspond to multiple values. A simplified example would be:
Red-Apple
Green-Apple
Red-Strawberry
Green-Grapes
Purple-Grapes
Considering the above example, I need to be able to return what color apples I have and/or what red fruits I have. The actual data will generated dynamically based upon an input file where each set will be anywhere from 100-100,000 values and each value may correspond to hundreds of values in the other set.
What would be the most efficient way to store and parse this data? I would prefer a solution as native to java as possible rather than something such as an external database.
This question is related, but I'm not sure how to apply the solution in my case given that I would need to assign multiple values to each key in both directions.
As you can't have duplicate keys in a Map, you can rather create a Map<Key, List<Value>>, or if you can, use Guava's Multimap.
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("Red", "Apple");
multimap.put("Red", "Strawberry");
System.out.println(multimap.get("Red")); // Prints - [Apple, Strawberry]
But the problem is you can't ask for the keys of a given object, I'll keep looking and make and edit if I find something else, hope it helps.
Still, you can make the reverse yourself by iterating the map and finding the keys for the object.
I suggest you use Guava's Table structure. Use color as your row keys and fruit as your column key or the other way round. Specifically, HashBasedTable is well suited for your case.
As per your use case, you wouldn't need to store anything for the values. However, these Tables don't allow null values. You could use a dummy Boolean or any other statistical useful value, i.e. date and time of insertion, user, number of color/fruit pairs, etc.
Table has the methods you need, such as column() and row(). Bear in mind that the docs say that these structures are optimized for row access. This might be OK for you if you plan to access by one key more than by the other.
You can create your own custom data structure
public class MultiValueHashMap<K, V> {
private HashMap<K, ArrayList<V>> multivalueHashMap = new HashMap<K, ArrayList<V>>();
public static void main(String[] args) {
MultiValueHashMap<String, String> multivaluemap = new MultiValueHashMap<String, String>();
multivaluemap.put("Red", "Apple");
multivaluemap.put("Green", "Apple");
multivaluemap.put("Red", "Strawberry");
multivaluemap.put("Green", "Grapes");
multivaluemap.put("Purple", "Grapes");
for(String k : multivaluemap.keySet()){
System.out.println(k + " : " + multivaluemap.get(k).toString());
}
}
public void put(K key, V value){
if (multivalueHashMap.containsKey(key)){
ArrayList<V> values = multivalueHashMap.get(key);
values.add(value);
}else{
ArrayList<V> values = new ArrayList<V>();
values.add(value);
multivalueHashMap.put(key, values);
}
}
public Set<K> keySet(){
return multivalueHashMap.keySet();
}
public ArrayList<V> get(K key){
return multivalueHashMap.get(key);
}
}
The output should be
Red : [Apple, Strawberry]
Purple : [Grapes]
Green : [Apple, Grapes]
In my program I have a List of Plants, each plant has a measurement (String), day (int), camera (int), and replicate number(int). I obtain a List of all plants wanted by using filters:
List<Plant> selectPlants = allPlants.stream().filter(plant -> passesFilters(plant, filters)).collect(Collectors.toList());
What I would like to do now is take all Plants that have the same camera, measurement, and replicate values. And combine them in order of day. So if I have days 1,2,3,5 I would want to find all similar plants and append the values to one plant where the getValues (function).
I added a method to Plant that appends values by just using addAll( new plant values ).
Is there any way of doing this without iterating through the list over and over to find the similar plants, and then sorting each time by day then appending? I'm sorry for the horrible wording of this question.
While Vakh’s answer is correct, it is unnecessarily complex.
Often, the work of implementing your own key class does not pay off. You can use a List as a key which implies a slight overhead due to boxing primitive values but given the fact that we do operations like hashing here, it will be negligible.
And sorting doesn’t have to be done by using a for loop and, even worse, an anonymous inner class for the Comparator. Why do we have streams and lambdas? You can implement the Comparator using a lambda like (p1,p2) -> p1.getDay()-p2.getDay() or, even better, Comparator.comparing(Plant::getDay).
Further, you can do the entire operation in one step. The sort step will create an ordered stream and the collector will maintain the encounter order, so you can use one stream to sort and group:
Map<List<?>, List<Plant>> groupedPlants = allPlants.stream()
.filter(plant -> passesFilters(plant, filters))
.sorted(Comparator.comparing(Plant::getDay))
.collect(Collectors.groupingBy(p ->
Arrays.asList(p.getMeasurement(), p.getCamera(), p.getReplicateNumber())));
That’s all.
Using Collectors.groupBy:
private static class PlantKey {
private String measurement;
private int camera;
private int replicateNumber;
// + constructor, getters, setters and haschode equals
}
Map<PlantKey, List<Plant>> groupedPlants =
allPlants.stream().filter(plant -> passesFilters(plant, filters))
.collect(Collectors.groupBy(p ->
new PlantKey(p.getMeasurement(),
p.getCamera(),
p.getReplicateNumber())));
// order the list
for(List values : groupedPlants.values()) {
Collections.sort(values, new Comparator<Plant>(){
#Override
public int compare(Plant p1, Plant p2) {
return p1.getDay() - p2.getDay();
}
});
}
I would group them by the common characteristics and compare similar results.
for(List<Plant> plantGroup : allPlants.stream().collect(Collectors.groupingBy(
p -> p.camera+'/'+p.measurement+'/'+p.replicate)).values()) {
// compare the plants in the same group
}
There is a function called sorted which operates on a stream
selectPlants.stream().sorted(Comparator.comparingInt(i -> i.day)).collect(Collectors.toList());
Lets say we have a class called Activity:
public class Activity {
private Project project;
}
So every Activity Object will have a Project.. Also every Project has a Unit. Two different Projects can have the same unit:
public class Project {
private String projectName;
private Unit unit;
}
And a Unit has a name as well:
public class Unit {
private String unitName;
}
So currently I have a TreeMap like this:
SortedMap<Project,List<Activity> myMap
One example may be:
projectOne -> [activityOne,activityTwo,activityThree]
projectTwo -> [activityThree,activityFouractivityFive]
projectThree -> [activityFour,activityFive,activitySix]
etc...
Now lets assume projectOne has the same unit with projectThree (unitAAA) and projectTwo has unitZZZ...
I want to sort my Map alphabetically by the unit of the project elements:
projectOne -> [...]
projectThree -> [...]
projectTwo -> [...]
How can I achieve this? I know in Stackoverflow the question is What have you tried so far?, well I am really stuck in this point so I have not really tried anything besides trying to come up with what I can even try..
A map is basically an unsorted collection but there are sorted maps as well, e.g. TreeMap. In that case provide a comparator which sorts projects based on their to the constructor:
SortedMap<Project, List<Activity>> myMap = new TreeMap<>( new Comparator<Project>() {
public int compare( Project lhs, Project rhs) {
int r = lhs.unit.unitName.compareTo(rhs.unit.unitName); //note that null checks etc. are omitted for simplicity, don't forget them in your code unless you know for sure that unit and unitName can't be null
if( r == 0 && !lhs.equals(rhs)) {
//take other properties into account for consistent behavior with equals()
//see "Update 2" below
}
return r;
}
});
Note that if you need to sort the map using a different comparator (or can't provide a comparator) you'd have to create a list using the entries of the map and sort that.
Something like this:
List<Map.Entry<Project, List<Activity>> l = new ArrayList<>(myMap.entrySet());
Collections.sort(l, new Comparator<Map.Entry<Project, List<Activity>>() {
public int compare( Map.Entry<Project, List<Activity> lhs, Map.Entry<Project, List<Activity> rhs) {
return lhs.getKey().unit.unitName.compareTo(rhs.getKey().unit.unitName);
}
});
Also note that it is not possible for a collection or sorted map to have different sort orders, i.e. you can only provide one comparator or natural ordering for the elements.
In any case you'd either have to change the sort order of the collection (e.g. by using Collections.sort(...) or, if you need to maintain multiple orders simultanously, use multiple collections (which can be sorted views to a base collection/map).
Update I'll add an example for a copy of the TreeMap:
//new TreeMap like above
SortedMap<Project, List<Activity>> copy = new TreeMap<>( new Comparator<Project>() { ... } );
copy.putAll( myMap );
Update 2
As for the comparator, note that it is required to be consistent with equals, i.e. the comparator must only return 0 if both objects are equal. Thus you'd need to take other properties of a Project into account if the units are equal. Without that, if two projects use the same unit, they are considered equal by the TreeMap and thus entries might get lost.
For more information see: What does comparison being consistent with equals mean ? What can possibly happen if my class doesn't follow this principle?
The compare method might look like this, if the project names are unique:
public int compare( Project lhs, Project rhs) {
//as above null checks etc. are omitted for simplicity's sake
int r = lhs.unit.unitName.compareTo(rhs.unit.unitName);
if( r == 0 && !lhs.equals(rhs)) {
r = lhs.projectName.compareTo( rhs.projectName );
//you could also use the natural ordering of the projects here:
//r = lhs.compareTo( rhs );
}
return r;
}
A HashMap can't be sorted, but if you want to display the contents of the map sorted, you could sort a list of your projects by unit (look for a Comparator on Project) and get the value of the corresponding project from the map.
I have the following list:
List<ArrayList> list;
list.get(i) contains the ArrayList object with the following values {p_name=set1, number=777002}.
I have to create a
Map<key,value>
where the key contains the p_name, and values are the numbers.
How to do it easily and fast as there can be hundreds of entries in the initial list and each number can be present in multiple p_name entries.
Update: Here is my current solution
List<Row> list; //here is my data
Map<String,String> map = new TreeMap<String,String>();
for (Row l : list) {
if (l.hasValues()) {
Map<String, String> values = l.getResult(); // internal method of Row interface that returns a map
String key = values.get( "number");
map.put(key, values.get( "p_name" ));
}
}
The method works, but maybe it could be done better?
PS : There is an obvious error in my design. I wonder if you find it :)
Sine the key can have more then one values, what you are looking for is a MultiMap. Multimap
Or a simple map in the form
Map<Key,ArrayList<Values>>
There is no "fast" way here to me. You still need to iterate through all the elements and check all the values.
And actually hundreds to Java is not much at all