【问题标题】:Manipulating data from 2 maps to create a 3rd map处理来自 2 个地图的数据以创建第三个地图
【发布时间】:2016-06-24 19:46:35
【问题描述】:
Map<String, List<myStruct>> map1 = ...;  
Map<String, List<myStruct>> map2 = ...; 

myStruct 有 2 个字段:时间戳和数字。

我需要使用在 map1 和 map2 中找到的键创建一个新的 map3。对于每个键,每次 map1 和 map2 的时间戳匹配时,我都需要在 myStrcts 列表中添加一个新条目。该数字应等于 map1 的数字除以 map 2 的数字。

例如,如果他们每个人都有 1 个条目:

Map<String, List<myStruct>> map1 = < "Cat" , < <1pm, 10> <2pm, 6>, <3pm, 8>>;  
Map<String, List<myStruct>> map2 = < "Cat" , < <1pm, 5> <2pm, 2>, <3pm, 1>>; 

然后

Map<String, List<myStruct>> map3 = < "Cat" , < <1pm, 2> <2pm, 3>, <3pm, 8>>; 

因为下午 1 点:10/5 = 2,下午 2 点:6/2 = 3,下午 3 点:8/1 = 8。

我尝试过使用 lambda 函数,但我想不出任何好的和高效的方法。

我现在正在做的是遍历 map1 中的每个条目,然后遍历 myStructs 的每个时间戳 - 将其与 map2 中的时间戳进行比较,如果时间戳相等,则将结果添加到映射 3。我试图找出一个更有效的解决方案。

【问题讨论】:

  • 你能贴出你试过的代码吗?这将有助于消除重复工作。
  • 另外,结果数可以是浮点值吗?如果下午 1 点的条目是 8 和 5,结果是 8/5。你想要浮点值还是整数?
  • 顺便说一句,将Class/Object 称为‘struct’ 令人困惑。
  • @lucasvw 是的,它应该是一个浮点数。为了简单起见,我只是使用了这些示例。
  • @BasilBourque 对此感到抱歉!为未来着想。

标签: java dictionary hashmap java-8


【解决方案1】:

您可以遍历map1 的条目,然后通过映射值的流进行除法。

检查下面的代码

短代码

tmp 是结果List&lt;myStruct&gt;

 tmp = e.getValue()
        .stream()
        .map(struct -> new myStruct(struct.time, map2.get(e.getKey())
                                                           .stream()
                                                           .filter(x -> x.time.equals(struct.time))
                                                           .mapToInt(x -> struct.number / x.number)
                                                           .findFirst().getAsInt()))
        .collect(Collectors.toList());

长代码

public static void main(String[] args) {
    Map<String, List<myStruct>> map1 = new HashMap<>();
    Map<String, List<myStruct>> map2 = new HashMap<>();

    myStruct one = new myStruct("1pm", 10);
    myStruct two = new myStruct("1pm", 5);
    myStruct thr = new myStruct("2pm", 6);
    myStruct fou = new myStruct("2pm", 2);
    myStruct fiv = new myStruct("3pm", 8);
    myStruct six = new myStruct("3pm", 1);

    map1.put("Cat", Arrays.asList(one, thr, fiv));
    map2.put("Cat", Arrays.asList(two, fou, six));

    Map<String, List<myStruct>> map3 = new HashMap<>(map1);

    List<myStruct> tmp;

    for (Map.Entry<String, List<myStruct>> e : map1.entrySet()) {
         tmp = e.getValue()
                .stream()
                .map(struct -> new myStruct(struct.time, map2.get(e.getKey())
                                                                   .stream()
                                                                   .filter(x -> x.time.equals(struct.time))
                                                                   .mapToInt(x -> struct.number / x.number)
                                                                   .findFirst().getAsInt()))
                .collect(Collectors.toList());

         map3.put(e.getKey(), tmp);
    }

    System.out.println(map3); // {Cat=[<1pm, 2>, <2pm, 3>, <3pm, 8>]}
}

【讨论】:

  • 有趣!我会试试这个。
  • upvoted :) 但我想先尝试一下,看看能不能在接受之前更快地提出任何建议。
  • @user101 没问题随时告诉我是否有其他方法。我很好奇:D
【解决方案2】:

你可以这样做:

这是我的struct类,时间戳为String字段(不知道实际示例中使用的是哪种数据类型,仅供参考)

public class Mystruct 
{
    private String timeStamp;
    private int number;

