I am trying to initialize a Map to zeros in a class. I am doing that in this way:
public class A{
private final Map<String,Integer> myMap;
public A(){
this.myMap = new HashMap<String,Integer>() {
{
put("a",0);
put("b",0);
}
};
}
}
My question: Is this a good implementation? Is there anything wrong with this? Or is there any better way to implement this?
What Rod_Algonquin meant was:
public class A {
private final Map<String,Integer> myMap;
public A() {
this.myMap = new HashMap<String,Integer>();
this.myMap.put("a",0);
this.myMap.put("b",0);
}
}
Following up on Luiggi Mendoza's comment, since the Map is declared final, you might have meant for the map to be unmodifiable, but final does not assure that. This will:
public class A {
private final Map<String,Integer> myMap;
public A() {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a",0);
map.put("b",0);
this.myMap = Collections.unmodifiableMap(map);
}
}
A better way would be to simply put the values in after you've initialized the map:
myMap = new HashMap<>();
myMap.put("a",0);
myMap.put("b",0);
What your current version is doing is that it's using an instance initializer block (a.k.a. double brace initialization), which creates an unnecessary anonymous class in the background. There is no real benefit in doing so here. On the contrary, it will likely cause a small performance hit.
If your real goal is the creation of an immutable Map, I highly recommend using Guava's ImmutableMap.Builder
Related
I have a line of code:
private final Map<MyClassA<?>, MyClassB<?>> myMap = new HashMap<>();
Is there any way to define that map in a way that would tell the compiler that the ? in each case must be the same class?
Something like this?
private final <T> Map<MyClassA<T>, MyClassB<T>> myMap = new HashMap<>();
... which is not legal syntax?
It's just a self-learning question at this point.
FWIW, I want to add a method
public <T> MyClassB<T> getForA(MyClassA<T> a) {
return this.myMap.get(a);
}
But I get a compile error unless I can define myMap to insist that both the key and the value wrap the same type.
As you already figured out, you can't do that if key and value are different for different entries:
map.put(new MyClassA<Foo>(), new MyClassB<Foo>());
map.put(new MyClassA<Bar>(), new MyClassB<Bar>());
(I've taken this requirement from your comment)
What you can do is to write some helper methods, which enforce this constraint:
public <T> void put(MyClassA<T> key, MyClass<B> value) {
// Maybe check at runtime if the constraint is not validated?
map.put(key, value);
}
public <T> MyClassB<T> get(MyClassA<T> key) {
// This will produce an unchecked warning.
return (T) map.get(key);
}
As long as you only access the map through such helper methods (and don't use raw types), the constraint on the map will not be violated, which allows you to write type safe code.
The only part that is not typesafe are those helper methods, and that's where you have to be careful.
You can do something similar if you introduce one static inner class for the type you need. For example:
public class DoubleGenericTest<T> {
public static class MapHolder<Z> {
private final Map<MyClassA<Z>, MyClassB<Z>> myMap = new HashMap<>();
}
private final MapHolder<String> stringMap = new MapHolder<>();
private final MapHolder<Integer> integerMap = new MapHolder<>();
}
class MyClassA<X> {}
class MyClassB<Y> {}
This gives you the class you need to hang the type parameter onto. Maybe not ideal in every situation but it's the only thing I can think of.
In the below class I am declaring myMap
public class AllMap {
public static final Map<String, String> myMap= new HashMap<>();
static {
Map.put("yy", "AA");
Map.put("xx", "BB");
}
}
I need to access map in other class.
public class Test {
FieldMap.Map;
}
Everything is working fine,but sonar is giving warning on 1st class:
Make this member "protected".
on the line
public static final Map<String, String> myMap = new HashMap<>();
Should I ignore this warning or should I change it to protected?
If you need to access the map in other classes, then you should protect it against modifications:
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class AllMap {
public static final Map<String, String> myMap;
static {
final Map<String, String> tmpMap = new HashMap<>();
tmpMap.put("yy", "AA");
tmpMap.put("xx", "BB");
myMap = Collections.unmodifiableMap(tmpMap);
}
}
You can still use it and SonarQube won't mark this as error (because the map is read-only).
Read more about Collections#unmodifiableMap(Map).
Sonar is giving you suggestion that your "member" which is:
public final Map<String, String> myMap = new HashMap<>();
should not be public.
Why?
Leaving this as public makes it available from any other package - so you are exposing the member to everybody. Below code is accessing the member directly:
AllMap allMap = new AllMap();
allMap.myMap.put("X", "Y");
In most cases members should be private and accessed by getters and setters, which could prevent with returning the same reference - so you can implement some logic before you get the reference or set it.
If you need to make it static, make static getters and setters.
Sonar lint issue because you are exposing references to mutable objects to client code.Here you are exposing a Map though it is final the final object allows clients to modify the contents of the object.
Never initialize such a field to a client-provided object reference or return the object reference from an accessor.
private static final SomeType [] THE_THINGS = { ... };
public static final List<SomeType> SOMETHINGS =
Collections.unmodifiableList(Arrays.asList(THE_THINGS));
Reference link
Reference link2
So I have this code in JarFile1.jar:
public static HashMap<String, Double[]> myHashMap = new HashMap<String, Double[]>();
How would I access that variable in a separate JarFile2.jar using import?
Put getter and setter to that var in jar1File:
class MyClass {
public HashMap<String, Double[]> getMyHashMap() {
return myHashMap;
}
public void setMyHashMap(HashMap<String, Double[]> myHashMap) {
this.myHashMap = myHashMap;
}
}
Once you are in jarFile2 just you the Class name, Because this is static member.
class MyClassJar2 {
public MyClassJar2() {
MyClass.getMyHashMap();
}
}
So when you will import you can access that var
Hope that help :)
Firstly, that doesn't sound like a good practice at all. It's not a good idea to allow access to an atribute of an object like a Map (you could clutter the data easily and without noticing).
If you want to access data from another class, you could implement a public method like public double[] getData(String key) and importing it in the other jar. Also, note that you need to add the first jar to the classpath while executing the second one.
I have run into such cases on 3 different occasions now. Most of the times when refactoring some code.
Lets say I have:
//code block A
List<Bar> foo = doSomething(inputParams);
//code block B
Now, I need to refactor the code such that I want to use doSomething() method's process to do something else too. Lets say,create a map too (Bar.id -> Bar.name).
Is there an elegant way to do this without passing a mutable map to doSomething() and not duplicating the code to another doSomethingDifferent()?
Dummy implementation:
doSomething(){
List<Bar> ret = new ArrayList<Bar>();
while(//condition){
ret.add(new Bar());
}
returrn ret;
}
doSomethingDifferently(){
Map<Integer, String> ret = new HashMap<Integer, String>();
while(//condition){
Bar b = new Bar()
ret.put(b.getId(),b.getName());
}
returrn ret;
}
Summary:
Is there a better way than the possible solutions below?
Solution 1:(repeated code)
List<Bar> foo = doSomething(inputParams);
Map<Integer,String> foobar = doSomethingDifferent(inputParams); //Very similar to doSomething
Solution 2:(hard to read)
Map<Integer,String> foobar = new HashMap<Integer,String>();
List<Bar> foo = doSomething(inputParams, foobar); //mutate the map
You're returning different data structures..
List<Bar> foo = doSomething(inputParams);
Map<Integer,String> foobar = doSomethingDifferent(inputParams);
Are you sure they do similar things? If so, you could extract common part or change them to return same type and after that it will be easily seen what you can do not to duplicate code.
What your example methods do is different enough to have different method names. E.g. createListFrom(param), createMapFrom(param). Combining that with the same name is just confusing. And I would not count calling doSomething in place and doSomethingElse in another place repetition. The broader concept is maybe repeating.
One way to approach this is to move duplicate code into another method. Same / similar code is
while(condition) {}
Bar b = new Bar()
add b somehow to some sort of collection.
The generic version of your methods could look like
private void doSomethingGeneric(? param, GenericWayToHandle handler) {
while (condition) {
Bar b = createBar();
handler.doSomethingWith(b);
}
}
"Short" example how you could implement that
public List<Object> doSomethingList(int param) {
ListHandler handler = new ListHandler();
doSomethingGeneric(param, handler);
return handler.list;
}
public Map<Object, Object> doSomethingMap(int param) {
MapHandler handler = new MapHandler();
doSomethingGeneric(param, handler);
return handler.map;
}
private void doSomethingGeneric(int param, CollectionHandler handler) {
for (int i = 0; i < param; i++) {
handler.handle("Hello");
}
}
private interface CollectionHandler {
void handle(String string);
}
private static class MapHandler implements CollectionHandler {
public final Map<Object, Object> map = new HashMap<Object, Object>();
#Override
public void handle(String string) {
map.put(string, string);
}
}
private static class ListHandler implements CollectionHandler {
public final List<Object> list = new ArrayList<Object>();
#Override
public void handle(String string) {
list.add(string);
}
}
It's unfortunately pretty ugly to handle for each situations like that and Java 8 will simplify that via closures.
The other way to approach your problem is to use the output of one method to derive another version from it.
e.g. (very similar to what you posted)
List<Bar> bars = createBarList(inputParams); // doSomething
Map<Integer,String> foobar = deriveMap(bars); // doSomethingSimilar
where deriveMap would just iterate over the list and create a map. That's the place where this code would be now
for(Bar b: input)
ret.put(b.getId(),b.getName());
It's ultimately up to you what those methods do and how they should be used. The name of a method can help a lot to express the intention & help to use them correctly. Don't give that up by merging functionality into magical functions that behave unpredictably when you don't know the source code.
One other other thing: broader refactoring can often get rid of weird structures. Maybe you can encapsulate the whole (list & map of Bars)-thing into it's own class. Specialized data-structures are very often a good candidate to be moved out of a class that uses them. A class that is responsible to handle a map and a list of things could be seen as responsible for doing more than one thing -> Single responsibility principle
I have a class, lets call it Fruit, and I have a HashMap. I want to be able to initialize a new instance of Fruit, but set to the values in HashMap. So for example:
Map<String, String> map = new HashMap<String, String>();
map.put("name", "Banana");
map.put("color", "Yellow");
Then I want to be initialize a new Fruit instance like so:
Fruit myFruit = new Fruit(map);
or
Fruit myFruit = (Fruit)map;
Is this possible in Java, by means of iterating the Map?
The second is not possible because a HashMap is not a Fruit. You could do the first by providing a constructor that takes a Map<String, String> argument.
public Fruit(Map<String, String> map) {
this.name = map.get("name");
this.color = map.get("color");
}
It seems like you can use reflection for this
Fruit f = new Fruit();
Class aClass = f.getClass();
for(Field field : aClass.getFields()){
if(map.containsKey(field.getName())){
field.set(f,map.get(field.getName()));
}
}
Little old but:
import com.fasterxml.jackson.databind.ObjectMapper;
...
final ObjectMapper mapper = new ObjectMapper();
final Map myObjectMapped = new HashMap();
//fill map
final Class clazz = Class.forName(MyClassToBeConverted.class.getName());
final MyClassToBeConverted convertedObj = (MyClassToBeConverted) mapper.convertValue(myObjectMapped, clazz);
...
Yes, it's possible. But you'd have to write a constructor for Fruit that knows how to pull values -- and which values -- from the map.
public Fruit(Map params) {
this.setColor(map.get("color"));
this.setName(map.get("name"));
}
I have fixed Anni's solution, now it supports inheritance, and static and final fields.
By the way, I have not checked for type mismatches.
public static void populateBean(Object bean, Map<String, Object> properties) throws Exception {
Class<?> clazz = bean.getClass();
while(clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifier) && !Modifier.isFinal(modifier)) {
if (map.containsKey(field.getName())) {
field.accessible(true);
field.set(bean, map.get(field.getName()));
}
}
}
clazz = clazz.getSuperclass();
}
}
By the way Apache BeanUtils DynaBeans almost does what you want, as far as I remember it supports Java Beans Introspection.
Maybe it could be a little slower in comparison to other solutions, but for not demanding purposes, my code works very well for me (And it is very simple and clean):
public class Utils {
static Object parseHashMapToObject(HashMap map, Class cls) {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
String jsonString = gson.toJson(map);
return gson.fromJson(jsonString, cls);
}
}
Gson Github: https://github.com/google/gson
You would traverse the map in your constructor and assign the values. If there's an actual library for doing this(almost like a Bean), then I've never heard of it.
Casting of a HashMap to a fruit wouldn't be possible.
The second is not possible but you can create a class that will take a Map as a constructor parameter.
class Fruit{
private Map<String, String> fruitMap;
Fruit(Map<String, String> map){
}
}
Assuming the keys in map correspond to setter methods in the Fruit class, you could use one of Apache bean's utilities like PropertyUtils.
final Fruit f = new Fruit();
for(String key : map.keySet()) {
PropertyUtils.setProperty(fruit, key, map.get(key));
}
For very complicated cases of this you might want to take a look at Dozer. We use Dozer to map very large Maps to very large objects.