converting static 2D String array to HashMap - java

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;
}

Related

Java How to access hash map data held in an array

learning Java and have figured out how to store a hashmap in an array. But I can't figure out how to get to the stored data. Here is a simplified version of what I'm doing. I've got as far as displaying the specific array items, but how do I access the hash map stored in the array?
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
Map<String, String> custOrder = new HashMap<String, String>();
List ordersPlaced = new ArrayList();
custOrder.put("colour", "blue");
custOrder.put("manu", "bmw");
custOrder.put("body", "4x4");
ordersPlaced.add(custOrder);
custOrder = new HashMap();
custOrder.put("colour", "green");
custOrder.put("manu", "merc");
custOrder.put("body", "saloon");
ordersPlaced.add(custOrder);
System.out.println(ordersPlaced.get(0).toString());
}
}
Hope that makes sense. Thanks in advance
Neil
You're already accessing it.
In order to get the iterate on the map's items, you can:
ordersPlaced.get(0).forEach((key, value) -> {
System.out.println("Key is: " + key + ", Value is: " + value);
});
Or, earlier to Java 8, something like:
for (Map.Entry<String, String> entry : ordersPlaced.get(0).entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
Please note that you should avoid using raw type list. Instead, you should have:
List<Map<String, String>> ordersPlaced = new ArrayList<>();
And then:
Map<String, String> m = ordersPlaced.get(0);
You know it already.
You can get back the stored map by writing
Map<String, String> placedCustOrder = ordersPlaced.get(0);
And avoid using raw types while using List. Declare your list as
List<Map<String, String>> ordersPlaced = new ArrayList<>();
I would like to know how to access the colour of the data stored in the array at location 0
Since you got the map as I said in the line 1
Map<String, String> placedCustOrder = ordersPlaced.get(0);
String colorVal = placedCustOrder.get("colour");
I strongly suggest you to look through Map documentation before proceeding further.

Get top 3 counts from list using stream

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;
}

Java 8 way to convert collection into one key multiple values map

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…

Initializing a dictionary with a specific set of data cleanly in Java

I am curious how I can more effectively instantiate a dictionary in Java. At present I have passable code, yet I am filling it with data in a very obfuscated fashion.
Is there any way for me to initialize my dictionary similar to this? This is python for the record:
westernCanadaAdjList = { 'BC': ['AB'],
'AB': ['BC', 'SK'],
'SK': ['AB', 'MB'],
'MB': ['SK']
}
I find for presentation purposes that is a whole lot more clear.
My current code in Java:
public class Main {
public static void main(String[] args) {
//Adjacency List representation through a dictionary. Allows fast O(1) lookup time.
Map<String,ArrayList<String>> adjList = new HashMap<String,ArrayList<String>>();
//Adding values for Edmonton
adjList.put("Edmonton", new ArrayList<String>());
adjList.get("Edmonton").add("Neighbour1");
adjList.get("Edmonton").add("Neighbour2");
adjList.get("Edmonton").add("Neighbour3");
//Adding values for Vancouver
adjList.put("Vancouver", new ArrayList<String>());
adjList.get("Vancouver").add("V neighbour1");
adjList.get("Vancouver").add("V neighbour2");
System.out.println(adjList.keySet() +" And Values " + adjList.values());
for (String neighbour: adjList.get("Edmonton")){
System.out.println(neighbour);
}
for (String neighbour: adjList.get("Vancouver")){
System.out.println(neighbour);
}
}
}
Thank you very much!
Note: The original answer is over 8 years old and Java has come a long way since then. As of now I'd recommend:
var map = Map.of(
"BC", List.of("AB"),
"AB", List.of("BC", "SK"),
"SK", List.of("AB", "MB"),
"MB", List.of("SK")
);
This is the best technique I know of:
Map<String, String> myMap = new HashMap<String, String>() {{
put("foo", "bar");
put("key", "value");
//etc
}};
Note the double braces -- this is commonly called double brace initialization.
What you're actually doing is creating an anonymous inner class that extends HashMap, and your new subclass contains an initializer block, in which you can call any arbitrary code that is guaranteed to be executed before the instance can be used.
Also note the 'diamond operator' cannot be used with anonymous classes, for whatever reason.
This is a nice technique for test classes, but I tend to avoid it for production code.
EDIT: Thought I should answer your actual question!
double-brace initialization is probably the best solution in "pure" Java, your Map would specifically look like:
Map<String, List<String>> westernCanadaAdjList = new HashMap<String, List<String>> () {{
put("BC", new ArrayList<String>(){{ add("AB"); }});
put("AB", new ArrayList<String>(){{ add("BC"); add("SK"); }});
put("SK", new ArrayList<String>(){{ add("AB"); add("MB"); }});
put("MB", new ArrayList<String>(){{ add("SK"); }});
}};
... Still not super awesome. Java really does need a Map literal, and it does not have one.
For production code, I'd use a Guava's MultiMap, but honestly populating it with literals isn't much better:
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("BC", "AB");
multimap.put("AB", "BC");
multimap.put("AB", "SK");
multimap.put("SK", "SK");
multimap.put("SK", "SK");
multimap.put("SK", "SK");
I recently faced a similar issue. I represented the data as a 2d array, relatively easy to type and parse, and wrote a utility method to parse it into the data structure. e.g. for your case
static String[][] CANADA_DATA = {
{"BC"," AB"},
{"AB","BC","SK"},
// rest of Canada here
}
Example code
public Map<String, List<String>> parseIt() {
Map<String, List<String>> map = new HashMap();
for (String[] provinceData : CANADA_DATA ) {
String name = provinceData [0];
ArrayList neighbors = new ArrayList(Arrays.asList(provinceData ));
neighbors.remove(0); // remove ourself
map.put(name, neighbors);
}
return map;
}
Obviously you can change the data format and parsing code to fit your specific needs.
I agree with Louis and didn't intend to add anything.
The use of streams in this case allows you to compact the code into one line but I realize this is not an answer to your question (just to closest I could think of).
Map<String, List<String>> adjList = Stream.of(
new SimpleEntry<>("Edmonton", Arrays.asList("E N1", "E N2", "E N3")),
new SimpleEntry<>("Vancouver", Arrays.asList("V N1", "V N2", "V N3")))
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
Yes, you can: Parse it as json:
import com.fasterxml.jackson.databind.ObjectMapper;
String json = "{'BC': ['AB']," +
"'AB': ['BC', 'SK']," +
"'SK': ['AB', 'MB']," +
"'MB': ['SK']"
"}";
Map<String, Object> map = new ObjectMapper().readValue(json, HashMap.class);

