I have a List of Stat objects and each Stat has a Date field. This list may be very large. What I would like to do is organize these objects into date ranges (weeks to be specific) efficiently. So, if two objects occur in the same week, they go into the same List. For simplicity, the 'week' intervals are already defined so you don't have to worry about coming up with the ranges.
So here is my current approach. I have a LinkedHashMap<Date, List<Stat>> that contains all the dates I need in ascending order and each entry is initialized with an empty ArrayList. I am thinking for each Stat, iterating through the entire entrySet of the Map, and keeping track of the closest Date that the stat is also greater than or equal to. Then it would be something like map.get(closestDate).add(stat).
It seems like there should be a better way to do this. Perhaps create something like a findClosestKeyGreaterThanOrEqualTo so that I don't have to iterate through the entire map every time?
Any ideas?
You can make use of a NavigableMap (like TreeMap) which has very useful methods exactly for this purpose.
Initialize your map with all the values that you have computed and empty lists for each:
NavigableMap<Date, List<Stat>> map = new TreeMap<>();
// add all your weekly values associated with empty lists
Then for each of your Stat objects, find the proper list simply by:
final Entry<Date, List<Stat>> entry = map.floorEntry(stat.getDate());
entry.getValue().add(stat);
Thanks for your answers.
I am going to go with a NavigableMap as the floorKey function is exactly what I need.
Here is what I would do, assuming you can already convert Date into a weekId:
Create a Map<WeekId, List<Stat>>
Iterate through your original List<Stat> once and in each cycle calculate weekId from the date, then add Stat to an appropriate List by retrieving it from the Map by weekId (create one if it does not exist and add it to the map)
You should have a desired result in the map at the end of the loop.
Make sure all dates are rounded to the nearest week. Then use a getWeek(stat) function and insert the stat into the map there. A simple HashMultimap will do.
You need to deal with the issue of hash codes and equality. Say I have a data type
class Pair {
public int x;
public int y;
// obvious constructor
}
Observe:
new Pair(1,2) == new Pair(1,2) // false
new Pair(1,2).equals(new Pair(1,2)) // false
And if you use them as keys in a hash map they will be different keys.
You need to override equals and hashCode to overcome this.
For Date we observe:
public boolean equals(Object obj) {
return obj instanceof Date && getTime() == ((Date) obj).getTime();
}
Hashcode will be defined correspondingly. So if you key off of Date you will only get what you want if the dates agree to the milliscecond.
Therefore a
HashMultimap<Date, Stat> map;
Is fine, but you should programmatically observe the convention that all dates are rounded down to the nearest week. Then do:
Date key = roundToWeek(stat);
map.insert(key, stat);
And you should be good.
Related
I have a Map of Long and String - Map which stores my timestamp as the key and value as my data.
Map<Long, String> eventTimestampHolder = new HashMap<Long, String>();
Now I want to get 100 most recent data from the above map by looking at timestamp which is part of key and then keep on adding those data in a List of String. In general populate the 100 most recent data in a List.
What is the best way to do this? Can I use TreeMap here and it will sort my keys basis on the timestamp properly?
In general my timestamp is going to look like this - 1417686422238 and it will be in milliseconds
In case you mean by "recent" recently added, then you can try LinkedHashMap which will maintain the order of insertion. Then you can iterate over the first 100 items.
You can iterate over the map like this:
for(Long key : eventTimestampHolder.keySet()) {
String value = eventTimestampHolder.get(key);
}
For any key that can be sorted, you should use a SortedMap (unless there are other requirements making it unsuitable). A TreeMap is a sorted map. Since you need the most recent k entries, you need the largest keys first. This can be done by going through the k first keys in the map's descendingKeySet as follows, a one-liner in Java-8:
eventTimestampHolder.descendingKeySet().stream().limit(k); // in your case, k = 100
If you want not just the keys, but the values as well, then you could find the k'th key, and then use
// the 2nd arg is a boolean indicating whether the k'th entry will be included or not
eventTimestampHolder.tailMap(kthTimestamp, true);
One thing to remember when using tailMap is that it will be backed by the original eventTimestampHolder map, and any changes to that will be reflected in the returned tail map.
I want a data structure that contain the following:
Two Objects that represent a timeslot (e.g. LocalDateTime in Java 8)
A boolean variable
The Timeslot needs to be connected to a boolean. The program needs to know if a timeslot is available. It will be used in a knapsack problem solving algorithm for work scheduling.
What I've got so far:
ArrayList<Map<List<LocalDateTime>, Boolean>>
But it looks pretty complicated and a Map might not be the best way to iterate through if I don't know the key. I tought about a ArrayList instead of a Map but I don't know how to initialize it with different Data Types.
Table from Google Guava sounds like a perfect fit for this.
Table<LocalDateTime, LocalDateTime, Boolean> dateTable
= TreeBasedTable.create();
The reason for it is that it gives you access to row, column, and value (in parameter order), and will allow you to do relatively straightforward lookups.
An example: If you want to find all of the values for a given LocalDateTime row, then you would do this:
LocalDateTime today = LocalDateTime.of(2014, Month.JULY, 26, 0, 0);
// prints a map of all of today's values
System.out.println(dateTable.row(today));
For example I have the following file
B 01-02-2013 1233
B 03-02-2013 129
B 04-02-2013 13
the date is stored as a String from user input. The next number is a integer for "CaloriesBurnt"
I want to if given a start date and end date. CALCULATE TOTAL CALORIES BURNED between those two dates. So for example here I would want to total the calories from 01-02-2013 TO 04-02-2013. So i need to pull the calories for the dates inbetween that period. I Know I have to convert to date format and then perhaps use a HASP MAP? to order?? any help really is appreciated
The best data structure in that situation is a TreeMap. It maintains its keys sorted, and has methods returning a "submap" containing all the entries between two key values.
If each date is unique, you can use a TreeMap<Date, Record>. If not, then use a TreeMap<Date, List<Record>> (Record being the class used to hold all the elements of one line of your file).
Once you have the submap, you just need to iterate on all the values and sum their calories.
First of all I would parse the whole text file into objects. No matter how the date is. Each line in the text file could be something like this (of course with setters and getters and public/private and all this stuff ;):
class Entry {
Date date;
int calories;
}
Parse the text file and create a list of such objects. Then you could operate on this list. You can sort it (with a sorter-object written by you), you can search it or do what ever you want. Once you get rid of the actual text file and you have objects, it gets much easier. You can even use some third-party libraries to easily compute statistics (e.g. the average, max values or such things).
You might probably want to use a sorted collection like a SortedMap<Date, UserData> ( UserData would represent whatever data you want to store in the map), then use subMap(startDate, endDate + 1 day) to extract period. Note that endDate + 1 day is needed since the upper bound is exclusive.
Example:
SortedMap<Date, Integer> caloriesByDate = new TreeMap<>(); //we only store the calories
... //fill here
SimpleDateFormat f = new SimpleDateFormat("dd-MM-yyyy");
//get entries in the interval `[01-02-2013,03-02-2013]`.
caloriesByDate.subMap( f.parse("01-02-2013"), f.parse("04-02-2013") );
I understand that a queue is a collection type that is accessed through the head and/or tail.
I also understand that a map is a collection type the contains a number of unique keys, which each have an associated value.
What I need, is a map, with a fixed number of values, where the keys will be set once, and will not be changed after they are first set.
The hard part I can't get my head around is getting the values to act as a queue with a fixed length, which can be pushed, without affecting the key.
For example:
Initial collection (newest items at the top):
Key1, Value1
Key2, Value2
Key3, Value3
Collection after adding new value NewValue:
Key1, NewValue
Key2, Value1
Key3, Value2
So, I want the key to stay the same, and be able to add the new value, by pushing the oldest value off the end, and moving all other values further down towards the end.
If a type like this isn't available within the framework, could somebody please recommend a way to implement it.
Thank you!
EDIT:
An example of why I need to do this:
I need to be able to access the values
according to the time period it was
created in.
Example:
Key: 0 will contain a value from today
Key: -1 will contain a value from yesterday
Key: -2 will contain a value from two days ago
etc.
Only one value will be entered every
day, and one value will ALWAYS be
entered every day.
Sounds like you could use:
new LinkedHashMap<Long,V>(initialCapacity, loadFactor, false) {
protected boolean removeEldestEntry(Map.Entry<Long,V> e) {
return size() > MAX_ENTRIES;
}
};
To get or put items in this map you will need to canonicalize your timestamp, e.g., to 00:00:00.000 of the day.
An advantage of this approach is when the assumption that "one value will ALWAYS be entered every day" is violated it won't fail silently.
So, you're really talking about an index into an array.
I'd just wrap an 'ArrayList` in your object.
Your push method would also remove the "oldest" element from the end. You'd define methods such as getToday() and getPriorDay(numDaysBackFromToday)
However, you're using a lot of magic numbers there if you're not actually storing a date or a time with the data. You're hoping that the data has been loaded correctly.
Personally I'd use a timestamp associated with the data in a LinkedHashMap and iterate to find the one you want.
I've rethought my question with regards to the magic numbers, and have decided it'd be easier to use a predefined collection type with positive indexes. Then I'll simply make the push method drop the last item off the end when I reach the maximum number of values I need.
Then I'll get my class to interpret -1 as 1 to get the required value.
Thanks for your help, I was simply over-thinking things.
HashMap selections = new HashMap<Integer, Float>();
How can i get the Integer key of the 3rd smaller value of Float in all HashMap?
Edit
im using the HashMap for this
for (InflatedRunner runner : prices.getRunners()) {
for (InflatedMarketPrices.InflatedPrice price : runner.getLayPrices()) {
if (price.getDepth() == 1) {
selections.put(new Integer(runner.getSelectionId()), new Float(price.getPrice()));
}
}
}
i need the runner of the 3rd smaller price with depth 1
maybe i should implement this in another way?
Michael Mrozek nails it with his question if you're using HashMap right: this is highly atypical scenario for HashMap. That said, you can do something like this:
get the Set<Map.Entry<K,V>> from the HashMap<K,V>.entrySet().
addAll to List<Map.Entry<K,V>>
Collections.sort the list with a custom Comparator<Map.Entry<K,V>> that sorts based on V.
If you just need the 3rd Map.Entry<K,V> only, then a O(N) selection algorithm may suffice.
//after edit
It looks like selection should really be a SortedMap<Float, InflatedRunner>. You should look at java.util.TreeMap.
Here's an example of how TreeMap can be used to get the 3rd lowest key:
TreeMap<Integer,String> map = new TreeMap<Integer,String>();
map.put(33, "Three");
map.put(44, "Four");
map.put(11, "One");
map.put(22, "Two");
int thirdKey = map.higherKey(map.higherKey(map.firstKey()));
System.out.println(thirdKey); // prints "33"
Also note how I take advantage of Java's auto-boxing/unboxing feature between int and Integer. I noticed that you used new Integer and new Float in your original code; this is unnecessary.
//another edit
It should be noted that if you have multiple InflatedRunner with the same price, only one will be kept. If this is a problem, and you want to keep all runners, then you can do one of a few things:
If you really need a multi-map (one key can map to multiple values), then you can:
have TreeMap<Float,Set<InflatedRunner>>
Use MultiMap from Google Collections
If you don't need the map functionality, then just have a List<RunnerPricePair> (sorry, I'm not familiar with the domain to name it appropriately), where RunnerPricePair implements Comparable<RunnerPricePair> that compares on prices. You can just add all the pairs to the list, then either:
Collections.sort the list and get the 3rd pair
Use O(N) selection algorithm
Are you sure you're using hashmaps right? They're used to quickly lookup a value given a key; it's highly unusual to sort the values and then try to find a corresponding key. If anything, you should be mapping the float to the int, so you could at least sort the float keys and get the integer value of the third smallest that way
You have to do it in steps:
Get the Collection<V> of values from the Map
Sort the values
Choose the index of the nth smallest
Think about how you want to handle ties.
You could do it with the google collections BiMap, assuming that the Floats are unique.
If you regularly need to get the key of the nth item, consider:
using a TreeMap, which efficiently keeps keys in sorted order
then using a double map (i.e. one TreeMap mapping integer > float, the other mapping float > integer)
You have to weigh up the inelegance and potential risk of bugs from needing to maintain two maps with the scalability benefit of having a structure that efficiently keeps the keys in order.
You may need to think about two keys mapping to the same float...
P.S. Forgot to mention: if this is an occasional function, and you just need to find the nth largest item of a large number of items, you could consider implementing a selection algorithm (effectively, you do a sort, but don't actually bother sorting subparts of the list that you realise you don't need to sort because their order makes no difference to the position of the item you're looking for).