0

I am having two lists of Strings and want to compare the values in the lists and construct a new list of another object. I am able to do this with nested loops but looking for a more performant and neater solution.

List<String> list1 = new ArrayList();
list1.add("A,Airplane");
list1.add("B,Boat");

List<String> list2 = new ArrayList();
list2.add("A90, Boing Airplane");
list2.add("A70, Boing777");
list2.add("B80, Boing Boat");

There is a Vehicle object.

class Vehicle {

private String model;
private String submodel;
private String type;
private String subtype;
// setters getters
}

Now, I need to build the vehicle object using by matching the model (the first character from list1) with the first character in list2 and build something like this

private List<Vehicle> buildVehicle(List<String> list1, List<String> list2) {
        List<Vehicle> vehicles = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(list2)) {
            list2.forEach(v -> {
                Vehicle vehicle = new Vehicle();
                if (v.contains(",")) {
                    String[] val = StringUtils.split(v,",");
                    vehicle.setSubtype(val[0]);
                    vehicle.setSubmodel(val[1]);
                }
                for (String c : list1) {
                    //Matching the first character from element in list1 
                    if (c.substring(0,1).equals(vehicle.getSubtype().substring(0,1))
                            && c.contains(",")) {
                        String[] val = StringUtils.split(c, ",");
                        vehicle.setType(val[0]);
                        vehicle.setModel(val[1]);
                        }
                        break;
                    }
                }
                vehicles.add(vehicle);
            });
        }
        return vehicles;
    }

Can the nested loop be avoided by using streams?

3
  • 1
    Note that you might be able to get more compact, readable and a neater solution, but most possibly not a more "performant" one as you mentioned. Performance is mostly determined by the data structures you make use of, and the time/space complexities of your solution. On top of that, stream api is not as optimized and therefore slightly slower than a good old for/while loops. Commented Aug 19, 2020 at 23:57
  • Can you please explain on the compact, and neater solution? Commented Aug 20, 2020 at 0:00
  • ObjectUtils and StringUtils are not part of the JDK. Commented Aug 20, 2020 at 0:12

2 Answers 2

4

I'd follow an approach like the following:

List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");

List<String> list2 = new ArrayList<>();
list2.add("A90, Boing Airplane");
list2.add("A70, Boing777");
list2.add("B80, Boing Boat");

Pattern commaPattern = Pattern
    .compile("\\s*,\\s*"); // a regex pattern to split by comma and the whitespace around it

Map<String, String> modelToType = list1.stream().map(commaPattern::split)
    .collect(Collectors
        .toMap(modelAndType -> modelAndType[0],
            modelAndType -> modelAndType[1])); // mapping models to types for o(1) lookups

List<Vehicle> vehicles = list2.stream().map(commaPattern::split)
    .map(subModelAndSubType -> {
      Vehicle vehicle = new Vehicle();
      vehicle.submodel = subModelAndSubType[0];
      vehicle.subtype = subModelAndSubType[1];
      vehicle.model = vehicle.submodel.substring(0, 1);
      vehicle.type = modelToType.get(vehicle.model);
      return vehicle;
    }).collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks so much. This is exactly what I was looking for.
2

The data as provided.

List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");

List<String> list2 = new ArrayList<>();
list2.add("A90, Boeing Airplane");
list2.add("A70, Boeing777");
list2.add("B80, Boeing Boat");

Regardless of how the model type information is obtained, this conversion might be more efficient and certainly easier if instead of using Lists you used Maps to hold the information. If the information was read in from a file it would be best to pre-process them (split if need be) as they are read in and put them in a map. If it was entered by hand then putting the information in a map would negate the need for any pre-processing at all. Here is a simple example.

Map<String,String> map1 = new HashMap<>();
map1.put("A","Airplane");
map1.put("B","Boat");

However, using the information as provided here is how I proceeded.

First, I created a Lambda to assist in the List conversion.

Function<List<String>, Map<String, String>> makeMap =
        lst -> lst.stream().map(st -> st.split("\\s*,\\s*")).collect(
                Collectors.toMap(a -> a[0], a -> a[1]));

// create the TypeModel map
Map<String, String> mapTM = makeMap.apply(list1);
// create the subTypeSubModel Map;
Map<String, String> mapSTSM = makeMap.apply(list2);

Now just use the keysets of each to go thru and piece it together. I created a constructor in my example for Vehicle which imho makes a cleaner object creation.

List<Vehicle> vehicles = mapTM.keySet().stream()
        .flatMap(type -> mapSTSM.keySet().stream()
                .filter(subType -> subType.startsWith(type))
                .map(sbType -> new Vehicle(mapTM.get(type),
                        mapSTSM.get(sbType), type, sbType)))
        .collect(Collectors.toList());

vehicles.forEach(System.out::println);

Prints based on toString (which can be changed).

[Airplane, Boeing Airplane,A,A90]
[Airplane, Boeing777,A,A70]
[Boat, Boeing Boat,B,B80]

Here is the class sans setters and getters.

class Vehicle {
    
    private String model;
    private String submodel;
    private String type;
    private String subtype;
    
    public Vehicle() {
    }
    
    public Vehicle(String model, String submodel, String type,
            String subtype) {
        this.model = model;
        this.submodel = submodel;
        this.type = type;
        this.subtype = subtype;
    }
    
    public String toString() {
       return "[" + String.join(", ", model, submodel, type, subtype)
                + "]";
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.