I am trying to create an object based on a condition, therefore the object creation is within the conditional's scope, however I need to see the object outside of that scope. I thought adding it to a Map would work, but it doesn't. Consider the following example:
TestModel.java
public class TestModel {
private String text;
public void setText(String text){
this.text = text;}
public String getText(){
return this.text;}
}
ScopeTest.java
import java.util.*;
class ScopeTest {
public static void main(String[] args) {
TestModel testModel;
Map<String, Object> myModel = new HashMap<String, Object>();
for (int i=1; i<2; i++){ // if a certain condition is met, create an object as below
testModel = new TestModel();
testModel.setText("test text");
myModel.put("test", testModel);
}
for (Map.Entry<String, Object> entry : myModel.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
System.out.println("key=" + key); // I can see the key value
System.out.println("value.getText()=" + value.getText()); // but can't see testModel object. I am not sure how to resolve.
}
}
}
cheers,
Geofrey Rainey.
You have to cast the Object value with your Class. Like this.
System.out.println("value.getText()=" + ((TestModel) value).getText());
If you dont want to cast the object then you can use like this.
class ScopeTest {
public static void main(String[] args) {
TestModel testModel;
Map<String, TestModel> myModel = new HashMap<String, TestModel>();//Use TestModel
instead of object
for (int i=1; i<2; i++){
testModel = new TestModel();
testModel.setText("test text");
myModel.put("test", testModel);
}
for (Entry<String, TestModel> entry : myModel.entrySet()) {
String key = entry.getKey();
TestModel value = entry.getValue();
System.out.println("key=" + key);
System.out.println("value.getText()=" + value.getText());
}
}
}
You should cast the Object into your model.
TestModel value = (TestModel) entry.getValue();
I think you might have to cast the object returned by the HashMap to be a TestModel before you can use a method of that class.
Why not just use TestModel as generic type of value in your Map like below?
Map<String, TestModel> myModel = new HashMap<>();
// ^^^^^^^^^ - instead of Object
This way you can iterate over your map with
for (Map.Entry<String, TestModel> entry : myModel.entrySet()) {
...
}
and you will be able to store value in
TestModel value = entry.getValue();
without needing to cast it. Now since type of value reference will be TestModel compiler will let you use its methods like getText() without problems.
System.out.println("value.getText()=" + value.getText());
Also I am not sure why you are using loop if you want to iterate over it once. Simple if would be better. Another thing is using Map to hold one element seems unnecessary. You can just use one reference like
boolean someCondition = true;
TestModel testModel = null;
if (someCondition) { // if a certain condition is met, create
testModel = new TestModel();
testModel.setText("test text");
}
if (testModel!=null){
System.out.println(testModel.getText());
}
Related
I want to add an object to an array. If the data of other_amount is more than zero I want to add one object more. If it's equal to zero, it should add nothing. This is my code:
JSONArray acc_data = new JSONArray();
Map<String, Object> myaccount = new LinkedHashMap<>();
for (int i = 0; i < mpay.size(); i++) {
if(other_amount>0){
myaccount.put("poAccount", other_account);
myaccount.put("poAmount", other_amount);
system.out.println(myaccount);
//{poAccount=050017, poAmount=12}
}
myaccount.put("poAccount", amount_account);
myaccount.put("poAmount", amount);
system.out.println(myaccount);
//{"poAccount":"050016","poAmount":"800"}
acc_data.add(myaccount);
system.out.println(acc_data);
//[{"poAccount":"050016","poAmount":"800"}]
}
But I need it like this:
//[{"poAccount":"050016","poAmount":"800"},{poAccount=050017, poAmount=12}]
please help me to resolve it.
You shouldn't use map for your case.
When your put the pair with existing in map key, the pair will be overwrited.
For example
map.put ("k1","v1");
Map contains one pair "k1":"v1"
The next call
map.put ("k1","newV1");
The first pair will be overwrited and map still contains 1 pair: "k1":"newV1"
For your case it's better to define simple POJO class with 2 fields poAccount and poAmount. And add them to the JSONArray
The approach you are following, it will not serve your requirement. You should use pojo to store the records and then populate the Json Array. You can have a look at this code and modify as per your requirements.
public class Test {
public static void main(String[] args) {
Mypojo mypojo = new Mypojo();
Gson gson = new Gson();
JSONArray records = new JSONArray();
for (int i = 0; i < 1; i++) {
if (5 > 0) {
mypojo.setPoAccount("050017");
mypojo.setPoAmount("12");
JSONObject objects = new JSONObject(gson.toJson(mypojo));
records.put(objects);
}
mypojo.setPoAccount("050016");
mypojo.setPoAmount("800");
JSONObject objects = new JSONObject(gson.toJson(mypojo));
records.put(objects);
}
System.out.println(records);
}
}
Mypojo Class :
public class Mypojo
{
private String poAmount;
private String poAccount;
public String getPoAmount ()
{
return poAmount;
}
public void setPoAmount (String poAmount)
{
this.poAmount = poAmount;
}
public String getPoAccount ()
{
return poAccount;
}
public void setPoAccount (String poAccount)
{
this.poAccount = poAccount;
}
#Override
public String toString()
{
return "ClassPojo [poAmount = "+poAmount+", poAccount = "+poAccount+"]";
}
}
Output of the below code is :
This is Raja from ${Address.Street} i did my ${Education.degree} from ${Education.university}
but what I need is
This is Raja from Namakkal i did my B.E from Anna University
is it possible to achieve by using Freemarker, OGNL or by using spring.
public class Test
{
public static void main(String arg[]) throws TemplateModelException
{
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> address = new HashMap<String, Object>();
address.put("Street", "Namakkal");
Qualification qualification = new Test.Qualification();
map.put("Name", "Raja");
map.put("Address", address);
map.put("Education", qualification);
StrSubstitutor strsub = new StrSubstitutor(map);
String str = "This is ${Name} from ${Address.Street} i did my ${Education.degree} from ${Education.university}";
System.out.println(strsub.replace(str));
}
public static class Qualification
{
public String getDegree()
{
return "B.E";
}
public String getUniversity()
{
return "Anna University";
}
}
}
please explain the simplest and effective way to achieve this.
If you want to use StrSubstitutor itself, you could try using a custom variable resolver by extending the StrLookUp class.
Example:
StrSubstitutor strsub = new StrSubstitutor(new CustomLookUp(map));
...
...
private static class CustomLookUp extends StrLookup<Object> {
Map<String, Object> map = new HashMap<String, Object>();
public CustomLookUp(Map<String, Object> map) {
this.map = map;
}
#Override
public String lookup(String key) {
// ...
// Logic for resolving your variables.
// ...
}
}
You could do it with freemarker, using the StringTemplateLoader class. This class allows you to create templates from Strings, instead of reading them from files.
Without freemarker you can use the following:
String str="This is "+(String)map.get("Name")+" from "+((Map)map.get("Address")).get("Street")+". I did my "+((Qualification)map.get("Education")).getDegree()+" from "+((Qualification)map.get("Education")).getUniversity()+".";
#Anoop example implementation with spring reflection
#Override
public String lookup(String key) {
String[] keys = key.split("\\.");
Object obj = map.get(keys[0]);
for (int i = 1; i < keys.length; i++) {
Class<?> clazz = obj.getClass();
Field field = null;
try {
field = org.springframework.util.ReflectionUtils.findField(clazz, (keys[i]));
org.springframework.util.ReflectionUtils.makeAccessible(field);
obj = field.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return (String) obj;
}
This is my first question in Stackoverflow.I have come to find a issue with one of the problem suggested and give to me by my colleague to do some research on it.
My question is
i have a class
Class Function{
String func;
String funcname;
boolean log;
}
i have created some objects:
obj1 : ("a" ,"b",true)- //these values come from either DB or UI
obj2 : ("c" ,"x",true)
obj3 : ("a" ,"z",true)
i have a list:
List<function> flist;
now i want to have that list in the map and want to put in inside the map
Map<String, List<function>> funcMap
and then display this following output:
a:[obj1 obj3]
b:[obj2]
if i have the list but how to go about and find the above output as desired
Try this,
add all the objects in the flist.
initialize the map
Map<String, List<Function>> funcMap = new HashMap<String, List<Function>>();
going to add the object to the relevant key based on the func value the object will add to the value list.
for (Function functionValue : flist)
{
List<Function> functionList = funcMap.get(functionValue.getFunc());
if (functionList != null && !functionList.isEmpty())
{
functionList.add(functionValue);
}
else
{
functionList = new ArrayList<Function>();
functionList.add(functionValue);
funcMap.put(functionValue.getFunc(), functionList);
}
}
Atlast print the funcMap
for (Map.Entry< String, List<Function>> entry : funcMap.entrySet())
{
System.out.println("Key : " + entry.getKey() + "Values : "+entry.getValue());
}
Hmm.. I think it's a case of parsing your list in a nested loop kind of way. Here is the pseudo-code:
public void listToMap(List<Function> list)
{
Map<String, List<Function>> map := new Map
for every function in the list.
{
if(is the current function's func value does not exist in the map)
{
func := current functions func value
List matchingFunctions := new list of Functions.
for(every function in the list.)
{
// Every Function with the same key get's added to a list.
if(function has the same func value as func)
{
add to matchingFunctions.
}
}
// That list and key get put into the HashMap.
map.put(func, matchingFunctions).
}
}
}
A Note on your code design
Java convention states that you should wrap your member objects up in getters and setters, and that those members should be private.
what about:
public class FuncTest {
public static void main(String[] args) {
new FuncTest().start();
}
private void start() {
List<Function> flist = new ArrayList<Function>();
flist.add(new Function("a", "b", true));
flist.add(new Function("c", "x", true));
flist.add(new Function("a", "z", true));
Map<String, List<Function>> funcMap = new HashMap<String, List<Function>>();
for (Function func : flist) {
this.add(func.func, func, funcMap);
this.add(func.funcname, func, funcMap);
}
}
private void add(String field, Function func, Map<String, List<Function>> funcMap) {
List<Function> subList = funcMap.get(field);
if (subList == null) {
subList = new ArrayList<Function>();
funcMap.put(field, subList);
}
subList.add(func);
}
}
Note
As already mentioned by Chris you should think about your code design. Use getters and setters ..
public class Stackoverflow {
public static void main(String[] args) {
Function obj1 = new Function("a" ,"b",true);
Function obj2 = new Function("c" ,"x",true);
Function obj3 = new Function("a" ,"z",true);
List<Function> functionsList1 = new ArrayList<Function>();
functionsList1.add(obj1);
functionsList1.add(obj3);
List<Function> functionsList2 = new ArrayList<Function>();
functionsList2.add(obj2);
Map<String, List<Function>> funcMap = new LinkedHashMap<String, List<Function>>();
funcMap.put("a", functionsList1);
funcMap.put("b", functionsList2);
Set<Entry<String,List<Function>>> entrySet = funcMap.entrySet();
for (Entry<String, List<Function>> entry : entrySet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
class Function {
String func;
String funcname;
boolean log;
public Function(String func, String funcname, boolean log) {
super();
this.func = func;
this.funcname = funcname;
this.log = log;
}
#Override
public String toString() {
return "Function [func=" + func + ", funcname=" + funcname + ", log="
+ log + "]";
}
}
Write your own map.
Pass the list to map, let map will decide what portion of list to keep as value.
I have added put method here, like the same, have to Override other methods.
class MyHashMap<K,V> extends HashMap<K,V>{
#SuppressWarnings("unchecked")
public V put(K k, V v) {
String key = (String)k;
List<Function> list = (List<Function>) v;
List<Function> list2 = new ArrayList<Function>();
for (Function function : list) {
if(key.equalsIgnoreCase(function.func)){
list2.add(function);
}
}
return (V) list2;
};
#Override
public boolean equals(Object o) {
// Your own code
return true;
}
// other methods goes here..
}
I create object which contain feature geometry and attributes:
public class Feature {
Feature(String wkt) {
this.wkt = wkt;
}
private HashMap<Column, String> columnMap;
private String wkt;
public String getWKT() {
return wkt;
}
public void addAttribute(Column column, String value) {
columnMap.put(column, value);
}
public String getAttribute(String column) {
return columnMap.get(column) ;
}
public Map<Column, String> getAttributes(){
return columnMap;
}
}
Wkt is a geometry. ColumnMap is object contain a attributes as HashMap:
public class Column {
private String columnName;
Column(String columnName) {
this.columnName = columnName;
}
public String getName() {
return columnName;
}
}
Now i says:
columnList = new ArrayList<Column>(columns);
......
Feature feature= new Feature(WKT);
for(int p=0;p<columnList.size();p++){
for(int k=0;k<=ViewObject.getMIDInfo(totalObjects).length;k++){
if(p==k){
System.out.println("Column "+columnList.get(p).getName()+" Value "+ ViewObject.getMIDInfo(totalObjects)[k].toString());
//feature.addAttribute(columnList.get(p), ViewObject.getMIDInfo(totalObjects)[k].toString());
}
}
}
And get output:
Column id Value 22
Column kadnumm Value "66-41-0707001-19"
So how i understand columnList and ViewObject.getMIDInfo(totalObjects) is not empty. After this i change :
//feature.addAttribute(columnList.get(p), ViewObject.getMIDInfo(totalObjects)[k].toString());
to:
feature.addAttribute(columnList.get(p), ViewObject.getMIDInfo(totalObjects)[k].toString());
And get exeption:
Column id Value 22
java.lang.NullPointerException
at objects.Feature.addAttribute(Feature.java:18)
at objects.MIFParser.findRegion(MIFParser.java:181)
at objects.MIFParser.instanceNextObject(MIFParser.java:66)
at Read.main(Read.java:40)
How i understand NullPointerException means that i try to use empty objects? Whats wrong?
P.s. Sorry my english can be terrible especially with title .
UPDATE
Okey i add this: this.columnMap = new HashMap<Column, String>(); in FEature class constructor.
But now i try to do:
System.out.println(feature.getAttribute("id")+" "+feature.getAttribute("kadnumm"));
and output:
null null
What can be wrong?
You didnt initialize your columnMap:
private HashMap<Column, String> columnMap = new HashMap<Column, String>();
addAttribute tries to put something on columnMap, but you don't create columnMap anywhere. You need to add to your Feature constructor:
Feature(String wkt) {
this.wkt = wkt;
this.columnMap = new HashMap<Column, String>(); // <=== The new bit
}
...or add an initialization to your declaration:
private HashMap<Column, String> columnMap = new HashMap<Column, String>();
// The new bit--- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Just declaring the member isn't sufficient, the member just refers to an object, and starts off null. You need to create the object for it to refer to and assign that object to it.
columnMap object is not initialized when you create a new instance of Feature. So it is null when you call columnMap.put(column, value); in addAttribute
instead of
private HashMap<Column, String> columnMap;
do
private HashMap<Column, String> columnMap = new HashMap<Column, String>();
You must initialize the map:
private HashMap<Column, String> columnMap = new HashMap<Column, String>();
Is there a version of BeanUtils.describe(customer) that recursively calls the describe() method on the complex attributes of 'customer'.
class Customer {
String id;
Address address;
}
Here, I would like the describe method to retrieve the contents of the address attribute as well.
Currently, all I have can see the name of the class as follows:
{id=123, address=com.test.entities.Address#2a340e}
Funny, I would like the describe method to retrieve the contents of nested attributes as well, I don't understand why it doesn't. I went ahead and rolled my own, though. Here it is, you can just call:
Map<String,String> beanMap = BeanUtils.recursiveDescribe(customer);
A couple of caveats.
I'm wasn't sure how commons BeanUtils formatted attributes in collections, so i went with "attribute[index]".
I'm wasn't sure how it formatted attributes in maps, so i went with "attribute[key]".
For name collisions the precedence is this: First properties are loaded from the fields of super classes, then the class, then from the getter methods.
I haven't analyzed the performance of this method. If you have objects with large collections of objects that also contain collections, you might have some issues.
This is alpha code, not garunteed to be bug free.
I am assuming that you have the latest version of commons beanutils
Also, fyi, this is roughly taken from a project I've been working on called, affectionately, java in jails so you could just download it and then run:
Map<String, String[]> beanMap = new SimpleMapper().toMap(customer);
Though, you'll notice that it returns a String[], instead of a String, which may not work for your needs. Anyway, the below code should work, so have at it!
public class BeanUtils {
public static Map<String, String> recursiveDescribe(Object object) {
Set cache = new HashSet();
return recursiveDescribe(object, null, cache);
}
private static Map<String, String> recursiveDescribe(Object object, String prefix, Set cache) {
if (object == null || cache.contains(object)) return Collections.EMPTY_MAP;
cache.add(object);
prefix = (prefix != null) ? prefix + "." : "";
Map<String, String> beanMap = new TreeMap<String, String>();
Map<String, Object> properties = getProperties(object);
for (String property : properties.keySet()) {
Object value = properties.get(property);
try {
if (value == null) {
//ignore nulls
} else if (Collection.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertAll((Collection) value, prefix + property, cache));
} else if (value.getClass().isArray()) {
beanMap.putAll(convertAll(Arrays.asList((Object[]) value), prefix + property, cache));
} else if (Map.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertMap((Map) value, prefix + property, cache));
} else {
beanMap.putAll(convertObject(value, prefix + property, cache));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return beanMap;
}
private static Map<String, Object> getProperties(Object object) {
Map<String, Object> propertyMap = getFields(object);
//getters take precedence in case of any name collisions
propertyMap.putAll(getGetterMethods(object));
return propertyMap;
}
private static Map<String, Object> getGetterMethods(Object object) {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info;
try {
info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null) {
String name = pd.getName();
if (!"class".equals(name)) {
try {
Object value = reader.invoke(object);
result.put(name, value);
} catch (Exception e) {
//you can choose to do something here
}
}
}
}
} catch (IntrospectionException e) {
//you can choose to do something here
} finally {
return result;
}
}
private static Map<String, Object> getFields(Object object) {
return getFields(object, object.getClass());
}
private static Map<String, Object> getFields(Object object, Class<?> classType) {
Map<String, Object> result = new HashMap<String, Object>();
Class superClass = classType.getSuperclass();
if (superClass != null) result.putAll(getFields(object, superClass));
//get public fields only
Field[] fields = classType.getFields();
for (Field field : fields) {
try {
result.put(field.getName(), field.get(object));
} catch (IllegalAccessException e) {
//you can choose to do something here
}
}
return result;
}
private static Map<String, String> convertAll(Collection<Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
Object[] valArray = values.toArray();
for (int i = 0; i < valArray.length; i++) {
Object value = valArray[i];
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + i + "]", cache));
}
return valuesMap;
}
private static Map<String, String> convertMap(Map<Object, Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
for (Object thisKey : values.keySet()) {
Object value = values.get(thisKey);
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + thisKey + "]", cache));
}
return valuesMap;
}
private static ConvertUtilsBean converter = BeanUtilsBean.getInstance().getConvertUtils();
private static Map<String, String> convertObject(Object value, String key, Set cache) {
//if this type has a registered converted, then get the string and return
if (converter.lookup(value.getClass()) != null) {
String stringValue = converter.convert(value);
Map<String, String> valueMap = new HashMap<String, String>();
valueMap.put(key, stringValue);
return valueMap;
} else {
//otherwise, treat it as a nested bean that needs to be described itself
return recursiveDescribe(value, key, cache);
}
}
}
The challenge (or show stopper) is problem that we have to deal with an object graph instead of a simple tree. A graph may contain cycles and that requires to develop some custom rules or requirements for the stop criteria inside the recursive algorithm.
Have a look at a dead simple bean (a tree structure, getters are assumed but not shown):
public class Node {
private Node parent;
private Node left;
private Node right;
}
and initialize it like this:
root
/ \
A B
Now call a describe on root. A non-recursive call would result in
{parent=null, left=A, right=B}
A recursive call instead would do a
1: describe(root) =>
2: {parent=describe(null), left=describe(A), right=describe(B)} =>
3: {parent=null,
{A.parent=describe(root), A.left=describe(null), A.right= describe(null)}
{B.parent=describe(root), B.left=describe(null), B.right= describe(null)}}
and run into a StackOverflowError because describe is called with objects root, A and B over and over again.
One solution for a custom implementation could be to remember all objects that have been described so far (record those instances in a set, stop if set.contains(bean) return true) and store some kind of link in your result object.
You can simple use from the same commom-beanutils:
Map<String, Object> result = PropertyUtils.describe(obj);
Return the entire set of properties for which the specified bean provides a read method.