Sorry, new to Java, probably a really simple question.
Let's say I have an outter map, that has a (key, inner map), and in the inner map I have (String, Double).
So it looks something like this.
HashMap<String, Double> inner = new HashMap<String, Double>();
HashMap<Integer, Map<String, Double>> outter = new HashMap<Integer, Map<String, Double>>();
inner.put("MyVal", 24.5930553450692151964475150);
inner.put("MyVal2", 48.6514790522118734018261775);
outter.put(20151205, inner)
I end up with and outter map like this:
{20151205={MyVal=24.593055345069214, MyVal2=48.651479052211876}}
Now let's say I no longer have access to the inner map, so I can't put any more values in it. But, I want to add a MyVal3 using only the outter map.
How can this be done?
I want to end up with something like this using code for only the outter map.
{20151205={MyVal=24.593055345069214, MyVal2=48.651479052211876, MyVal3=48.4846855555555}}
Thanks a lot! Couldn't find exactly this question elsewhere on SO.
As always thanks everyone!
You always have access to the inner map, but you need to get the key.
Then you simply put.
outter.get(20151205).put("MyVal3", 48.4846855555) ;
Beware of the Nullpointerexception when you try to get a key that doesn't exist
Related
I have a Map<String, List<SomeClassA>> that I'm trying to convert into a Map<String, Map<String, List<SomeWrapperOfClassA>>> and I'm just having so much trouble wrapping my head around how to do this.
Really, all the information needed to create the map should be in the objects of type SomeClassA - say
SomeClassA:
String attributeA;
String attributeB;
SomeClassB someOtherInfo;
SomeClassB:
String attribute C;
And I want to say it's a map based on this:
Map<attributeA's values,Map<attribute C vals, List SomeWrapperOfClassA>>
where the list is only of SomeWrapperClassA that has those values of attributeA and attributeB. I was thinking it might have to do with groupingBy, but I'm not too familiar with how to do it in such a way that its nested like this.
(or for the sake of simplicity, any help just getting the original list of SomeClassA into a Map<String, Map<String, List<SomeClassA>>> would already be a huge help.
I haven't quite gotten the hang of Java 8 and the more complex streaming concepts yet, so some help would be greatly appreciated. I'm only familiar with the basics.
I am not sure what you meant by:
Map<String, Map<String, List<SomeClassA>>>, what is the key here to group by? in general if you want to group List<SomeClassA> by say attributeA from SomeClassA you can do this:
List<SomeClassA>.stream().collect(Collectors.groupingBy(someClsA-> someClsA.getAttributeA()));
If I understand your questions correctly I would express it as follows:
Given:
class ClassA {
public String getA();
public ClassB getB();
}
class ClassB {
public String getC();
}
Map<String, List<ClassA>> input;
How would I create a Map<String, Map<String, List<ClassB>> where the key of the inner map is the result of getA?
If that's correct then you aren't looking to change the keys of the outer map at all. That makes it a good candidate for Map.replaceAll. For clarity I've split the collector into a separate method so it's clear what's happening:
input.replaceAll(listA.stream().collect(mapCollector()));
private Collector<A, ?, Map<String, List<B>> mapCollector() {
return Collectors.groupingBy(ClassA::getA,
Collectors.mapping(ClassA::getB,
Collectors.toList()));
}
Explaining the method it says group As by getA as key, then collect those As by mapping using getB then putting in a list.
If you particularly want a new map (rather than changing the original one) then:
Map<String, Map<String, List<ClassB>>> output = input.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().collect(mapCollector()));
It’s not entirely clear what you want to achieve. Apparently, the keys of your source map are irrelevant and you only want to process the SomeClassA instances contained in the value lists. So, the first step is to stream over the map’s values, i.e. sourceMap.values().stream(), then flatten this Stream<List<SomeClassA>> to a Stream<SomeClassA> via flatMap(List::stream).
Grouping these SomeClassA instances by one of their properties works indeed straight-forwardly via groupingBy, which needs another nested groupingBy to group each group further by “Attribute C”:
Map<String, Map<String, List<SomeClassA>>> resultMap
= sourceMap.values().stream().flatMap(List::stream)
.collect(Collectors.groupingBy(SomeClassA::getAttributeA,
Collectors.groupingBy(a->a.getSomeOtherInfo().getAttributeC())));
To convert the SomeClassA instances to SomeWrapperClassA instances, we need another nested collector for the innermost groupingBy; the mapping collector allows to map the values before transferring to another collector, which has to be the toList() collector, which was formerly implicit.
Now, it might be better to start using import static java.util.stream.Collectors.*;
Map<String, Map<String, List<SomeWrapperClassA>>> resultMap
= sourceMap.values().stream().flatMap(List::stream)
.collect(groupingBy(SomeClassA::getAttributeA, groupingBy(
a -> a.getSomeOtherInfo().getAttributeC(),
mapping(a->new SomeWrapperClassA(a.getAttributeA(),a.getAttributeB()), toList()))));
I've read that when you define a {Map, Set, Etc} it is good practice use the interface name as so:
Map<Integer, String> map = new LinkedHashMap<Integer, String>();
instead of:
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
I'm not sure why this is, but I've put it to practice in hopes I will understand at a later time. Maybe that time has come.
So I create a class that defines one and create a getter for the Map:
class Data{
private Map<Integer, String> map;
public Data(){
map = new LinkedHashMap<Integer, String>();
//dynamically put some things into the map
}
public Map<Integer, String> getMap(){
return map;
}
}
Now I come to my first impasse. I can't return a LinkedHashMap, I have to return a Map.
So in another class I get that Map
class Foo{
public Foo{
Data data = new Data();
Map<Integer, String> map = data.getMap();
}
}
Can someone explain what is happening to map when it gets passed?
Is it still a LinkedHashMap?
Is the data changed at all?
What would happen to the order of the Map if, after calling getData(), I put something in the Map?
Why wouldn't/shouldn't I just define the Map as in the second code snippet?
Is my method of getting the map done in ignorance?
Should I just make map public?
Now I come to my first impasse. I can't return a LinkedHashMap
Here's the misunderstanding: you can return a LinkedHashMap, because a LinkedHashMap is a Map, a particular sort of Map, but a Map anyway.
Can someone explain what is happening to map when it gets passed?
When it's passed, it is seen as any Map, like incognito, but it remains the same.
Is it still a LinkedHashMap?
Yes.
Is the data changed at all?
No.
What would happen to the order of the Map if, after calling getData(), I put something in the Map?
This is another topic.
Why wouldn't/shouldn't I just define the Map as in the second code snippet?
You needn't do that, since a LinkedHashMap is a Map (on the other hand, a Map is not necessarily a LinkedHashMap).
Is my method of getting the map done in ignorance?
Should I just make map public?
No.
You may want your variable to be able do what it needs to do and still be as flexible as possible in the way it does that. So your variable should be of the type that covers all your needed functionality but is as high as possible in hirachy.
Here is an example method:
public void loadStuff(Map<String, Object> someMap) {
Map<String, Object> myMap = new HashMap<String, Object>();
//I now load defaults here first
myMap.put("One", someObject);
myMap.put("two", someObject);
myMap.put("three", someObject);
//Now I put the entire someMap so that only those keys that are present in someMap are overridden in myMap and others remain default.
myMap.putAll(someMap);
}
Now, is there a better way of doing these redundant puts as the number of defaults in my scenario are a lot.
Consider creating an initial map with your defaults in, and then use:
// Alternatively, you could use clone()
Map<String, Object> myMap = new HashMap<String, Object>(defaults);
myMap.putAll(someMap);
Aside from anything else that means you can load the "default map" from a properties file or whatever.
If you really don't like the fact that it will put each value twice, you could write a loop to check for each key - but I'd personally just use the above code. It's simple and it should work fine.
Are you wanting to preload a single answer for just a few items, or are you wanting a default for all unfound keys? If you want to change the default answer from null to something else, see this question. If you're wanting to preload some items, then you'll need to put all of them, though it's best not to embed the values in code like that; use a for loop instead that iterates over a single official list of the keys.
If you are going to be initializing blank copies of this Map frequently, it will make more sense to have a template Map that each myMap is constructed from; either a HashMap wrapped as unmodifiable or a Guava ImmutableMap are good choices there. Constructing from a preexisting Map instead of copying all of the elements into the new HashMap is much more efficient since the new one knows how big to make itself.
I need a double table from which I get two values from a key or index. I have seen this question already and I want to know what would be a better approach considering also performance.
1) Create a HashMap on this way:
HashMap<Integer, HashMap<String, String>> = ...;
I don't know how to put values inside this the put method, I have this and Eclipse gives me an error prueba.put(0, new Hashtable<"Hi", "Bye">); As you can see I have never used something like this before I am sure is a simple question.
2) Create a HashMap on this way:
HashMap<Integer, YourFancyDatatype>
So I create a class which pack the two or more values I want to have in one Object inside a single key or index.
Which would perform better ? Also if you can help me about how to use number 1) approach. The HashMap will have about 20000 entries.
Thank you very much for your time and help :)
You would want something with a single key and a collection of values. I would suggest using Apache's MultiMap, as they already implement this functionality for you.
Your first approach uses the same datastructure as provided by the Guava's HashBasedTable so you can use it instead.
But if you want the best performance you could try to use something based on arrays (e.g. Guava's ArrayTable)
Anyway I suggest to make some simple performance tests to check which solution performs better.
It you want to do an "in-line" put, you can do this:
prueba.put(0, new HashMap<String, String>() {{put("Hi", "Bye");}});
This employs an anonymous subclass of HashMap that has an instance block that loads the values.
Note that this will create one extra class for the JVM (called MyClass$1 or similar).
I don't know how to put values inside this the put method, I have this
and Eclipse gives me an error prueba.put(0, new Hashtable<"Hi",
"Bye">); As you can see I have never used something like this before I
am sure is a simple question.
Firstly, Hashtable<String, String> is not a subtype of HashMap<String,String>. your HashMap expects a HashMap<String, String> as a value. either insert a hashmap into values or change your hashmap declaration to :
HashMap<Integer, ? extends Map<String, String>> = ...;
however your 2nd approach is more object oriented. so i'd recommend using 2nd approach
The second one would probably be easier in your case in this way
HashMap<Integer, HashMap<String, FancyDataType>> h= ...;
this is how you'll have to insert the data
h=HashMap<Integer, FancyDataType> new Hashtable<Integer,FancyDataType>();
numbers.put(0, new FancyDataType("o","x"));
numbers.put(1, new FancyDataType("t","y"));
numbers.put(1, new FancyDataType("q","z"));
/// ...so one for all 20000
Assuming FancyDataType is something like
class FancyDataType{
String k,v;
FancyDataType(String k,String v){
this.k=k;this.v=v;
}
}
Is this possible in Java: Map<SomeObject, Map<SomeOtherObject>>? I'm trying Map<Integer, Map<String>> am getting an
"Incorrect number of arguments for
type Map; it cannot be
parameterized with arguments "
error.
Every Map needs to be parametrized on two types; your second (nested) Map has only one.
A Map maps keys to values, so Map<String> is incorrect. So you'd need something like Map<String, Object>.
You need a second argument on your second Map<>. Perhaps you mean Map<Integer, Map<String, String>>?
No, not really like that. You need to give a type for both Key and Value for the second "inner" Map, this is ok:
Map<SomeObject, Map<SomeOtherObject, Object>>
Just like with the outer Map, where the Key is SomeObject, and Value is the inner Map. So, if you add a value specification for the inner Map is, that would be ok.