Related
I have a List<Map<String, String>>. One particular value of the map is a numeric entry with decimals. I wish to sort the list in descending order based on that particular value of the map.
for example:
List<Map<String, String>> foo= new ArrayList<Map<String, String>>();
Map<String, String> bar1 = new HashMap<>();
Map<String, String> bar2 = new HashMap<>();
Map<String, String> bar3 = new HashMap<>();
bar1.put("name", "abc");
bar1.put("score", "72.5");
bar1.put("sex", "male");
foo.add(bar1);
bar2.put("name", "pqr");
bar2.put("score", "98.7");
bar2.put("sex", "female");
foo.add(bar2);
bar3.put("name", "xyz");
bar3.put("score", "100.0");
bar3.put("sex", "male");
foo.add(bar3);
.
.
.
.
and so on
I want to sort the List<Map<String, String>> in descending order such that the map containing the score of 100.0 is on top.
I tried
Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
#Override
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});
but the "V" here is a string, while i need it to be sorted as a float.
Any help would be appreciated.
If is not possible to create a class from that map then you can do something like:
Collections.sort(foo, (o1, o2) -> {
return new BigDecimal(o2.get("score")).compareTo(new BigDecimal(o1.get("score")));
});
or if you are not using java 8:
Collections.sort(foo, new Comparator<Map<String, String>>() {
#Override
public int compare(Map<String, String> o1, Map<String, String> o2) {
return new BigDecimal(o2.get("score")).compareTo(new BigDecimal(o1.get("score")));
}
});
Why you need List Of map, you can go with List<SomeClass> and sort it as you wish, where Some class will hold you value for name sex score.
Edit: You can use convert your String in float or can take float as Someclass data type, it make your code very easy.
This could be a another simple solution.
Use Float to parse the string and use existing compare method of Float.
Collections.sort(foo, new Comparator<Map<String, String>>() {
#Override
public int compare(Map<String, String> o1, Map<String, String> o2) {
return Float.compare(Float.parseFloat(o2.get("score")), Float.parseFloat(o1.get("score")));
}
});
to sort a list of map by values of specific key :
public static void sortMapByKey(List<Map<String, Object>> crList, final String sortKey, final boolean ascending) {
Collections.sort(crList, new Comparator<Map<String, Object>>() {
#Override
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
Object obj1 = o1.get(sortKey);
Object obj2 = o2.get(sortKey);
if (obj1 != null && obj2 != null) {
if (ascending)
return obj1.toString().compareTo(obj2.toString());
else
return obj2.toString().compareTo(obj1.toString());
} else
return 0;
}
});
}
Given a Map<String, Object>, where the values are either a String or another Map<String, Object>, how would one, using Java 8, flatten the maps to a single list of values?
Example:
Map - "key1" -> "value1"
- "key2" -> "value2"
- "key3" -> Map - "key3.1" -> "value3.1"
- "key3.2" -> "value3.2"
- "key3.3" -> Map - "key3.3.1" -> "value3.3.1"
- "key3.3.2" -> "value3.3.2"
For the above example, I would like the following list:
value1
value2
value3.1
value3.2
value3.3.1
value3.3.2
I know it can be done like this:
public static void main(String args[]) throws Exception {
//Map with nested maps with nested maps with nested maps with nested......
Map<String, Object> map = getSomeMapWithNestedMaps();
List<Object> values = new ArrayList<>();
addToList(map, values);
for (Object o:values) {
System.out.println(o);
}
}
static void addToList(Map<String, Object>map, List<Object> list) {
for (Object o:map.values()) {
if (o instanceof Map) {
addToList((Map<String, Object>)o, list);
} else {
list.add(o);
}
}
}
How can I do this with a Stream?
Edit:
After some playing around I figured it out:
public static void main(String args[]) throws Exception {
//Map with nested maps with nested maps with nested maps with nested......
Map<String, Object> map = getSomeMapWithNestedMaps();
//Recursively flatten maps and print out all values
List<Object> list= flatten(map.values().stream()).collect(Collectors.toList());
}
static Stream<Object> flatten(Stream<Object> stream) {
return stream.flatMap((o) ->
(o instanceof Map) ? flatten(((Map<String, Object>)o).values().stream()) : Stream.of(o)
);
}
You could define a recursive method which flattens one map and use it as a function for Stream#flatMap or use it by calling it directly.
Example:
public class FlatMap {
public static Stream<Object> flatten(Object o) {
if (o instanceof Map<?, ?>) {
return ((Map<?, ?>) o).values().stream().flatMap(FlatMap::flatten);
}
return Stream.of(o);
}
public static void main(String[] args) {
Map<String, Object> map0 = new TreeMap<>();
map0.put("key1", "value1");
map0.put("key2", "value2");
Map<String, Object> map1 = new TreeMap<>();
map0.put("key3", map1);
map1.put("key3.1", "value3.1");
map1.put("key3.2", "value3.2");
Map<String, Object> map2 = new TreeMap<>();
map1.put("key3.3", map2);
map2.put("key3.3.1", "value3.3.1");
map2.put("key3.3.2", "value3.3.2");
List<Object> collect = map0.values().stream()
.flatMap(FlatMap::flatten)
.collect(Collectors.toList());
// or
List<Object> collect2 = flatten(map0).collect(Collectors.toList());
System.out.println(collect);
}
}
For the given nested map, it prints
[value1, value2, value3.1, value3.2, value3.3.1, value3.3.2]
I am newbie in Java 8 Streams. Please advice, how to Convert Stream Stream<HashMap<String, Object>> to HashMap Array HashMap<String, Object>[] ?
For example, I has some stream in code:
Stream<String> previewImagesURLsList = fileNames.stream();
Stream<HashMap<String, Object>> imagesStream = previewImagesURLsList
.map(new Function<String, HashMap<String, Object>>() {
#Override
public HashMap<String, Object> apply(String person) {
HashMap<String, Object> m = new HashMap<>();
m.put("dfsd", person);
return m;
}
});
How I can do something like
HashMap<String, Object>[] arr = imagesStream.toArray();
?
Sorry my bad English.
The following should work. Unfortunately, you have to suppress the unchecked warning.
#SuppressWarnings("unchecked")
HashMap<String, Object>[] arr = imagesStream.toArray(HashMap[]::new);
The expression HashMap[]::new is an array constructor reference, which is a kind of method reference. Method references provide an alternative way to implement functional interfaces. You can also use a lambda expression:
#SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, Object>[] array = stream.toArray(n -> new HashMap[n]);
Before Java 8, you would have used an anonymous inner class for that purpose.
#SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, Object>[] array = stream.toArray(new IntFunction<HashMap[]>() {
public HashMap[] apply(int n) {
return new HashMap[n];
}
});
It may be a bad practice, but I haven't been able to figure out any better solution for my problem. So I have this map
// Map<state, Map<transition, Map<property, value>>>
private Map<String, Map<String, Map<String, String>>> properties;
and I want to initialize it so I don't get NullPointerException with this
properties.get("a").get("b").get("c");
I tried this one but I didn't work (obviously)
properties = new HashMap<String, Map<String, Map<String,String>>>();
Other things I tried didn't compile.
Also if you have any ideas how to avoid this nested maps, I would appreciate it.
It seems to me that you need to create your own Key class:
public class Key {
private final String a;
private final String b;
private final String c;
public Key(String a, String b, String c) {
// initialize all fields here
}
// you need to implement equals and hashcode. Eclipse and IntelliJ can do that for you
}
If you implement your own key class, your map will look like this:
Map<Key, String> map = new HashMap<Key, String>();
And when looking for something in the map you can use:
map.get(new Key("a", "b", "c"));
The method above will not throw a NullPointerException.
Please remember that for this solution to work, you need to override equals and hashcode in the Key class. There is help here. If you don't override equals and hashcode, then a new key with the same elements won't match an existing key in the map.
There are other possible solutions but implementing your own key is a pretty clean one in my opinion. If you don't want to use the constructor you can initialize your key with a static method and use something like:
Key.build(a, b, c)
It is up to you.
You need to put maps in your maps in your map. Literally:
properties = new HashMap<String, Map<String, Map<String,String>>>();
properties.put("a", new HashMap<String, Map<String,String>>());
properites.get("a").put("b", new HashMap<String,String>());
If your target is lazy initialization without NPE you have to create your own map:
private static abstract class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val == null && key instanceof K) {
put((K)key, val = create());
}
return val;
}
protected abstract V create();
}
public void initialize() {
properties = new MyMap<String, Map<String, Map<String, String>>>() {
#Override
protected Map<String, Map<String, String>> create() {
return new MyMap<String, Map<String, String>>() {
#Override
protected Map<String, String> create() {
return new HashMap<String, String>();
}
};
}
};
}
You could use a utility method:
public static <T> T get(Map<?, ?> properties, Object... keys) {
Map<?, ?> nestedMap = properties;
for (int i = 0; i < keys.length; i++) {
if (i == keys.length - 1) {
#SuppressWarnings("unchecked")
T value = (T) nestedMap.get(keys[i]);
return value;
} else {
nestedMap = (Map<?, ?>) nestedMap.get(keys[i]);
if(nestedMap == null) {
return null;
}
}
}
return null;
}
This can be invoked like this:
String result = get(properties, "a", "b", "c");
Note that care is required when using this as it is not type-safe.
The only way to do it with this structure is to pre-initialise the 1st and 2nd level maps with ALL possible keys. If this is not possible to do you can't achieve what you are asking with plain Maps.
As an alternative you can build a custom data structure that is more forgiving. For example a common trick is for a failed key lookup to return an "empty" structure rather than null, allowing nested access.
You can't initialize this in one go, since you normally don't know what keys you'll have in advance.
Thus you'd have to check whether the submap for a key is null and if so you might add an empty map for that. Preferably you'd only do that when adding entries to the map and upon retrieving entries you return null if one of the submaps in the path doesn't exist. You could wrap that in your own map implementation for ease of use.
As an alternative, apache commons collections' MultiKeyMap might provide what you want.
It's impossible to use properties.get("a").get("b").get("c"); and be sure to avoid null unless you make your own Map. In fact, you can't predict that your map will contains "b" key.
So try to make your own class to handle nested get.
I think a better solution is using an object as the only key to the map of values. The key will be composed of three fields, state, transition and property.
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Key {
private String state;
private String transition;
private String property;
public Key(String state, String transition, String property) {
this.state = state;
this.transition = transition;
this.property = property;
}
#Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
When you check for a value, the map will return null for a key that is not associated with a value
Map<Key, String> values = new HashMap<Key, String>();
assert values.get(new Key("a", "b", "c")) == null;
values.put(new Key("a", "b", "c"), "value");
assert values.get(new Key("a", "b", "c")) != null;
assert values.get(new Key("a", "b", "c")).equals("value");
To efficiently and correctly use an object as a key in a Map you should override the methods equals() and hashCode(). I have built thos methods using the reflective functionalities of the Commons Lang library.
I think, following is the easier way:
public static final Map<Integer, Map<Integer, Map<Integer, Double>>> A_Map = new HashMap<Integer, Map<Integer, Map<Integer, Double>>>()
{
{
put(0, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 60.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 160.0);
put(1, 1 / 13600.0);
}
});
}
});
put(1, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 260.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 560.0);
put(1, 1 / 1300.0);
}
});
}
});
}
};
Using computeIfAbsent/putIfAbsent makes it simple:
private <T> void addValueToMap(String keyA, String keyB, String keyC, String value) {
map.computeIfAbsent(keyA, k -> new HashMap<>())
.computeIfAbsent(keyB, k -> new HashMap<>())
.putIfAbsent(keyC, value);
}
In short, if you want to write a map of e.g. constants in Java, which in e.g. Python and Javascript you would write as a literal,
T<String,String> CONSTANTS =
{
"CONSTANT_NAME_0": CONSTANT_VALUE_0 ,
"CONSTANT_NAME_1": CONSTANT_VALUE_1 ,
"CONSTANT_NAME_2": CONSTANT_VALUE_2 ,
//...
} ;
is there a Class or any preset Object that you can use for writing a data structure like that?
I like to do it this way:
Map map = new HashMap() {{
put("foo", "bar");
put(123, 456);
}};
The double {{ }} are an instance initialization block. They are a bit unusual but they are useful. No need for libraries or helpers.
No, Java doesn't have a map literal. The closest you'll come to this is using Google Collections' ImmutableMap:
Map<K,V> CONSTANTS = ImmutableMap.of(
NAME_1, VALUE_1,
NAME_2, VALUE_2
//etc.
);
Constants? I'd use an enum.
public enum Constants {
NAME_1("Value1"),
NAME_2("Value2"),
NAME_3("Value3");
private String value;
Constants(String value) {
this.value = value;
}
public String value() {
return value;
}
}
Value for e.g. NAME_2 can be obtained as follows:
String name2value = Constants.NAME_2.value();
Only give the enum a bit more sensible name, e.g. Settings, Defaults, etc, whatever those name/value pairs actually represent.
Sorry, I'm a tinkerer :-) Here's a somewhat cleaner way.
public class MapTest {
private static Map<String, String> map;
static {
Map<String, String> tmpMap = new HashMap<String, String>();
tmpMap.put("A", "Apple");
tmpMap.put("B", "Banana");
// etc
map = Collections.unmodifiableMap(tmpMap);
}
public Map<String, String> getMap() {
return map;
}
}
You can write yourself a quick helper function:
#SuppressWarnings("unchecked")
public static <K,V> Map<K,V> ImmutableMap(Object... keyValPair){
Map<K,V> map = new HashMap<K,V>();
if(keyValPair.length % 2 != 0){
throw new IllegalArgumentException("Keys and values must be pairs.");
}
for(int i = 0; i < keyValPair.length; i += 2){
map.put((K) keyValPair[i], (V) keyValPair[i+1]);
}
return Collections.unmodifiableMap(map);
}
Note the code above isn't going to stop you from overwriting constants of the same name, using CONST_1 multiple places in your list will result in the final CONST_1's value appearing.
Usage is something like:
Map<String,Double> constants = ImmutableMap(
"CONST_0", 1.0,
"CONST_1", 2.0
);
Here's another way, best suited for maps that won't be changing:
public class Whatever {
private static Map<String,String> map = new HashMap<String,String>();
static {
map.put("A", "Apple");
map.put("B", "Banana");
// etc
}
}
Java7 suppose to implement following syntax:
Map<String, String> = {
"key1": "value",
"key2": "value",
"key3": "value",
"key4": "value"
};
However now you're forced to use solutions proposed by Jorn or Tony Ennis.
Ok, with Jorn's improvement I can't seem to change the map at all, internally or externally. Perhaps not quite as readable, but if you need the map to be unmodifiable I think this is better.
public class MapTest {
private static Map<String, String> map = initMap();
private static Map<String, String> initMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "Apple");
map.put("B", "Banana");
// etc
return Collections.unmodifiableMap(map);
}
public Map<String, String> getMap() {
return map;
}
public static void main(String[] args) {
MapTest m = new MapTest();
System.out.println(m.getMap().get("A"));
m.getMap().put("this", "that");
}
}
I like to do the declaration and initialization on the same line. I've used this handy little utility for so long it basically is my "map literal" and until they're done "right" in the languange, I'm gonna continue on using it like that :)
Happy to share it here.
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A handy utility for creating and initializing Maps in a single statement.
* #author Jonathan Cobb. This source code is in the Public Domain.
*/
public class MapBuilder {
/**
* Most common create/init case. Usage:
*
* Map<String, Boolean> myPremadeMap = MapBuilder.build(new Object[][]{
* { "a", true }, { "b", false }, { "c", true }, { "d", true },
* { "e", "yes, still dangerous but at least it's not an anonymous class" }
* });
*
* If your keys and values are of the same type, it will even be typesafe:
* Map<String, String> someProperties = MapBuilder.build(new String[][]{
* {"propA", "valueA" }, { "propB", "valueB" }
* });
*
* #param values [x][2] array. items at [x][0] are keys and [x][1] are values.
* #return a LinkedHashMap (to preserve order of declaration) with the "values" mappings
*/
public static <K,V> Map<K,V> build(Object[][] values) {
return build(new LinkedHashMap<K,V>(), values);
}
/**
* Usage:
* Map<K,V> myMap = MapBuilder.build(new MyMapClass(options),
* new Object[][]{ {k,v}, {k,v}, ... });
* #param map add key/value pairs to this map
* #return the map passed in, now containing new "values" mappings
*/
public static <K,V> Map<K,V> build(Map<K,V> map, Object[][] values) {
for (Object[] value : values) {
map.put((K) value[0], (V) value[1]);
}
return map;
}
/** Same as above, for single-value maps */
public static <K,V> Map<K,V> build(Map<K,V> map, K key, V value) {
return build(map, new Object[][]{{key,value}});
}
/**
* Usage:
* Map<K,V> myMap = MapBuilder.build(MyMapClass.class, new Object[][]{ {k,v}, {k,v}, ... });
* #param mapClass a Class that implements Map
* #return the map passed in, now containing new "values" mappings
*/
public static <K,V> Map<K,V> build(Class<? extends Map<K,V>> mapClass, Object[][] values) {
final Map<K,V> map;
try { map = mapClass.newInstance(); } catch (Exception e) {
throw new IllegalStateException("Couldn't create new instance of class: "+mapClass.getName(), e);
}
return build(map, values);
}
/** Usage: Map<K,V> myMap = MapBuilder.build(key, value); */
public static <K,V> Map build(K key, V value) {
Map<K,V> map = new HashMap<>();
map.put(key, value);
return map;
}
}
Since Java 9, you can create unmodifiable maps out of the box -
Map<String, String> emptymap = Map.of();
Map<String, String> map = Map.of("key1", "val1");
there are variants supporting upto 10 keypairs.
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html#of()
This is also supported https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...)
import static java.util.Map.entry;
Map<Integer,String> map = Map.ofEntries(
entry(1, "a"),
entry(2, "b"),
entry(3, "c"),
...
entry(26, "z"));