Java 8流:如何在两个字符串ArrayList之间匹配值,并创建另一个对象的列表

huangapple go评论68阅读模式
英文:

Java 8 Streams: How to match values between two ArrayList of Strings and create a list of another object

问题

import java.util.List;
import java.util.ArrayList;

class Vehicle {
    private String model;
    private String submodel;
    private String type;
    private String subtype;
    // setters getters
}

public class Main {
    public static void main(String[] args) {
        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");

        List<Vehicle> vehicles = buildVehicle(list1, list2);
        vehicles.forEach(System.out::println);
    }

    private static List<Vehicle> buildVehicle(List<String> list1, List<String> list2) {
        List<Vehicle> vehicles = new ArrayList<>();
        if (!list2.isEmpty()) {
            vehicles = list2.stream()
                    .map(v -> {
                        Vehicle vehicle = new Vehicle();
                        if (v.contains(",")) {
                            String[] val = v.split(",");
                            vehicle.setSubtype(val[0]);
                            vehicle.setSubmodel(val[1]);
                        }
                        list1.stream()
                            .filter(c -> c.startsWith(vehicle.getSubtype().substring(0, 1)) && c.contains(","))
                            .findFirst()
                            .ifPresent(c -> {
                                String[] val = c.split(",");
                                vehicle.setType(val[0]);
                                vehicle.setModel(val[1]);
                            });
                        return vehicle;
                    })
                    .toList();
        }
        return vehicles;
    }
}
英文:

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&lt;String&gt; list1 = new ArrayList();
list1.add(&quot;A,Airplane&quot;);
list1.add(&quot;B,Boat&quot;);
List&lt;String&gt; list2 = new ArrayList();
list2.add(&quot;A90, Boing Airplane&quot;);
list2.add(&quot;A70, Boing777&quot;);
list2.add(&quot;B80, Boing Boat&quot;);

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&lt;Vehicle&gt; buildVehicle(List&lt;String&gt; list1, List&lt;String&gt; list2) {
List&lt;Vehicle&gt; vehicles = new ArrayList&lt;&gt;();
if (ObjectUtils.isNotEmpty(list2)) {
list2.forEach(v -&gt; {
Vehicle vehicle = new Vehicle();
if (v.contains(&quot;,&quot;)) {
String[] val = StringUtils.split(v,&quot;,&quot;);
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))
&amp;&amp; c.contains(&quot;,&quot;)) {
String[] val = StringUtils.split(c, &quot;,&quot;);
vehicle.setType(val[0]);
vehicle.setModel(val[1]);
}
break;
}
}
vehicles.add(vehicle);
});
}
return vehicles;
}

Can the nested loop be avoided by using streams?

答案1

得分: 4

我会按照以下方式进行操作:

List<String> list1 = new ArrayList<>();
list1.add("A,飞机");
list1.add("B,船");

List<String> list2 = new ArrayList<>();
list2.add("A90, 波音飞机");
list2.add("A70, 波音777");
list2.add("B80, 波音船");

Pattern commaPattern = Pattern
    .compile("\\s*,\\s*"); // 一个用于按逗号及其周围空格进行分割的正则表达式模式

Map<String, String> modelToType = list1.stream().map(commaPattern::split)
    .collect(Collectors
        .toMap(modelAndType -> modelAndType[0],
            modelAndType -> modelAndType[1])); // 将模型映射到类型,以便进行 o(1) 查找

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());
英文:

I'd follow an approach like the following:

List&lt;String&gt; list1 = new ArrayList&lt;&gt;();
list1.add(&quot;A,Airplane&quot;);
list1.add(&quot;B,Boat&quot;);

List&lt;String&gt; list2 = new ArrayList&lt;&gt;();
list2.add(&quot;A90, Boing Airplane&quot;);
list2.add(&quot;A70, Boing777&quot;);
list2.add(&quot;B80, Boing Boat&quot;);

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

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

List&lt;Vehicle&gt; vehicles = list2.stream().map(commaPattern::split)
    .map(subModelAndSubType -&gt; {
      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

得分: 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");

无论如何获取模型类型信息,如果使用Map来保存信息,可能会更有效,并且肯定会更容易。如果信息是从文件中读取的,最好在读取它们并将其放入映射之前预处理它们(如果需要拆分)。如果是手动输入的,那么将信息放入映射中将消除任何预处理的需求。这里是一个简单的示例。

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

但是,根据此处提供的信息,这是我所进行的步骤。

首先,我创建了一个Lambda来辅助进行列表转换。

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]));
// 创建 TypeModel 映射
Map<String, String> mapTM = makeMap.apply(list1);
// 创建 subTypeSubModel 映射
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]

以下是去除了设置器和获取器的类。

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

The data as provided.

List&lt;String&gt; list1 = new ArrayList&lt;&gt;();
list1.add(&quot;A,Airplane&quot;);
list1.add(&quot;B,Boat&quot;);
List&lt;String&gt; list2 = new ArrayList&lt;&gt;();
list2.add(&quot;A90, Boeing Airplane&quot;);
list2.add(&quot;A70, Boeing777&quot;);
list2.add(&quot;B80, Boeing Boat&quot;);

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&lt;String,String&gt; map1 = new HashMap&lt;&gt;();
map1.put(&quot;A&quot;,&quot;Airplane&quot;);
map1.put(&quot;B&quot;,&quot;Boat&quot;);

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

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

Function&lt;List&lt;String&gt;, Map&lt;String, String&gt;&gt; makeMap =
lst -&gt; lst.stream().map(st -&gt; st.split(&quot;\\s*,\\s*&quot;)).collect(
Collectors.toMap(a -&gt; a[0], a -&gt; a[1]));
// create the TypeModel map
Map&lt;String, String&gt; mapTM = makeMap.apply(list1);
// create the subTypeSubModel Map;
Map&lt;String, String&gt; 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&lt;Vehicle&gt; vehicles = mapTM.keySet().stream()
.flatMap(type -&gt; mapSTSM.keySet().stream()
.filter(subType -&gt; subType.startsWith(type))
.map(sbType -&gt; 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 &quot;[&quot; + String.join(&quot;, &quot;, model, submodel, type, subtype)
+ &quot;]&quot;;
}
}
</details>

huangapple
  • 本文由 发表于 2020年8月20日 07:46:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/63496430.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定