Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have two lists of Strings and two lists of objects. I need to copy the list of strings to the correspondence list of objects with specific values.

public List<Name> retrieveCitiesAndCountries(){
  List<String> list1 = new ArrayList<String>(); //list of cities
  List<String> list2 = new ArrayList<String>(); //list of countries
  list1 = retrieveListOfCities();
  list2 = retrieveListOfCountries();

  List<Name> namesList = new ArrayList<Names>();
  return namesList;
}

Name class

class Name{
  String name;
  String attrib;
  ..
}

I need to add both lists to namesList and add the respective attribs. For example, all cities should have City as their attribs and all countries should have Country as their attribs.

 for(int i=0;i<list1.size();i++){
     Name name = new Name();        
     name.setName(list1.get(i));
     name.setAttrib("City");
     namesList.add(name);
   }

  for(int i=0;i<list2.size();i++){
     Name name = new Name();        
     name.setName(list2.get(i));
     name.setAttrib("Country");
     namesList.add(name);
   }

I am wondering if there is any better way to do it.

I use namesList to return in JSON format.

@RequestMapping(value = "/names")
public @ResponseBody List<Name> retrieveCitiesAndCountries(){
    List<Name> namesList = retrievalServ.retrieveCitiesAndCountries();
    return namesList;
}
share|improve this question
    
Are you on Java 8? – h.j.k. Oct 9 '15 at 4:00
    
@h.j.k. no Java 7 – Jack Oct 9 '15 at 4:01
    
Can you also elaborate more on how your lists are instantiated (will list1 and list2 happen to be method arguments, for example), and how is namesList used finally? – h.j.k. Oct 9 '15 at 4:02
1  
Can you also show the complete implementation for Name, if feasible, and how it is used? Is it meant to be used generically (in layman terms) to represent either city or country names? How about landmarks? Other geographical features? – h.j.k. Oct 9 '15 at 4:06
1  
@h.j.k. question is updated thanks. – Jack Oct 9 '15 at 4:23

The Name() class has what I would call a half-assed constructor: it creates an object in a not-quite-initialized state, making it temporarily useless until the setters are called. If you had a Name(String name, String attrib) constructor, then the code would be a lot less cumbersome.

You can also avoid using indexed loops.

for (String city : list1) {
    namesList.add(new Name(city, "City"));
}
for (String country : list2) {
    namesList.add(new Name(country, "Country"));
}
share|improve this answer
    
thanks, would you elaborate how to pass lists to these loops? – Jack Oct 9 '15 at 4:25
    
I already gave you the entire code. What else do you need? – 200_success Oct 9 '15 at 4:26
    
I got you, you mean I should use contractor to make it easier. I was confused with the work half-assed – Jack Oct 9 '15 at 4:27
    
@200_success I am curious about metrics which can help us decide when and how much params to give to constructors and by setter methods. – CodeYogi Oct 9 '15 at 4:40
1  
@CodeYogi it is (or could be) a matter of immutability. – Spotted Oct 9 '15 at 6:05

You don't need the 2 intermediate lists. Plus you could set the name and attrib directly at the construction of a Name.

public final List<Name> retrieveCitiesAndCountries() {
    List<Name> namesList = new ArrayList<Names>();

    for(String city : retrieveListOfCities()) {
        namesList.add(new Name(city, "City"));
    }

    for(String country : retrieveListOfCountries()) {
        namesList.add(new Name(country, "Country"));
    }

    return namesList;
}

class final Name {
    final String name;
    final String attrib;

    public Name(String name, String attrib) {
        this.name = name;
        this.attrib = attrib;
    }

    ...
}

Again in your JSON service, you can directly return:

@RequestMapping(value = "/names")
public @ResponseBody List<Name> retrieveCitiesAndCountries() {
    return retrievalServ.retrieveCitiesAndCountries();
}
share|improve this answer
    
thanks but I do not get how your answer differs from 200_success' answer? – Jack Oct 13 '15 at 1:07
    
@Jack I explicitely declared Name immutable, this is a guarantee for someone who call retrieveCitiesAndCountries that the returned list of Name will contain only fully-initialized objects (and not objects in an inconsistent state). Plus I also removed the temporary lists to gain in readability. – Spotted Oct 13 '15 at 6:20
    
As h.j.k. said, you should also use enums instead of strings ("City", "Country") because it's less error-prone. – Spotted Oct 13 '15 at 6:22

Expressive instantiation

Adding to @200_success's answer, why not consider static methods too, that makes the instantiation a little more expressive?

public class Name {
    // ...

    public static Name ofCity(String cityName) {
        return new Name(cityName, "City");
    }

    public static Name ofCountry(String countryName) {
        return new Name(countryName, "Country");
    }
}

I know you are on Java 7, so you'll still have to rely on the explicit for-loop constructs, but here's a sneak peak at how this can be done much more conveniently in Java 8 (when you get the chance to upgrade):

public List<Name> retrieveCitiesAndCountries() {
    return Stream.concat(retrieveListOfCities().stream().map(Name::ofCity),
                            retrieveListOfCountries().stream().map(Name::ofCountry))
                    .collect(Collectors.toList());
}

Here, we use the method references Name::ofCity and Name::ofCountry to convert each String value we have to a Stream of the required type. Finally, we concat() both and collect() all the results using toList().

What's in a "City" or "Country"?

Is it meant to be used generically (in layman terms) to represent either city or country names? How about landmarks? Other geographical features? - myself

The reason why I made that comment is that usually, String values are a poor substitute for representing enumerated values:

  • Prone to human errors ("city" vs "City", and that's "goodbye sleep!").
  • Can't reliably rely on == for comparison (only if you take extreme care to intern() everywhere that's required).
  • Can't be used in a switch pre-Java 7.
  • No efficient Collection classes' implementations.
  • May not be easy to validate for correct values (depending on use cases).

This is where you may want to consider using an enum for such representations, as they are feasible solutions to the points listed above.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.