How do you properly nest multiple ArrayLists / Maps in Java? - java

I am trying to run a very simple program, and I'm stuck on the basics of declaring the nested lists and maps.
I'm working on a project which requires me to store polynomials into an ArrayList.
Each polynomial is named, so I want a key/value map to pull the name of the polynomial (1, 2, 3 etc.) as the key, and the actual polynomial as the value.
NOW the actual polynomial requires key values as well because the nature of this program requires that the exponent be associated with the coefficient.
So for example I need an ArrayList of polynomials, say the first one is a simple:
polynomial 1: 2x^3
the array list contains the whole thing as a map, and the map contains key: polynomial 1 and value: is a Map... with the 2 and 3 being key/values.
The code I have is below but I'm not 100% on how to format such nested logic.
public static void main(String[] args) throws IOException{
ArrayList<Map> polynomialArray = new ArrayList<Map>();
Map<String, Map<Integer, Integer>> polynomialIndex = new Map<String, Map<Integer, Integer>>();
String filename = "polynomials.txt";
Scanner file = new Scanner(new File(filename));
for(int i = 0; file.hasNextLine(); i++){
//this will eventually scan polynomials out of a file and do stuff
}
EDIT:
Updated the key/value in Map, still having issues.
The code above is giving me the following error:
Cannot instantiate the type Map<String,Map<Integer,Integer>>
So how then do I go about doing this or am I just going about this all the wrong way?

You can't instantiate new Map<String, Map<Integer, Integer>>() because java.util.Map is an interface (it doesn't have a constructor). You need to use a concrete type like java.util.HashMap:
Map<String, Map<Integer, Integer>> polynomialIndex = new HashMap<String, Map<Integer, Integer>>();
Also, if you're using Java 7 or above, you can use generic type inference to save some typing:
Map<String, Map<Integer, Integer>> polynomialIndex = new HashMap<>();

This is incorrect:
Map<String, Map<Integer>> polynomialIndex = new Map<String, Map<Integer>>();
Maps need to have two parameters and your nested map Map<Integer> only has one. I think you're looking for something like:
Map<String, Map<Integer, Integer>> polynomialIndex = new Map<String, Map<Integer, Integer>>();
Or it may be best done separate.
Map<String, Map> polynomialIndex = new Map<String, Map>();
Map<Integer, Integer> polynomialNumbers = new Map<Integer, Integer>();
With this you can just put the numbers in the polynomailNumbers Map then use that in polynomialIndex.

Related

How do I put in the values (of different data types) from two HashMaps with the same keys into a new third HashMap?

I need to make a third HashMap based off the values from the PeopleAndNumbers and PeopleAndGroups hashmaps. But the third HashMap would only have the 3 groups as keys and the total amounts from the people in that group as values.
(Also worth noting that the keys in the first both maps are the same.)
Here are the contents of the first two maps:
PeopleAndNumbers: {p1=1, p2=3, p3=2, p4=3, p5=1, p6=2}
PeopleAndGroups: {p1=GroupA, p2=GroupB, p3=GroupC, p4=GroupB, p5=GroupC, p6=GroupA}
I need to make a third HashMap that'd print out like this:
CombineMap: {GroupA=3, GroupB=6, GroupC=3}
Here is what the code looks like so far:
import java.util.HashMap;
public class HashmapTest {
public static void main(String[] args) {
HashMap<String, Integer> PeopleAndNumbers = new HashMap<String, Integer>();
HashMap<String, String> PeopleAndGroups = new HashMap<String, String>();
PeopleAndNumbers.put("p1", 1);
PeopleAndNumbers.put("p2", 3);
PeopleAndNumbers.put("p3", 2);
PeopleAndNumbers.put("p4", 3);
PeopleAndNumbers.put("p5", 1);
PeopleAndNumbers.put("p6", 2);
PeopleAndGroups.put("p1","GroupA");
PeopleAndGroups.put("p2","GroupB");
PeopleAndGroups.put("p3","GroupC");
PeopleAndGroups.put("p4","GroupB");
PeopleAndGroups.put("p5","GroupC");
PeopleAndGroups.put("p6","GroupA");
System.out.println(PeopleAndNumbers);
System.out.println(PeopleAndGroups);
HashMap<String, Integer> CombineMap = new HashMap<String, Integer>();
//Insert method to do this here, How would I go about this?
System.out.println("Expected Output for CombineMap should be");
System.out.println("{GroupA=3, GroupB=6, GroupC=3}");
System.out.println(CombineMap);
}
}
If I understand you correctly, you want to sum Numbers by Group, using the common keys to join them. If so, you can do it pretty easily with streams:
Map<String, Integer> combined = PeopleAndGroups.entrySet()
.stream()
.collect(Collectors.groupingBy(e -> e.getValue(),
Collectors.summingInt(e -> PeopleAndNumbers.get(e.getKey()))));
Or you can iterate and merge entries into your destination map:
Map<String, Integer> combined = new HashMap<>();
PeopleAndGroups.forEach((k, v) ->
combined.merge(v, PeopleAndNumbers.get(k), Integer::sum));
To achieve that you need to iterate over the entries of the PeopleAndGroups map and do the following for each entry:
check if the combinedMap has a key equal to the value of the current entry
If the key doesn't exist put the key with value 0: combinedMap.put(entry.getValue(), 0)
Get the value of the entry's key from the PeopleAndNumbers and let's call it N: int N = PeopleAndNumbers.get(entry.getKey())
add N to the old value of your result map:
combinedMap.put(entry.getValue(), combinedMap.get(entry.getValue()) + N)

