I am trying to convert java7 program into java 8. I want below output using stream API.
public List<String> getTopThreeWeatherCondition7() {
List<String> _Top3WeatherList = new ArrayList<String>();
Map<String, Integer> _WeatherCondMap = getWeatherCondition7();
List<Integer> _WeatherCondList = new ArrayList<Integer>(_WeatherCondMap.values());
Collections.sort(_WeatherCondList, Collections.reverseOrder());
List<Integer> _TopThreeWeathersList = _WeatherCondList.subList(0, 3);
Set<String> _WeatherCondSet = _WeatherCondMap.keySet();
Integer count = 0;
for (String _WeatherCond : _WeatherCondSet) {
count = _WeatherCondMap.get(_WeatherCond);
for (Integer _TopThreeWeather : _TopThreeWeathersList) {
if (_TopThreeWeather == count) {
_Top3WeatherList.add(_WeatherCond);
}
}
}
_WeatherCondList = null;
_WeatherCondMap = null;
_TopThreeWeathersList = null;
_WeatherCondSet = null;
return _Top3WeatherList;
}
I strongly suggests to adhere to Java coding conventions. Start variable names with a lower case letter instead of _+upper case letter. Second, don’t assign local variables to null after use. That’s obsolete and distracts from the actual purpose of the code. Also, don’t initialize variables with an unused default (like the count = 0). In this specific case, you should also declare the variable within the inner loop, where it is actually used.
Note also that you are comparing Integer references rather than values. In this specific case it might work as the objects originate from the same map, but you should avoid that. It’s not clear whether there might be duplicate values; in that case, this loop will not do the right thing. Also, you should not iterate over the keySet(), just to perform a get lookup for every key, as there is entrySet() allowing to iterate over key and value together.
Since you said, this code ought to be a “Java 7 program” you should mind the existence of the “diamond operator” (<>) which removes the need to repeat type arguments when creating new instances of generic classes.
Instead of sorting the values only and searching for the associated keys, you should sort the entries in the first place.
So a clean Java 7 variant of your original code would be:
static final Comparator<Map.Entry<String, Integer>> BY_VALUE_REVERSED=
new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return Integer.compare(o2.getValue(), o1.getValue());
}
};
public List<String> getTopThreeWeatherCondition7() {
List<String> top3WeatherList = new ArrayList<>();
Map<String, Integer> weatherCondMap = getWeatherCondition7();
List<Map.Entry<String, Integer>> entryList=new ArrayList<>(weatherCondMap.entrySet());
Collections.sort(entryList, BY_VALUE_REVERSED);
List<Map.Entry<String, Integer>> topThreeEntries = entryList.subList(0, 3);
for(Map.Entry<String, Integer> entry: topThreeEntries) {
top3WeatherList.add(entry.getKey());
}
return top3WeatherList;
}
This also handles duplicates correctly. Only if there is a tie on the third place, just one of the valid candidates will be chosen.
Only if you have a clean starting point, you may look, how this can benefit from Java 8 features
Instead of copying the content to a List to sort it, you can create a Stream right from the Map and tell the stream to sort
You can create a comparator much easier, or even use one of the new builtin comparators
You can chain the task of limiting the result to three elements, map to the key and collect to the result List right to the stream of the previous steps:
public List<String> getTopThreeWeatherCondition7() {
Map<String, Integer> weatherCondMap = getWeatherCondition7();
List<String> top3WeatherList =
weatherCondMap.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(3)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return top3WeatherList;
}
Related
Any way to perform the below code using Java 8.
final Map<String, Collection<ProductStrAttributeOverrideRulesModel>> attributeRulesMap = new HashMap<String, Collection<ProductStrAttributeOverrideRulesModel>>();
for (final ProductStrAttributeOverrideRulesModel rule : rules)
{
final String key = rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier();
if (attributeRulesMap.containsKey(key))
{
final Collection<ProductStrAttributeOverrideRulesModel> currentRules = attributeRulesMap.get(key);
currentRules.add(rule);
}
else
{
final Collection<ProductStrAttributeOverrideRulesModel> list = new LinkedList<ProductStrAttributeOverrideRulesModel>();
list.add(rule);
attributeRulesMap.put(key, list);
}
}
if it is only
final Map<String, ProductStrAttributeOverrideRulesModel> attributeRulesMap
than i can do like following but i need to arrange the whole collection inside a map based on key and each key can have multiple values stored in collection.
Map<String, ProductStrAttributeOverrideRulesModel> result =
choices.stream().collect(Collectors.toMap(ProductStrAttributeOverrideRulesModel::getProductStrAttributeOverride.getProductStrTypeField.getAttributeDescriptorQualifier,
Function.identity()));
You could use groupingBy :
Map<String,List<ProductStrAttributeOverrideRulesModel>>
map =
choices.stream()
.collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier()));
And if you don't want a List, you can pass a second argument to groupingBy and specify whatever Collection you want. For example :
Map<String,Collection<ProductStrAttributeOverrideRulesModel>>
map =
choices.stream()
.collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier(),
Collectors.toCollection(HashSet::new)));
Note that it doesn’t always have to be a Stream operation. Your code would also benefit from using the “diamond operator” (though not new to Java 8) and from using new collection operations, i.e. computeIfAbsent, which allows to elide the entire conditional inside the loop and its code duplication. Putting both together, you’ll get:
final Map<String, Collection<ProductStrAttributeOverrideRulesModel>>
attributeRulesMap = new HashMap<>();
for(final ProductStrAttributeOverrideRulesModel rule: rules)
{
final String key = rule.getProductStrAttributeOverride()
.getProductStrTypeField().getAttributeDescriptorQualifier();
attributeRulesMap.computeIfAbsent(key, x->new LinkedList<>()).add(rule);
}
You could also replace the loop by a forEach invocation, if you wish:
rules.forEach(rule -> attributeRulesMap.computeIfAbsent(
rule.getProductStrAttributeOverride()
.getProductStrTypeField().getAttributeDescriptorQualifier(),
x->new LinkedList<>()).add(rule)
);
though it’s debatable whether this is an improvement over the classical loop here…
Learning Java as I go (Python background). Simple word count program in Java 7 code (can not use J8!).
I have a hash map of word:count pairs. Now I need to sort on count (decreasing order), and break ties with using word in alphabetical order.
Have read s/o and I tried a treemap but it does not seem to handle ties very well so I don't think that is right.
I have seen a lot of solutions posted that define a new class sortbyvalue and define a comparator. These will not work for me as I need to keep the solution all contained in the existing class.
I am looking for feedback on this idea:
iterate over the map entries (me) in the hashmap
use me.getKey = K and me.getValue = V
new Map.Entry reverse_me = (V,K) {not sure about this syntax}
add reverse_me to a List
repeat for all me in map
List.sort { this is where I am unsure, on how this will sort and no idea how to write a comparator. At this point each List element would be a (count, word) pair and the sort() should sort by count in decreasing and then by word in case of same counts in alphabetical order)
This would be the final output.
Is this a logical progression? I can tell from the many posts that there are many opinions on how to do this, but this is the one I can wrap my head around.
Also, can not use Guava.
You can create an List of Entry set from the map. Sort the List using Collections.sort(). You can pass the custom Comparator for sorting by Key when Value(s) are same.
Set<Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
{
public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
{
int result = (o2.getValue()).compareTo( o1.getValue() );
if (result != 0) {
return result;
} else {
return o1.getKey().compareTo(o2.getKey());
}
}
} );
This collection reflects the correct order only as long as the map entries are not changed
HashMap<String, Integer> map = new HashMap<>();
TreeSet<Map.Entry<String, Integer>> entriesSet = new TreeSet<>(new Comparator<Map.Entry<String, Integer>>(){
#Override
public int compare(Map.Entry<String, Integer> me1, Map.Entry<String, Integer> me2) {
return me1.getValue().compareTo(me2.getValue());
}
});
entriesSet.addAll(map.entrySet());
In a Java class I have a method to reOrder an existing HashMap by date. The HashMap is of a type <String, Object> where the Object contains a field called expPayDate and the key string is a sequential number turned into a string.. So I need to loop through the items in the sourceMap and find the item with the newest date then copy it to a tempMap in the correct order. My issue is what is the best way to determine the item with the newest date.
Your best bet will be to use a SortedMap with the Comparator interface.
Here is an example:
public SortedMap<String, Object> getSortedMap(Map<String, Object> originalMap) {
SortedMap<String, Object> tmpMap = new TreeMap<String, Object>(new Comparator<String>(){
#Override
public int compare(String key1, String key2) {
//logic for comparing dates
}
});
tmpMap.putAll(originalMap);
return tmpMap;
}
Use a TreeMap instead of HashMap. it will be sorted automatically on insertion.
Map< Date, Object> m = new TreeMap< Date, Object>();
Alternatively, if you have an existing HashMap and want to create a TreeMap based on it, pass it to the constructor:
Map< Date, Object> sortedMap = new TreeMap< Date, Object>(m);
Hope it will help you.
For simplicity I am assuming that type of your map is something more like Map<String, MyClass> map where MyClass has method like getDate() which returns expPayDate.
My issue is what is the best way to determine the item with the newest date.
If you want to find single map entry which value contains max date you don't need to sort entire map which at best would give you O(n*logn). What you need is simple iteration of all elements in map and comparing them with current max, which will be O(n) operation.
You can use stream() (functionality added in Java 8) and its max method. This method needs Comparator and you can easily create one by using comparing method and passing lambda expression which will return value which should be used when comparing.
So your code can look like
//import java.util.Map.Entry;
Optional<Entry<String, MyClass>> max = map.entrySet().stream()
.max(Comparator.comparing(e -> e.getValue().getDate()));
Entry<String, MyClass> entry = max.get();
MyClass maxMC = entry.getValue();
If you can't use Java 8 you can write your own method which will iterate over elements and find max. Such method can look like
public static <T> T max(Iterable<T> iterable, Comparator<T> comp) {
Iterator<T> it = iterable.iterator();
T max = null;
if (it.hasNext()) {
max = it.next();
}
while (it.hasNext()) {
T tmp = it.next();
if (comp.compare(max, tmp) < 0)
max = tmp;
}
return max;
}
and you can use it like
Comparator<Entry<String, MyClass>> myComparator = new Comparator<Entry<String, MyClass>>() {
#Override
public int compare(Entry<String, MyClass> o1, Entry<String, MyClass> o2) {
return o1.getValue().getDate().compareTo(o2.getValue().getDate());
}
};
Entry<String, MyClass> maxEntry = max(map.entrySet(), myComparator);
MyClass max = maxEntry.getValue();
Get all Entries by calling entrySet() method of Map
Create a custom Comparator to sort entries based upon values
Convert Entry set to List
Sort Entry list by using Collections.sort() method by passing your value comparator
Create a LinkedHashMap by adding entries in sorted order.
Look at example code # Sort HasMap by value
If you just need the minimum or maximum date, a simple for each loop might be sufficient:
Date maxDate = null;
for (Entry<String, Object> item: hashMap.entrySet())
if (maxDate == null || maxDate before((Date)item.getValue()))
maxDate = (Date)item.getValue();
This way complexity is only O(n) and insert and delete operations are cheaper than using a sortedMap. Anyway, I think patstuart's suggestion (using a sortedMap) is more elegant.
The correct solution depends on your performance constraints.
If your issue is just finding the item with the newest date, then if O(n) performance is OK you can do a scan of the values() in your HashMap and find the minimum that way.
It depends on how often you need to do this relative to other access on the data structure. It would be perfectly reasonable to use a SortedMap or use a secondary data structure such as a PriorityQueue (acting as a heap on the date), depending on your access patterns for this data structure.
I want to sort a Java TreeMap based on some attribute of value. To be specific, I want to sort a TreeMap<Integer, Hashset<Integer>> based on the size of Hashset<Integer>. To achieve this, I have done the following:
A Comparator class:
private static class ValueComparer implements Comparator<Integer> {
private Map<Integer, HashSet<Integer>> map = null;
public ValueComparer (Map<Integer, HashSet<Integer>> map){
super();
this.map = map;
}
#Override
public int compare(Integer o1, Integer o2) {
HashSet<Integer> h1 = map.get(o1);
HashSet<Integer> h2 = map.get(o2);
int compare = h2.size().compareTo(h1.size());
if (compare == 0 && o1!=o2){
return -1;
}
else {
return compare;
}
}
}
A usage example:
TreeMap<Integer, HashSet<Integer>> originalMap = new TreeMap<Integer, HashSet<Integer>>();
//load keys and values into map
ValueComparer comp = new ValueComparer(originalMap);
TreeMap<Integer, HashSet<Integer>> sortedMap = new TreeMap<Integer, HashSet<Integer>>(comp);
sortedMap.putAll(originalMap);
The problem:
This doesn't work when originalMap contains more than 2 values of the same size. For other cases, it works alright. When more than two values in the map are of same size, the third value in the new sorted-map is null and throws NullPointerException when I try to access it.
I can't figure out what the problem is. Woule be nice if someone could point out.
Update:
Here's an example that works when two values have the same size: http://ideone.com/iFD9c
In the above example, if you uncomment lines 52-54, this code will fail- that's what my problem is.
Update: You cannot return -1 from ValueComparator just because you want to avoid duplicate keys to not be removed. Check the contract of Comparator.compare.
When you pass a Comparator to TreeMap you compute a ("new") place to put the entry. No (computed) key can exist more than once in a TreeMap.
If you want to sort the orginalMap by size of the value you can do as follows:
public static void main(String[] args) throws Exception {
TreeMap<Integer, HashSet<Integer>> originalMap =
new TreeMap<Integer, HashSet<Integer>>();
originalMap.put(0, new HashSet<Integer>() {{ add(6); add(7); }});
originalMap.put(1, new HashSet<Integer>() {{ add(6); }});
originalMap.put(2, new HashSet<Integer>() {{ add(9); add(8); }});
ArrayList<Map.Entry<Integer, HashSet<Integer>>> list =
new ArrayList<Map.Entry<Integer, HashSet<Integer>>>();
list.addAll(originalMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer,HashSet<Integer>>>(){
public int compare(Map.Entry<Integer, HashSet<Integer>> o1,
Map.Entry<Integer, HashSet<Integer>> o2) {
Integer size1 = (Integer) o1.getValue().size();
Integer size2 = (Integer) o2.getValue().size();
return size2.compareTo(size1);
}
});
System.out.println(list);
}
Your comparator logic (which I'm not sure I follow why you'd return -1 if the set sizes are equal but they keys are different) shouldn't affect what the Map itself returns when you call get(key).
Are you positive you aren't inserting null values into the initial map? What does this code look like?
Your comparator doesn't respect the Comparator contract: if compare(o1, o2) < 0, then compare(o2, o1) should be > 0. You must find a deterministic way of comparing your elements when both sizes are the same and the integers are not identical. You could perhaps use the System.identityHashCode() of the integers to compare them in this case.
That said, I really wonder what you could do with such a map: you can't create new Integers and use them to get a value out of the map, and you can't modify the sets that it holds.
Side note: your comparator code sample uses map and data to refer to the same map.
You can have TreeMap ordered only by keys. There is no way of creating TreeMap ordered by values, because you will get StackOverflowException.
Think about it. To get an element from a tree, you need to perform comparisions, but to perform comparisions, you need to get elements.
You will have to sort it in other collection or to use Tree, you will have to encapsulate the integer (from entry value) also into the entry key and define comparator using that integer taken from a key.
Assuming you cannot use a comparator that returns 0 with a Set, this might work: Add all the elements in originalMap.entrySet() to an ArrayList and then sort the ArrayList using your ValueComparer, changing it to return 0 as necessary.
Then add all the entries in the sorted ArrayList to a LinkedHashMap.
I had a similar problem as the original poster. I had a TreeMap i wanted to sort on a value. But when I made a comparator that looked at the value, i had issues because of the breaking of the comparator that JB talked about. I was able to use my custom comparator and still observe the contract. When the valuse I was looking at were equal, i fell back to comparing the keys. I didn't care about the order if values were equal.
public int compare(String a, String b) {
if(base.get(a)[0] == base.get(b)[0]){ //need to handle when they are equal
return a.compareTo(b);
}else if (base.get(a)[0] < base.get(b)[0]) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
What is the easiest way to convert a 2D array of Strings into a HashMap?
For example, take this:
final String[][] sheetMap = { /* XSD Name, XSL Sheet Name */
{"FileHeader", "FileHeader"},
{"AccountRecord", "AccountRecord"},
{"DriverCardRecord", "DriverCardRecord"},
{"AssetCardRecord", "AssetCardRecord"},
{"SiteCardRecord", "SiteCardRecord"}
};
This is most likely going to be loaded from a file and will be much bigger.
final Map<String, String> map = new HashMap<String, String>(sheetMap.length);
for (String[] mapping : sheetMap)
{
map.put(mapping[0], mapping[1]);
}
If you just want to initialize your map in a convenient way, you can use double brace initialization:
Map<String, String > sheetMap = new HashMap<String, String >() {{
put( "FileHeader", "FileHeader" );
put( "AccountRecord", "AccountRecord" );
put( "DriverCardRecord", "DriverCardRecord" );
put( "AssetCardRecord", "AssetCardRecord" );
put( "SiteCardRecord", "SiteCardRecord" );
}};
As a slightly cleaner alternative to tradeJmark answer:
String[][] arr = // your two dimensional array
Map<String, String> arrMap = Arrays.stream(arr).collect(Collectors.toMap(e -> e[0], e -> e[1]));
// Sanity check!
for (Entry<String, String> e : arrMap.entrySet()) {
System.out.println(e.getKey() + " : " + e.getValue());
}
Wait; if this is going to be loaded from a file, don't go through the intermediate array step! You would have had to load it all first before creating the array or you wouldn't know the size for the array. Just create a HashMap and add each entry as you read it.
The existing answers work well, of course, but in the interest of continually updating this site with new info, here's a way to do it in Java 8:
String[][] arr = {{"key", "val"}, {"key2", "val2"}};
HashMap<String, String> map = Arrays.stream(arr)
.collect(HashMap<String, String>::new,
(mp, ar) -> mp.put(ar[0], ar[1]),
HashMap<String, String>::putAll);
Java 8 Streams are awesome, and I encourage you to look them up for more detailed info, but here are the basics for this particular operation:
Arrays.stream will get a Stream<String[]> to work with.
collect takes your Stream and reduces it down to a single object that collects all of the members. It takes three functions. The first function, the supplier, generates a new instance of an object that collects the members, so in our case, just the standard method to create a HashMap. The second function, the accumulator, defines how to include a member of the Stream into the target object, in your case we simply want to put the key and value, defined as the first and second value from each array, into the map. The third function, the combiner, is one that can combine two of the target objects, in case, for whatever reason, the JVM decided to perform the accumulation step with multiple HashMaps (in this case, or whatever other target object in another case) and then needs to combine them into one, which is primarily for asynchronous execution, although that will not typically happen.
More concise with streams would be:
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
import java.util.Map;
...
public static Map<String, String> asMap(String[][] data) {
return stream(data).collect(toMap( m->m[0], m->m[1] ));
}
...
Java 8 way
public static Map<String, String> convert2DArrayToMap(String[][] data){
return Arrays.stream(data).collect(Collectors.toMap(m -> m[0], m -> m[1]));
}
with loop
public static Map<String, String> convert2DArrayToMap(String[][] data)
{
Map<String, String> map = new HashMap<>();
for (String[] m : data)
{
if (map.put(m[0], m[1]) != null)
{
throw new IllegalStateException("Duplicate key");
}
}
return map;
}