I have myArrayList that already have values in it. For this example lets say there are only two elements in it(firstName and lastName). I need to get values from myArrayList and compare them to String and if it matches then get value from the bean and put it into map:
Map<String,String> myMap;
for(String element: myArrayList){
if(element.equalsIgnoreCase("firstName")){
myMap.put("firstName", bean.getFirstName());
}else if(element.equalsIgnoreCase("lastName")){
myMap.put("lastName", bean.getLastName());
}
}
The problem is when you have thirty-forty elements in myArrayList you will have performance issues(I assume), and it just doesn't feel right.
I tried this:
String name = null;
String value = null;
for(int i = 0; i < myArrayList.size(); i++){
name = myArrayList.get(i);
value = bean.get(name);
myMap.put(name, value);
}
But the line "value = bean.get(name);" is saying that method get(String) is undefined in bean class, indeed we don't have such method in bean class, it has only standard getter and setter methods:
public class Bean implements Serializable {
private String firstName;
private String lastName;
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
}
Now I am thinking how I could come up with some design pattern that optimizes my logic and doesn't affect performance of the code. Please feel free to ask questions, I will edit if you need more info. Any help is greatly appreciated. Thanks.
Edit: shmosel's answer was pretty good for me, thank you all for your help! Cheers!
#HankD and #Natalia have offered some valid solutions, but another option I don't see mentioned is refactoring Bean to support a get(String) method:
public class Bean implements Serializable {
private Map<String, String> properties = new HashMap<>();
public String get(String property) {
return properties.get(property);
}
public void set(String property, String value) {
properties.put(property, value);
}
public String getFirstName(){
return get("firstName");
}
public void setFirstName(String firstName){
set("firstName", firstName);
}
public String getLastName(){
return get("lastName");
}
public void setLastName(String lastName){
set("lastName", lastName);
}
}
You can try to use reflection see javaDoc
However, I'll not recomend to use it, until it is really required. Possible, you should refactor you code to avoid having list of fields, you need to get.
If you decide you use reflection, there is ReflectionUtils in springframework
Your get(String) method is a really good idea, it just needs to be done right. Here's how I would do it, it's very similar to what you did outside of the Bean, but it allows separation of concerns, which is a good thing.
public String get(String field) {
switch(field.toLowerCase()) {
case "firstname":
return firstName;
case "lastname":
return lastName;
default:
throw new IllegalArgumentException(field + " is an invalid field name.");
}
}
I'm using a switch statement here because the Java Docs note that:
The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.
If you can't change your Bean class, then you should at least use this logic inside your loop instead of your current logic simply for the better speed of switch statements and calling toLowerCase() once instead of using equalsIgnoreCase() multiple times.
This seems like a strange way to do things. As 4castle points out, a few elements are not likely to cause performance problems in itself. Are you experiencing performance problems?
If it was me, I'd do something like this:
public static final String lastNameValue = "lastname";
for(String element: myArrayList){
if(element != null) element = element.toLowerCase();
if(lastNameValue.equals(element){
myMap.put("lastName", bean.getLastName());
} ....
}
The constants prevent the construction of a new String each time this method is called. You are not checking for a null element. Doing the toLowerCase() once is more efficient than doing it a number of times.
Related
edit:
I am looking to pass HomeBuyers Objects to my homeBuyers ArrayList in my CreditUnion class. The this.homeBuyers.add function throws a "cannot find symbol" error. I have tried making the firstName, lastName, and creditScore vars static and passing the object in with HomeBuyers(HomeBuyers.firstName, HomeBuyers.lastName, HomeBuyers.creditScore). That avoided errors but did not add the object to the ArrayList.
public class LabProgram {
public static void main(String[] args) {
CreditUnion cu = new CreditUnion();
cu.addHomeBuyer(new HomeBuyers("first","last",600));
}
}
public class HomeBuyers {
public String firstName;
public String lastName;
public int creditScore;
public HomeBuyers(String firstName, String lastName, int creditScore) {
this.firstName = firstName;
this.lastName = lastName;
this.creditScore = creditScore;
}
}
public class CreditUnion {
public ArrayList<HomeBuyers> homeBuyers;
public CreditUnion() {
this.homeBuyers = new ArrayList<>();
}
public void addHomeBuyer(HomeBuyers homeBuyers) {
this.homeBuyers.add(HomeBuyers(firstName, lastName, creditScore));
}
You have multiple problems here.
The right syntax for creating a new object is new HomeBuyers(...). Not HomeBuyers().
Separately, firstName doesn't exist as a variable in your addHomeBuyer method. The only variable that does exist is homeBuyers. You could write homeBuyers.firstName. However, you already have an instance of your class HomeBuyers. There is no need to make another one with identical values. this.homeBuyers.add(homeBuyers) would have done the job.
These questions are incredibly simplistic; stack overflow probably isn't the right venue. I suggest any of the many nice tutorials out there, they tend to take a little more time to explain things in an orderly fashion.
String variable - "isInitial" in below example is not getting converted into JSON. Is it possible to generate JSON structure for isInitial variable without modifying the EmployeePOJO class?
PS: converting the getter method to getIsInitial works.
Please find below POJO class used for generating the JSON:
public class EmployeePOJO {
private String firstName;
private String lastName;
private String isInitial;
private boolean isFirstNameAvailable;
private boolean hasLastNameAvailable;
private String hasHouse;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String isInitial()
{
return this.isInitial;
}
public void setInitial(String pInitial)
{
this.isInitial = pInitial;
}
public String getHasHouse() {
return hasHouse;
}
public void setHasHouse(String hasHouse) {
this.hasHouse = hasHouse;
}
public boolean isFirstNameAvailable() {
return isFirstNameAvailable;
}
public void setFirstNameAvailable(boolean firstNameAvailable) {
isFirstNameAvailable = firstNameAvailable;
}
public boolean isHasLastNameAvailable() {
return hasLastNameAvailable;
}
public void setHasLastNameAvailable(boolean hasLastNameAvailable) {
this.hasLastNameAvailable = hasLastNameAvailable;
}
}
Please find below main logic for POJO conversion:
import org.codehaus.jackson.map.ObjectMapper;
import java.io.IOException;
public class JacksonPojoToJson {
public static void main(String[] args) throws IOException {
// Create ObjectMapper
ObjectMapper mapper = new ObjectMapper();
EmployeePOJO employeePOJO = new EmployeePOJO();
employeePOJO.setFirstName("FIRST NAME");
employeePOJO.setFirstNameAvailable(true);
employeePOJO.setLastName("last name");
employeePOJO.setHasLastNameAvailable(true);
employeePOJO.setInitial("true");
// Convert object to JSON string
String json = mapper.writeValueAsString(employeePOJO);
System.out.println(json);
}
}
Bad naming conventions. If you are using IDE let the IDE generate the getters and setters for you.
The isInitial method can be used as getter for a Boolean variable. Not a String variable.
So you can fix your POJO class (https://en.wikipedia.org/wiki/Plain_old_Java_object)
or you can use the #JsonProperty annotation (https://fasterxml.github.io/jackson-annotations/javadoc/2.8/com/fasterxml/jackson/annotation/JsonProperty.html)
Use Lombok maven dependency for generate automatic getter & setter.
You have to use the correct naming of getters and setters for your variable for example:
private String isInitial;
For the above variable getter setter should be:
public String getIsInitial()
{
return this.isInitial;
}
public void setIsInitial(String isInitial)
{
this.isInitial = isInitial;
}
Note: I don't know your use case but name like isXXXX used for getters of boolean variables.
I would recommend using Lombok's #Getter and #Setter annotations to auto-generate your getter and setter methods. Using these, you will not run into issues with naming conventions etc.
Please see the following URL for more information:
https://projectlombok.org/features/GetterSetter
#Loizos - Thanks a lot for the suggestion #JsonProperty, it worked fine. I was looking at a solution without modifying the class - EmployeePOJO (since it is legacy code which we dont want to touch).
I got this resolved by using Mixins, which does not require modification in EmployeePOJO file.
Sample code which worked for me:
public interface EmployeePOJOMixIn {
#JsonProperty("isInitial")
public abstract String isInitial();
}
Then adding this Mixin to mapper (this will vary based on whether you are using fasterxml or codehaus)
https://www.leveluplunch.com/java/tutorials/024-modifying-fields-external-domain-jackson-mixin/ and https://medium.com/#shankar.ganesh.1234/jackson-mixin-a-simple-guide-to-a-powerful-feature-d984341dc9e2 are good sites which I referred for the solution.
The reason for this is that the setters/getters for boolean variables do not follow the pattern setVariableName/getVariableName.
You could see in your own code that it is like this:
public String isInitial()
{
return this.isInitial;
}
public void setInitial(String isInitial)
{
this.isInitial = isInitial;
}
Quick fix would be to create the setters/getters for boolean variables manually.
This (setter/getter creation issue) usually happens in eclipse, but I think IntelliJ handles this right.
This question already has answers here:
Invoking all setters within a class using reflection
(4 answers)
Closed 5 years ago.
I have a POJO object and a collection of appropriate data.
import java.util.ArrayList;
import java.util.List;
public class TestPojo {
private String name;
private String number;
private String id;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static void main(String[] args) {
TestPojo test = new TestPojo();
List<String> sampleData = new ArrayList<>();
sampleData.add("Bob");
sampleData.add("641-613-623");
sampleData.add("id-1451");
sampleData.add("Male");
test.setName(sampleData.get(0));
test.setNumber(sampleData.get(1));
test.setId(sampleData.get(2));
test.setSex(sampleData.get(3));
}
}
My question is how can i fill my POJO object with data in a loop? Is it posible to iterate all object setters and set data from List in appropriate places? I know that reflection can help in this case.
Here is an simple example to call setters via reflection (which needs to be adjusted):
[if this is a good approach, is another question. But to answer your question:]
public static void main(String[] args) throws Exception
{
//this is only to demonstrate java reflection:
Method[] publicMethods = TestPojo.class.getMethods(); //get all public methods
TestPojo testObj = TestPojo.class.newInstance(); //when you have a default ctor (otherwise get constructors here)
for (Method aMethod : publicMethods) //iterate over methods
{
//check name and parameter-count (mabye needs some more checks...paramter types can also be checked...)
if (aMethod.getName().startsWith("set") && aMethod.getParameterCount() == 1)
{
Object[] parms = new Object[]{"test"}; //only one parm (can be multiple params)
aMethod.invoke(testObj, parms); //call setter-method here
}
}
}
You can also save all setter-methods in an list/set for later re-use...
But as others already said, you have to be careful by doing so (using reflection)!
Cheers!
You can't easily - and you shouldn't.
You see, your POJO class offers some setters. All of them have a distinct meaning. Your first mistake is that all of these fields are strings in your model:
gender is not a string. It would rather be an enum.
"number" is not a string. It should rather be int/long/double (whatever the idea behind that property is)
In other words: you premise that "input" data is represented as array/list is already flawed.
The code you have written provides almost no helpful abstractions. So - instead of worrying how to call these setter methods in some loop context - you should rather step back and improve your model.
And hint: if this is really about populating POJO objects from string input - then get your string into JSON format, and use tools such as gson or jackson to do that (reflection based) mapping for you.
"Iterating over methods" seems pretty much of a wrong idea in OO programming. You could simply add a constructor to your class setting all of your attributes, and then just call that constructor in a loop as desired to create new objects with data you desire.
In your class define:
public TestPojo(String name, String number, String id, String sex){
this.name = name;
this.number = number;
this.id = id;
this.sex = sex;
}
Also using a List makes no much sense here. I'd recommend using a HashMap to then iterate over it in a for loop making proper calls of the above constructor.
Countless times I've read that public properties on a class are a major faux pas, but I fail to see why on data that doesn't get transformed going in/out.
example of something I would write
public class Employee
{
public String firstName;
public String lastName;
private int ssn = 0;
public boolean setSsn(String s)
{
//makes perfect sense why something like an ssn would use a getter setter
//some code to validate ssn
if(!validSsn(s)){
ssn = 0;
return false;
}
ssn = raw;
return true;
}
public String getSsn()
{
return ssn;
}
private boolean validSsn(String s)
{
//validation goes here
retrun val;
}
//I don't understand why I should make firstName private
// and then write
public void setFirstName(String s)
{
firstName = s;
}
public String getFirstName(String s)
{
return firstName;
}
}
please provide a scenario in which this would cause a problem and please be specific, not "because it causes issues when other people use your code" WHY? Why does it cause issues. Thanks in advance for constructive criticism and detailed answers :D
Imagine that your code has gone to production. I write a front-end that uses your Employee class that accesses firstName and lastName directly.
6 months go by, and now there's a new business requirement that you add validation to the name fields.
Where are you going to add it? You're going to have to make the fields private and add set methods, and this will force me and everyone else using your code to re-write our apps.
If you had encapsulated that data properly in the first place, all we'd have to do is recompile using the new version of the library with your code.
enum generalInformation {
NAME {
#Override
public String toString() {
return "Name";
}
},
EDUCATION {
#Override
public String toString() {
return "Education";
}
},
EMAIL {
#Override
public String toString() {
return "Email";
}
},
PROFESSION {
#Override
public String toString() {
return "Profession";
}
},
PHONE {
#Override
public String toString() {
return "Phone";
}
}
}
I have that information are avaiable in enum.
How to print all enum values like: print.generalInformation?
That outputs:
Name
Education
Email
Phone
How to pass that enum generalInformation as an arg in another function?
System.out.println(java.util.Arrays.asList(generalInformation.values()));
Your second part... Just the same as an interface or a class
Firstly, I would refactor your enum to pass the string representation in a constructor parameter. That code is at the bottom.
Now, to print all enum values you'd just use something like:
// Note: enum name changed to comply with Java naming conventions
for (GeneralInformation info : EnumSet.allOf(GeneralInformation.class)) {
System.out.println(info);
}
An alternative to using EnumSet would be to use GeneralInformation.values(), but that means you have to create a new array each time you call it, which feels wasteful to me. Admittedly calling EnumSet.allOf requires a new object each time too... if you're doing this a lot and are concerned about the performance, you could always cache it somewhere.
You can use GeneralInformation just like any other type when it comes to parameters:
public void doSomething(GeneralInformation info) {
// Whatever
}
Called with a value, e.g.
doSomething(GeneralInformation.PHONE);
Refactoring using a constructor parameter
public enum GeneralInformation {
NAME("Name"),
EDUCATION("Education"),
EMAIL("Email"),
PROFESSION("Profession"),
PHONE("Phone");
private final String textRepresentation;
private GeneralInformation(String textRepresentation) {
this.textRepresentation = textRepresentation;
}
#Override public String toString() {
return textRepresentation;
}
}
With your current values, you could actually just convert the name to title case automatically - but that wouldn't be very flexible for the long term, and I think this explicit version is simpler.
Since Java 8 I would suggest the following solution:
public static String printAll() {
return Stream.of(GeneralInformation.values()).
map(GeneralInformation::name).
collect(Collectors.joining(", "));
}
In applications, it's good practice to separate data from presentation. It allows the data to be used in different user interfaces, it makes the data objects more lightweight, and it allows for the future possibility of internationalization.
With that in mind, it's good to avoid strongly coupling the display name to the enum constant. Fortunately, there is a class which makes this easy: EnumMap.
public class ApplicationUI {
private final Map<GeneralInformation, String> names;
public ApplicationUI() {
names = new EnumMap<>(GeneralInformation.class);
names.put(GeneralInformation.NAME, "Name");
names.put(GeneralInformation.EDUCATION, "Education");
names.put(GeneralInformation.EMAIL, "Email");
names.put(GeneralInformation.PROFESSION, "Profession");
names.put(GeneralInformation.PHONE, "Phone");
assert names.keySet().containsAll(
EnumSet.allOf(GeneralInformation.class)) :
"Forgot to add one or more GeneralInformation names";
}
public String getNameFor(GeneralInformation info) {
return names.get(info);
}
}
If you are still on Java 1.7 this is what worked for me:
String genInfoValues = "";
boolean firstRun = true;
for (generalInformation info : generalInformation.values()){
if (firstRun) {
firstRun = false;
genInfoValues += info.name();
} else {
genInfoValues += ", " + info.name();
}
}
values() on the enum returns an array. So, it would be simple to do the following to:
System.out.println(Arrays.toString(generalInformation.values()));