How to sum elements of an ArrayList which are values of a HashMap using java streams?

I have a HashMap with integer and ArrayList of some Element objects. Element objects are described with price and amount. I want to go through all those elements in each ArrayList, sum them up by calling on each element element.price(), and create a new HashMap which contains old keys from and new values representing summ of each arraylist. Keys for new hash map should remain the same. Trying to do this using streams.
public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa){
Map<String, Double> m = mapa.entrySet().stream().flatMap()
return m;
}
The first solution that came to my mind was to use mapToDouble and sum.
That would have looked like this:
public static HashMap<Integer, Double> findIncomes(HashMap<Integer, List<Element>> mapa) {
HashMap<Integer, Double> sumsByKey = new HashMap<>();
mapa.entrySet().stream().forEach(entry -> sumsByKey.put(entry.getKey(), entry.getValue().stream().mapToDouble(element -> element.getPrice()).sum()));
return sumsByKey;
}
But when summing up 1.5d, 5.4d and 6.7d you get 13.600000000000001 as a result.
Therefore I had to remember: When performing calculations with doubles you usually better use BigDecimal.
So a more accurate solution could look like this:
public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa){
HashMap<Integer, Double> sumsByKey = new HashMap<>();
mapa.entrySet().stream().forEach(entry -> sumsByKey.put(entry.getKey(),
entry.getValue().stream().map(element -> BigDecimal.valueOf(element.getPrice())).reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue()));
return sumsByKey;
}
Since stream in stream is not really readable it might make sense to refactor it further.
You need a new map always. You cannot alter the same map with different types. Something like this would do the job,
public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa) {
final HashMap<Integer, Double> m = new HashMap<>();
mapa.entrySet().stream().forEach(entry -> m.put(entry.getKey(), Double.valueOf(entry.getValue().stream().mapToDouble(Element::price).sum())));
return m;
}

Define a recursive definition of a HashMap using generics

Is there a way to define a HashMap that has another HashMap as the value without warning?
I mean if I use generics I would have to define:
HashMap<Integer, HashMap<Integer, HashMap<Integer, HashMap<Integer,HashMap etc>> map = new HashMap<>();
Is the correct/only way to do it via the followin?
HashMap<Integer, HashMap> map = new HashMap<Integer, HashMap>();
Update based on comments:
I am just reviewing generics and I was under the impression that it is not uncommon to have a hashmap as a value of another hashmap.
Update based on #JimGarrison comment:
Using a hash of hashes is a very common structure in other languages so I am surprised that I need to actually give some specific use case in order for my question to make sense. If I need to give a real example that this could be used, one would be to e.g. navigate through some hierarchical structure. So we could "mimic" a tree.
You might find F-bound types useful, at least from a theoretical point of view. In your case, this might be:
class FBoundedMap<K> extends HashMap<K, FBoundedMap<K>> {
}
Then you could use it this way:
FBoundedMap<Integer> map = new FBoundedMap<>();
FBoundedMap<Integer> inner1 = new FBoundedMap<>();
map.put(1, inner1);
FBoundedMap<Integer> inner2 = new FBoundedMap<>();
map.put(2, inner2);
FBoundedMap<Integer> innerMost1 = new FBoundedMap<>();
inner1.put(11, innerMost1);
FBoundedMap<Integer> innerMost2 = new FBoundedMap<>();
inner2.put(22, innerMost2);
System.out.println(map); // {1={11={}}, 2={22={}}}
You could only store empty maps at the end, and maps of maps in the middle, so the only practical use I see to this is to store data in the keys (in this case these would be Integers) and use the values to keep references to children nodes of a tree structure.
Another way would be to let the values be of any type, including HashMap. This way, you could store maps as values of other maps. In this case, you'd need to declare your maps as:
Map<Integer, Object> map = new HashMap<>();
Map<Integer, Object> inner1 = new HashMap<>();
map.put(1, inner1);
Map<Integer, Object> inner2 = new HashMap<>();
map.put(2, inner2);
Map<Integer, Object> innerMost1 = new HashMap<>();
inner1.put(11, innerMost1);
Map<Integer, Object> innerMost2 = new HashMap<>();
inner2.put(22, innerMost2);
System.out.println(map); // {1={11={}}, 2={22={}}}
Of course, if you need to get a value, you'd need to cast:
Map<Integer, Object> value = (Map<Integer, Object>) map.get(1);
System.out.println(value); // {11={}}

