【问题标题】:filter and set() in one stream在一个流中过滤和设置()
【发布时间】:2019-02-06 13:45:28
【问题描述】:

所以我有一些我正在使用的示例代码来尝试找出下面的逻辑。它只是一个大的 main 方法和两个 POJO,但它会运行。我正在调试以获取终止点的值。

public class Main {

    public static void main(String[] args) {

        obj1 obj1 = new obj1();
        obj1 obj12 = new obj1();

        obj2 obj2 = new obj2();
        obj2 obj22 = new obj2();

        obj1.id = 123L;
        obj1.carId = 1234L;
        obj12.id = 123L;
        obj12.carId = 1234L;

        obj2.carId = 1234L;
        obj22.carId = 12345L;

        ArrayList<obj1> obj1Arr = new ArrayList<>();
        ArrayList<obj2> obj2Arr = new ArrayList<>();

        obj1Arr.add(obj1);
        obj1Arr.add(obj12);

        obj2Arr.add(obj2);
        obj2Arr.add(obj22);

        List<obj2> newCarList= obj2Arr.stream()
                .filter(anObjOf2 -> obj1Arr
                        .stream()
                        .anyMatch(carReg -> carReg.getCarId().equals(anObjOf2.getCarId())))
                .collect(Collectors.toList());
    }
}

POJO1:

public class obj1 {

    Long id = null;
    Long carId = null;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getCarId() {
        return carId;
    }

    public void setCarId(Long carId) {
        this.carId = carId;
    }
}

POJO2:

public class obj2 {

    Long id = null;
    Long carId = null;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getCarId() {
        return carId;
    }

    public void setCarId(Long carId) {
        this.carId = carId;
    }
}

如您所见,我正在流式传输并过滤掉与carId 不匹配的任何objs。我的下一个目标是在匹配的对象上使用 set id(不是 carId)。

基本上,如果obj1.carId == obj2.carId THEN set obj1.id = obj2.id

我的问题是我不知道如何在同一个流中执行此操作。是否可以?我不禁认为此时需要一个迭代器?

【问题讨论】:

  • 请详细说明这部分问题... 在obj2Arr中的objs上设置对应的id。
  • 这对我来说不是很清楚My next goal is to then set the corresponding id on the objs in obj2Arr. 并且在流中您正在过滤在obj1Arr 中具有相同carid 的对象
  • 我已经更新了它..希望它很清楚。基本上,if obj1.carId == obj2.carId 然后set obj1.id = obj2.id。这有意义吗?
  • @notAChance 不,因为它已经设置好了......这就是你能够比较它的方式......不是吗?
  • 我认为混乱来自过度使用idcarIdid 是不同的。我正在比较carId,匹配时,我想要obj2.setId(obj1.getId());

标签: java collections java-8 java-stream


【解决方案1】:

不要在一个流中执行这两个操作。这是一种非常糟糕的风格。

首先收集数据。然后变异元素。比如:

Map<obj2, Optional<obj1>> map = 
    obj2Arr.stream()
           .collect(toMap(identity(), o2 -> obj1Arr.stream()
                                                  .filter(o1 -> o1.getCarId().equals(o2.getCarId()))
                                                  .findAny()));

map.forEach((o1, optO2) -> optO2.ifPresent(o2 -> o1.setId(o2.id)));

否则,您可以只使用嵌套的 for-each 循环。

【讨论】:

  • 是的,似乎id 被任意设置为下一个迭代项。
  • @notAChance 好的,我的立场是正确的。检查更新的答案。
  • .filter(a -&gt; a.getCarId().equals(o.getCarId())) 将是 filter 的正确实现
  • 是的 ::equals 不起作用。感谢@ETO 的更新
【解决方案2】:

我建议首先为第一个列表中的每个元素创建一个 Map 映射 carIdid(我已经修改了你的类以使其更具可读性并删除重复的 ID,我假设是一个错误):

@Data @AllArgsConstructor
class Car {
    Long id = null;
    Long carId = null;
}
List<Car> obj1Arr = Arrays.asList(new Car(1L, 123L), new Car(2L, 1234L));
List<Car> obj2Arr = Arrays.asList(new Car(0L, 1234L), new Car(0L, 12345L));

Map<Long, Long> carIdToId = obj1Arr.stream()
        .collect(Collectors.toMap(Car::getCarId, Car::getId));
// [Car(id=2, carId=1234), Car(id=0, carId=12345)]

然后您可以使用此Map 过滤具有相同carID 的汽车,而无需与其他汽车进行比较。然后,使用forEach只需在原始列表中设置Id即可。

