【问题标题】:Java 8 Streams: How to match values between two ArrayList of Strings and create a list of another objectJava 8 Streams:如何匹配两个字符串数组列表之间的值并创建另一个对象的列表
【发布时间】:2020-08-19 23:46:46
【问题描述】:

我有两个字符串列表,想要比较列表中的值并构造另一个对象的新列表。我可以使用嵌套循环来做到这一点,但正在寻找更高效、更整洁的解决方案。

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");

有一个 Vehicle 对象。

class Vehicle {

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

现在,我需要通过将模型(list1 中的第一个字符)与 list2 中的第一个字符匹配来构建车辆对象,并构建类似这样的东西

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;
    }

使用流可以避免嵌套循环吗?

【问题讨论】:

  • 请注意,您可能可以获得更紧凑、更易读和更整洁的解决方案,但很可能不是您提到的更“高性能”的解决方案。性能主要取决于您使用的数据结构以及解决方案的时间/空间复杂性。最重要的是,流 api 没有优化,因此比旧的 for/while 循环稍慢。
  • 你能解释一下紧凑、更整洁的解决方案吗?
  • ObjectUtilsStringUtils 不是 JDK 的一部分。

标签: java java-stream


【解决方案1】:

我会采用如下方法:

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());

【讨论】:

  • 非常感谢。这正是我想要的。
【解决方案2】:

提供的数据。

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");

不管模型类型信息是如何获得的,如果不是使用Lists,而是使用Maps 来保存信息,这种转换可能会更有效,当然也更容易。如果信息是从文件中读入的,最好在读入时对其进行预处理(如果需要,进行拆分)并将它们放入地图中。如果它是手动输入的,那么将信息放入地图将完全不需要任何预处理。这是一个简单的例子。

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

但是,我使用此处提供的信息是如何进行的。

首先,我创建了一个 Lambda 来辅助 List 转换。

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);

现在只需使用每个键集将其拼凑起来。 我在我的示例中为Vehicle 创建了一个构造函数,恕我直言 更干净的对象创建。

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);

基于 toString 打印(可以更改)。

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

这里是没有 setter 和 getter 的类。

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)
                + "]";
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-01
    • 1970-01-01
    • 2019-03-18
    • 1970-01-01
    • 2019-12-06
    • 2011-06-28
    • 1970-01-01
    相关资源
    最近更新 更多