【问题标题】:How do I subtract two Object Lists based on the attributes?如何根据属性减去两个对象列表?
【发布时间】:2022-08-06 13:18:26
【问题描述】:

我有两个 Java 对象列表

假设 dataToBeAdded、dataToBeSubtracted

对象具有相同的数据类型并具有多个属性

虚拟对象{ attr1, attr2, 属性 3, attr4 }

我想合并这些列表,但以有条件的方式 如果 attr1、attr2、attr3 在列表中匹配

减去 attr4 并使其成为列表的一部分。

如果属性不匹配

  • 元素来自 list1(dataToBeadded) 将其按原样添加到输出

  • 元素来自 list2(dataToBeSubtracted) 使 attr4 为负数(乘以 -1)

这有点像完全外连接类型的操作

我使用 Maps 和 Streams 做了一些事情

        Map<String, DummyObj> dataToBeAddedMap = dataToBeAdded.stream()
                .collect(Collectors.toMap(obj -> obj.attr1() + obj.attr2() + obj.attr3(), item -> item));

        Map<String, CumulativeSalesDataByHour> dataToBeSubtractedMap = dataToBeSubtracted.stream()
                .collect(Collectors.toMap( obj -> obj.attr1() + obj.attr2() + obj.attr3(), item ->
                        new DummyObject(item.attr1(), item.attr2(), item.attr3(), -1 * item.attr4())));

        Map<String, DummyObject> resultantData = Stream.of(dataToBeAddedMap, dataToBeSubtractedMap)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> new DummyObject(v1.attr1(),
                                v1.attr2(),
                                v1.attr3(),
                                v1.attr4() + v2.attr4())
                ));

        System.out.println(resultantData.values());

这给了我想要的结果,但有没有更有效的方法来实现这一目标?

编辑1:

添加输入和预期输出

        DummyObject a1 = new DummyObject(\"uuid1\", \"abcd\", \"mer1\", 20D);
        DummyObject a2 = new DummyObject(\"uuid1\", \"pqrs\", \"mer1\", 25D);
        DummyObject a3 = new DummyObject(\"uuid2\", \"xyz\", \"mer1\", 18D);

        List<DummyObject> dataToBeAdded = ImmutableList.of(a1,a2,a3);

        DummyObject d1 = new DummyObject(\"uuid1\", \"abcd\", \"mer1\", 5D);
        DummyObject d2 = new DummyObject(\"uuid1\", \"pqrs\", \"mer1\", 2D);
        DummyObject d3 = new DummyObject(\"uuid3\", \"xyz\", \"mer2\", 10D);

        List<DummyObject> dataToBeSubtracted = ImmutableList.of(d1,d2,d3);

        Desired Output
        [

            DummyObject(\"uuid1\", \"abcd\", \"mer1\", 15D); // 20-5
            DummyObject(\"uuid1\", \"pqrs\", \"mer1\", 23D); // 25-2
            DummyObject(\"uuid2\", \"xyz\", \"mer1\", 18D); 
            DummyObject(\"uuid3\", \"xyz\", \"mer1\", -10D); 

        ]    

    标签: java list dictionary object java-stream


    【解决方案1】:

    无需为要添加的元素和要减去的元素创建两个额外的 Map,您可以立即使用两个 List 创建一个链式流,并将减去 List 的每个元素映射到其 attr4 字段的元素已经被否定了。

    然后,您可以使用collect(Collectors.toMap()) 终端操作收集单个Map 中的所有对象。关键是前 3 个字段的串联,值是对象本身,而碰撞案例可以通过创建一个新的 DummyObj 来处理,该 DummyObj 与您分组的相同 3 个字段和由第一个和第二个碰撞值的attr4 的总和(类似于您在上一个流中所做的)。

    另一个小的改进可能是不使用 + 运算符链接字符串,这会为每个连接创建一个新的 String,而是 使用产生单个StringString.format() 方法对元素进行分组(减少开销)。

    Map<String, DummyObj> mapRes = Stream.concat(dataToBeAdded.stream(), dataToBeSubtracted.stream().map(obj -> {
                obj.setAttr4(-1 * obj.getAttr4());
                return obj;
            }))
            .collect(Collectors.toMap(obj -> String.format("%s%s%s", obj.getAttr1(), obj.getAttr2(), obj.getAttr3()),
                    Function.identity(),
                    (obj1, obj2) -> new DummyObj(obj1.getAttr1(), obj1.getAttr2(), obj1.getAttr3(), obj1.getAttr4() + obj2.getAttr4())
            ));
    

    这是测试上述代码的链接:

    https://ideone.com/K5qfsI

    【讨论】:

    • 感谢您的解决方案,在创建地图时只是一个快速的疑问,如果我不创建 String 可以吗?我直接附加属性,因为那里的数据类型仅为字符串 另外 Intellij 建议我不要使用 map 来使 list2 的 attr4 否定使用 peek 函数,它是否有任何我应该注意的缺点
    • 您的意思是是否可以像以前那样使用 + 运算符链接字符串?是的,没关系。与生成具有格式的单个字符串相比,它只会产生稍多的开销,但仅对于大型记录列表来说才值得注意。
    【解决方案2】:

    如果你真的想要性能,你需要重写管道——无论发生什么让你两个这个问题?这样做是为了让您拥有 2 个 Map,使用前 3 个属性作为键(也许创建一个新类来代表这 3 个)。

    但是,如果管道不能改变,最快的算法是:

    1. 编写一个对这些列表进行排序的比较器。
    2. 制作 2 个迭代器(每个迭代器一个)。
    3. 制作输出列表。
    4. 循环,为两个迭代器创建一个“当前项”指针。
    5. 如果 current-1 低于 current-2(或 current-2 完成),则复制 current-1 并推进 iterator-1。
    6. 如果 current-2 低于 current-1(或 current-1 完成),翻转 c-2 上的符号,添加它,然后推进 iterator-2。
    7. 如果 current-1 和 current-2 相同,则适当更新 attr4,然后将两者都推进。
    8. 如果两者都完成,则返回输出列表。

      这将是更多的代码,但它不会产生任何新的瞬态对象,除了 2 个迭代器和一个比较器。

    【讨论】:

      猜你喜欢
      • 2021-12-07
      • 2020-05-16
      • 1970-01-01
      • 1970-01-01
      • 2016-07-28
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      相关资源
      最近更新 更多