In Java 8 how do I transform a Map<K,V> to another Map<K,V> using a lambda?

I've just started looking at Java 8 and to try out lambdas I thought I'd try to rewrite a very simple thing I wrote recently. I need to turn a Map of String to Column into another Map of String to Column where the Column in the new Map is a defensive copy of the Column in the first Map. Column has a copy constructor. The closest I've got so far is:
Map<String, Column> newColumnMap= new HashMap<>();
originalColumnMap.entrySet().stream().forEach(x -> newColumnMap.put(x.getKey(), new Column(x.getValue())));
but I'm sure there must be a nicer way to do it and I'd be grateful for some advice.
You could use a Collector:
import java.util.*;
import java.util.stream.Collectors;
public class Defensive {
public static void main(String[] args) {
Map<String, Column> original = new HashMap<>();
original.put("foo", new Column());
original.put("bar", new Column());
Map<String, Column> copy = original.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> new Column(e.getValue())));
System.out.println(original);
System.out.println(copy);
}
static class Column {
public Column() {}
public Column(Column c) {}
}
}
Map<String, Integer> map = new HashMap<>();
map.put("test1", 1);
map.put("test2", 2);
Map<String, Integer> map2 = new HashMap<>();
map.forEach(map2::put);
System.out.println("map: " + map);
System.out.println("map2: " + map2);
// Output:
// map: {test2=2, test1=1}
// map2: {test2=2, test1=1}
You can use the forEach method to do what you want.
What you're doing there is:
map.forEach(new BiConsumer<String, Integer>() {
#Override
public void accept(String s, Integer integer) {
map2.put(s, integer);
}
});
Which we can simplify into a lambda:
map.forEach((s, integer) -> map2.put(s, integer));
And because we're just calling an existing method we can use a method reference, which gives us:
map.forEach(map2::put);
Keep it Simple and use Java 8:-
Map<String, AccountGroupMappingModel> mapAccountGroup=CustomerDAO.getAccountGroupMapping();
Map<String, AccountGroupMappingModel> mapH2ToBydAccountGroups =
mapAccountGroup.entrySet().stream()
.collect(Collectors.toMap(e->e.getValue().getH2AccountGroup(),
e ->e.getValue())
);
The way without re-inserting all entries into the new map should be the fastest it won't because HashMap.clone internally performs rehash as well.
Map<String, Column> newColumnMap = originalColumnMap.clone();
newColumnMap.replaceAll((s, c) -> new Column(c));
If you use Guava (v11 minimum) in your project you can use Maps::transformValues.
Map<String, Column> newColumnMap = Maps.transformValues(
originalColumnMap,
Column::new // equivalent to: x -> new Column(x)
)
Note: The values of this map are evaluated lazily. If the transformation is expensive you can copy the result to a new map like suggested in the Guava docs.
To avoid lazy evaluation when the returned map doesn't need to be a view, copy the returned map into a new map of your choosing.
Here is another way that gives you access to the key and the value at the same time, in case you have to do some kind of transformation.
Map<String, Integer> pointsByName = new HashMap<>();
Map<String, Integer> maxPointsByName = new HashMap<>();
Map<String, Double> gradesByName = pointsByName.entrySet().stream()
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(
entry.getKey(), ((double) entry.getValue() /
maxPointsByName.get(entry.getKey())) * 100d))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
If you don't mind using 3rd party libraries, my cyclops-react lib has extensions for all JDK Collection types, including Map. You can directly use the map or bimap methods to transform your Map. A MapX can be constructed from an existing Map eg.
MapX<String, Column> y = MapX.fromMap(orgColumnMap)
.map(c->new Column(c.getValue());
If you also wish to change the key you can write
MapX<String, Column> y = MapX.fromMap(orgColumnMap)
.bimap(this::newKey,c->new Column(c.getValue());
bimap can be used to transform the keys and values at the same time.
As MapX extends Map the generated map can also be defined as
Map<String, Column> y
From Java 9 onwards it is even easier to do the transformation within the map part of the stream. Is was already possible to use a new AbstractMap.SimpleImmutableEntry but the Map interface has an additional static method Map.entry which can also create an entry which can be used for this usecase.
Java 9+
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
Map<String, Column> x;
Map<String, Column> y = x.entrySet().stream()
.map(entry -> Map.entry((entry.getKey(), new Column(entry.getValue())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
}

Categories

Resources