    public Mystruct(String timeStamp,int number) 
    {
        this.timeStamp=timeStamp;
        this.number=number;
    }

    public Mystruct() {
        // TODO Auto-generated constructor stub
    }

    public String getTimeStamp() {
        return timeStamp;
    }
    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "Mystruct [timeStamp=" + timeStamp + ", number=" + number + "]";
    }

}

和测试类:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Test {
    public static void main(String[] args) {
        Map<String, List<Mystruct>> map1 = new HashMap<String, List<Mystruct>>();
        Map<String, List<Mystruct>> map2 = new HashMap<String, List<Mystruct>>();

        List<Mystruct> list1 = new ArrayList<Mystruct>();
        List<Mystruct> list2 = new ArrayList<Mystruct>();

        Mystruct struct1 = new Mystruct("1PM", 10);
        Mystruct struct2 = new Mystruct("2PM", 6);
        Mystruct struct3 = new Mystruct("3PM", 8);
        list1.add(struct1);
        list1.add(struct2);
        list1.add(struct3);

        Mystruct struct4 = new Mystruct("1PM", 5);
        Mystruct struct5 = new Mystruct("2PM", 2);
        Mystruct struct6 = new Mystruct("3PM", 1);
        list2.add(struct4);
        list2.add(struct5);
        list2.add(struct6);

        map1.put("Cat", list1);
        map2.put("Cat", list2);

        List<Mystruct> list3 = null;
        List<Mystruct> list4 = null;

        Map<String, List<Mystruct>> map3 = new HashMap<String, List<Mystruct>>();
        for (String key : map1.keySet()) 
        {
            if(map2.get(key)!=null)
            {
                list3=map1.get(key);
                list4=map2.get(key);

                List<Mystruct> structList = new ArrayList<Mystruct>();

                for(Mystruct outerStruct:list3)
                {
                    for(Mystruct innerStruct:list4)
                    {
                        if(outerStruct.getTimeStamp().equals(innerStruct.getTimeStamp()))
                        {
                            Mystruct structToStore=new Mystruct();
                            structToStore.setTimeStamp(outerStruct.getTimeStamp());
                            structToStore.setNumber(outerStruct.getNumber()/innerStruct.getNumber());
                            structList.add(structToStore);
                        }

                    }
                }
                map3.put(key, structList);
            }
        }

        System.out.println(map3.toString());

    }

}

输出:

{Cat=[Mystruct [timeStamp=1PM, number=2], Mystruct [timeStamp=2PM, number=3], Mystruct [timeStamp=3PM, number=8]]}

【讨论】:

  • 这是我目前的做法,但我需要更高效的方法,因为我有很多条目和地图要迭代。
【解决方案3】:

这里有两个嵌套操作,合并地图和合并包含的列表。首先,我认为试图在一个代码块中表达整个操作将变得非常难以理解。其次,这些List&lt;myStruct&gt;s 的合并非常昂贵,尤其是当它们往往很大时。因此,应首先将这些列表之一转换为Map&lt;String,Integer&gt;(假设String 为时间戳值的类型),以便进行有效查找。在该地图上定居,将具有此类地图的另一个列表合并到结果列表中可以表示为

static List<myStruct> combine(List<myStruct> list, Map<String,Integer> map) {
    return list.stream()
        .map(a -> {
            Integer b=map.get(a.getTimeStamp());
            return b==null? null: new myStruct(a.getTimeStamp(), a.getNumber()/b);
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
}

此实现关注其他地图中不存在的时间戳。如果总有对应的入口,方法可以简化为

static List<myStruct> combineSimplified(List<myStruct> list, Map<String,Integer> map) {
    return list.stream()
        .map(a -> new myStruct(a.getTimeStamp(), map.get(a.getTimeStamp())/a.getNumber()))
        .collect(Collectors.toList());
}

使用列表组合方式,地图处理变得如此简单

// preparation step
Map<String, Map<String,Integer>> prep=map1.entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
        .collect(Collectors.toMap(myStruct::getTimeStamp, myStruct::getNumber))));

// final processing
Map<String, List<myStruct>> result = map2.entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey,
        e -> combine(e.getValue(), prep.get(e.getKey()))));

当然,您可以将列表组合方法内联到 lambda 表达式中,但我不建议这样做。可读性会受到很大影响。

【讨论】:

    猜你喜欢
    • 2019-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 2016-07-26
    • 1970-01-01
    相关资源
    最近更新 更多