Convert 2 collections into a Map

I have 2 Lists as List<String> a and List<String> b of equal size.
What is the most efficient way to create a Map<String, String> in Java 8 using lambdas or something else where List<String> a are the keys and List<String> b are the values?
The Java 7 way is as follows:
Map<String, String> map = new HashMap<String, String>();
for(int i=0;i<a.size();i++)
map.put(a.get(i), b.get(i));
Since there is no zip operation on Stream (and no Pair class), a simple solution is to use an IntStream and loop over the indexes of each List.
Map<String, String> map =
IntStream.range(0, a.size()).boxed().collect(Collectors.toMap(a::get, b::get));
Alternatively, you can use the StreamEx library which offers a zip method and have:
Map<String, String> map = EntryStream.zip(a, b).toMap();

Creating Map using put() method

I am trying to create a dictionnary in a <K, List<V>> format.
private static Map<String, Collection<String>> dict = new HashMap<String, Collection<String>>();
Using new HashMap<>(); or new HashMap<String, ArrayList<String>>(); throws incompatible data types error
I need a dictionary similar to the one below.
a: apple, ajar, axe, azure
b: ball, bat, box
d: dam, door, dish, drown, deer, dare
u: urn, umbrella
y: yolk
To do this, I worte below code. put() returns incompatible paramters compilation error. What is the right way to use put() for this example?
dict.put("a", "apple");
dict.put("a", "ajar");
.
.
.
dict.put("u", "umbrella");
dict.put("y", "yolk");
You need to place a List as the value to the map, for example:
List<String> listA = Arrays.asList("apple", "ajar", "axe", "azure");
dict.put("a", listA);
Alternatively, you can use guava Multimap which allows more than one value to be mapped to a given key.
This is because you need to put an arrayList in the value as your Map declaration is Map<String, Collection<String>> so it cannot take Map<String, String>.
ArrayList<String> list = new ArrayList<String>();
list.add("apple");
dict.put("a",list );
As per java 7 you can do it using diamond operator so you can create a map as,
List<String, List<String>> = new ArrayList<>();
What you need is this;
List al = new ArrayList<String>();
al.add("apple");
al.add("ajar");
HashMap<String, List<String>> hm = new HashMap<String, List<String>>();
hm.put("a", al);
System.out.println(hm.get("a"));
This is because, when you use;
private static Map<String, Collection<String>>
You need a Collection like a List. DoNOT insert Objects as Strings
You can only follow the definition you have done :
Map<String, Collection<String>> implies you use dict.put(a,b) with a being a String and b a Collection.
You're trying to put a String as a value that's your problem.
You may want to do something like that :
Collection col = dict.get("a");
if (col == null) {
col = new ArrayList();
}
col.add("apple");
dict.put("a",col);
I would first change the type of the dictionary to
private static Map<Character, ArrayList<String>> dict = new HashMap<>();
It'll allow easier putting of array lists as generics are not covariant.
For each letter, create:
ArrayList<String> myList=new ArrayList<>();
and put() it to dict with
dict.put(myList);
Then you can add words with:
dict.get(letter).put(word);
Your exact need is MultiMap feature of apache-commons
MultiMap dict = new MultiHashMap();
dict.put("a", "apple");
dict.put("a", "ajar");
.
.
.
dict.put("u", "umbrella");
dict.put("y", "yolk");

Categories

Resources