obj2Arr.stream()
        .filter(anObjOf2 -> carIdToId.containsKey(anObjOf2.getCarId()))
        .forEach(anObjOf2 -> anObjOf2.setId(carIdToId.get(anObjOf2.getCarId())));
System.out.println(obj2Arr);
// [Car(id=2, carId=1234), Car(id=0, carId=12345)]

如果您想要carId 相等的项目,您可以在peek 中设置ID,然后在collect 中设置ID。不过,这仍会修改原始实例:

obj2Arr = obj2Arr.stream()
        .filter(anObjOf2 -> carIdToId.containsKey(anObjOf2.getCarId()))
        .peek(anObjOf2 -> anObjOf2.setId(carIdToId.get(anObjOf2.getCarId())))
        .collect(Collectors.toList());
System.out.println(obj2Arr);
// [Car(id=2, carId=1234)]

或者创建一个全新实例的列表:

obj2Arr = obj2Arr.stream()
        .map(Car::getCarId)
        .filter(carIdToId::containsKey)
        .map(carId -> new Car(carIdToId.get(carId), carId))
        .collect(Collectors.toList());
System.out.println(obj2Arr);
// [Car(id=2, carId=1234)]

【讨论】:

  • 谢谢,这完全符合要求。
【解决方案3】:

如果 O(m*n) 解决方案适合您,那么更简单的实现是使用 for 循环:

// classes renamed for naming convention and relatiing to question at the same time 
List<ObjectTwo> newCarList = new ArrayList<>();
for(ObjectTwo objectTwo: objectTwoArr) {
    for (ObjectOne objectOne : objectOneArr) {
        if (objectTwo.getCarId().equals(objectOne.getCarId())) {
            objectOne.setId(objectTwo.getId());
            newCarList.add(objectTwo);
        }
    }
}

mn 分别是列表的大小。

【讨论】:

  • 这与我当前的解决方案逐个字母相同。不幸的是,我的拉取请求审阅者是 Java 8 迷恋者。
  • @notAChance 我在尝试的时候有一个疑问。我能感觉到一些不正确的(不确定的)。 场景:如果有 2 个obj1 和 2 个obj2 也有相同的cardId。现在,您最终将始终只更新前一个列表中的 1 个obj1,用于后者中的两个元素。
  • 这是真的,但在我的真实世界代码中,可以安全地假设不会有重复的 id。每个对象的所有 id 将始终是唯一的。两个列表都是从同一个实体填充的,一个是持久化的并给出id,另一个不是。然后转换其他列表,在此期间可能会删除一些对象。这导致需要匹配,因为我们需要在其他对象上持久化id。不过想法不错。
  • 某些对象可能会被丢弃。 理想情况下,此处存在的所有项目仍将出现在另一个列表中,只是需要在那里填写的映射添加了 @987654332 @.
【解决方案4】:

你可以在谓词anyMatch中做到这一点

List<obj2> newCarList= obj2Arr.stream()
            .filter(anObjOf2 -> obj1Arr
                    .stream()
                    .anyMatch(carReg ->  {
            if(carReg.getCarId().equals(anObjOf2.getCarId()))) {
                 carReg.setId(anObjOf2.getId());
                  return true;
               }
           return false;
     }).collect(Collectors.toList());

第二种方法

先过滤

List<obj2> newCarList= obj2Arr.stream()
            .filter(anObjOf2 -> obj1Arr
                    .stream()
                    .anyMatch(carReg -> carReg.getCarId().equals(anObjOf2.getCarId())))
            .collect(Collectors.toList());

现在设置id

newCarList.forEach(obj->{
           obj1Arr.forEach(o-> {
            if(o.getCarId().equals(obj.getCarId()) {
                  o.setId(obj.getId());
                }
           };
    });

【讨论】:

  • 我宁愿建议在另一个迭代中这样做,而不是在终端操作中改变对象。
  • 在一个期望纯谓词的方法中改变数据是一种糟糕的风格,只会在未来引发问题
  • @nullpointer 这就是我刚刚想到的。所以基本上答案是:“是的,它可以完成,但应该使用第二个迭代器完成”?
  • 我同意,但这似乎是双重操作,而且操作不在流对象@nullpointer
  • @Deadpool 您确实已经回答了我的问题,该问题专门要求“在一个流中”提供解决方案。谢谢:)
猜你喜欢
  • 2016-08-22
  • 1970-01-01
  • 2020-04-08
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
相关资源
最近更新 更多