简体   繁体   English

我如何从嵌套在 hashmap 和 Java 中的列表中访问和添加值

[英]How I can access and add value from the list which is nested in the hashmap and list in Java

I am trying to add value for the List which is stored in HashMap and that has one parent List.我正在尝试为存储在 HashMap 中且具有一个父列表的列表添加值。

When I try to do so I get "The method get in type is not compatible with the List"当我尝试这样做时,我得到“获取类型的方法与列表不兼容”

I am trying the following code, logic is:我正在尝试以下代码,逻辑是:

  • If I get the matching value of tID in the txnValue List I am just adding the " Values " List otherwise I am creating the new HashMap.如果我在txnValue列表中获得tID的匹配值,我只是添加“”列表,否则我将创建新的 HashMap。

 List < HashMap > txnvalues = new ArrayList < HashMap > (); for (LinkedHashMap < String, Object > linkedHashMap: resultset) { HashMap data = new HashMap < > (); HashMap attrData = new HashMap < > (); List values = new ArrayList < > (); data.put("values", new ArrayList < > ()); attrData.put("attrID", linkedHashMap.get("ID")); attrData.put("attrVal", linkedHashMap.get("VAL")); String txnID = linkedHashMap.get("T_ID").toString(); if (.txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) { data,put("tID". linkedHashMap;get("T_ID")). values;add(attrData). data,put("Values"; values). txnvalues;add(data). } else { txnvalues.get("Values");add(attrData); // this Line throws error } }

Example:例子:

 [{ "tID":123, "Values":[{attrID:1,attrVal:123}] }] //Here If linkedHashmap.get("T_ID") = 123 which matches with tID then I want to add data in the Values [{ "tID":123, "Values":[{attrID:1,attrVal:123},{attrID:11,attrVal:467}] }] //If it doesn't match then I want to create new Hashmap and update txnValues Like this [{ "tID":123, "Values":[{attrID:1,attrVal:123},{attrID:2,attrVal:3435}] }, { "tID":456, "Values":[{attrID:2,attrVal:233}] } ]

I decided to parameterize all of your various iterables.我决定参数化所有各种可迭代对象。 Below is the parameterized code.下面是参数化代码。

List<HashMap<String, List<HashMap<String, Object>>>> txnvalues = new ArrayList<HashMap<String, List<HashMap<String, Object>>>>();
for (LinkedHashMap<String, Object> linkedHashMap : resultset) {//Error here

    HashMap<String, List<HashMap<String, Object>>> data = new HashMap<String, List<HashMap<String, Object>>>();
    HashMap<String, Object> attrData = new HashMap<String, Object>();
    List<HashMap<String, Object>> values = new ArrayList<HashMap<String, Object>>();
    data.put("values", new ArrayList<>());
    attrData.put("attrID", linkedHashMap.get("ID"));
    attrData.put("attrVal", linkedHashMap.get("VAL"));
    String txnID = linkedHashMap.get("T_ID").toString();
    if (!txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) {
        data.put("tID", linkedHashMap.get("T_ID")); //Error here
        values.add(attrData);
        data.put("Values", values);
        txnvalues.add(data);
    } else {
        txnvalues.get("Values").add(attrData); //Error here
    }
}

First, you have multiple errors in your code such as trying to put a String key and Object value into data , which is a HashMap that only takes a String key and a List(of HashMaps of Strings and Objects) value.首先,您的代码中有多个错误,例如试图将 String 键和 Object 值放入data中,这是一个 HashMap ,它只需要一个 String 键和一个 List(字符串和对象的 HashMap)值。 Another such is trying to get an item from txnvalues by a String, when txnvalues is a List and therefore requires an integer index parameter.另一个例子是尝试通过字符串从txnvalues中获取一个项目,此时txnvalues是一个列表,因此需要一个 integer 索引参数。

Second, you have a variable here which is never defined: resultset .其次,这里有一个从未定义的变量: resultset We don't know what it is or how it is used, since it's never referenced elsewhere.我们不知道它是什么或如何使用,因为它从未在其他地方被引用过。

Third, there are many many ways to handle nested sets.第三,有很多方法可以处理嵌套集。 This >-> List<HashMap<String, List<HashMap<String, Object>>>> is simply horrible.这个 >-> List<HashMap<String, List<HashMap<String, Object>>>>简直太可怕了。

Please re-write your code in a way that is readable, parameterized, and can properly compile without errors.请以可读、参数化并且可以正确编译而不会出错的方式重写您的代码。 Just parameterizing will help you keep track of which iterables take which parameters and will help prevent the problem you had when you came here for help.仅参数化将帮助您跟踪哪些迭代器采用哪些参数,并有助于防止您来这里寻求帮助时遇到的问题。

I'm probably late with this answer.我可能迟到了这个答案。 Nevertheless, I'll introduce a possible remedy accompanied by a detailed explanation.不过,我将介绍一种可能的补救措施并附有详细说明。

At the first glance, such a deeply nested collection seems contrived and incomprehensible.乍一看,如此深度嵌套的集合似乎做作且难以理解。 But problems that you can see in this code aren't something unusual, they could be observed in many questions on StackOverflow, and in many repositories.但是您在这段代码中看到的问题并不罕见,它们可以在 StackOverflow 和许多存储库中的许多问题中观察到。 The only difference is in concentration.唯一的区别是浓度。

在此处输入图像描述

Let's try to examine it closely.让我们试着仔细检查一下。 A map is a data structure that is commonly misused by beginners because it allows to combine objects of different nature. map 是一种常被初学者误用的数据结构,因为它允许组合不同性质的对象。 I am pretty sure that provided code models something more or less tangible.我很确定提供的代码模型或多或少是有形的。 Did you notice that PO tries to access an entry that has a string key called "id" ?您是否注意到 PO 试图访问具有名为“id”的字符串键的条目? That's a clear indicator that collections here are used in place of objects.这清楚地表明这里的 collections 被用来代替对象。

If I say object graph can be far more complex, it probably wouldn't be something new.如果我说 object 图可以复杂得多,它可能就不是什么新东西了。 But how to reason about the code that is written in such a way?但是这样写的代码怎么推理呢?

Let's step aside for a moment and consider the following task :让我们暂时搁置一边,考虑以下任务

  • there are a number of sailboats, you need to determine which of them will win the race and return its name as a result;有许多帆船,您需要确定其中哪艘将赢得比赛并返回其名称;
  • input provided as a plain text and consists of the following parameters: unique name, displacement, and weight (only these three for simplicity);输入以纯文本形式提供,包含以下参数:唯一名称、位移和权重(为简单起见,仅这三个);
  • the speed of the vessel depends on its displacement and weight (ie formula is provided, we need only parse the values);船只的速度取决于它的排水量和重量(即提供了公式,我们只需要解析值);

It is very likely that somebody can come up with such a solution:很可能有人可以想出这样的解决方案:

  • create a Map<String, List<Double>> , where the key is a sailboat's name and the value is a list that contains displacement and weight;创建一个Map<String, List<Double>> ,其中键是帆船的名称,值是包含位移和重量的列表;
  • then just iterate over the entry set, apply the formula and so find the fastest vessel.然后只需遍历条目集,应用公式,然后找到最快的船只。

Only a couple of methods, and it seems that a separate class for a sailboat will allegedly increase the overall complexity and amount of code.只有几种方法,而且据称帆船的单独 class似乎会增加整体复杂性和代码量。 That's a common delusion for many students.这是很多学生的共同错觉 The creation of a separate class will provide a logical structure to the code and will pay off if you would wish to extend or reuse it.创建一个单独的 class 将为代码提供一个逻辑结构,如果您希望扩展或重用它,将会得到回报。 Note that not only attributes of the sailboat must belong to this class but also the methods that allow to compute sailboat's speed and compare sailboats based on it.请注意,不仅帆船的属性必须属于这个 class,而且允许计算帆船速度并基于它比较帆船的方法也必须属于。

Decomposition is a skill and it has to be exercised.分解是一种技能,必须加以锻炼。 And for those of you who didn't realize from the beginning that a sailboat in the previous example has to be represented by an object, I advise to try the next exercise : describe a university, a candy shop, a grocery store, a cat, anything you like but without using objects .对于那些从一开始就没有意识到前面示例中的帆船必须由 object 表示的人,我建议尝试下一个练习:描述一所大学、一家糖果店、一家杂货店、一只猫,任何你喜欢但不使用对象的东西。 First, think about a couple of use-cases that entail accessing some properties of the elements of the system that you're trying to model. Then draw diagrams and write the code using warriors collections and arrays, pay attention that the more complex your system becomes, the more cumbersome become all nested maps and lists, which make you write your code like this:首先,考虑一些需要访问系统元素的某些属性的用例 model。然后使用 warriors collections 和 arrays 绘制图表并编写代码,注意您的系统越复杂变得越来越麻烦,所有嵌套的映射和列表变得越来越麻烦,这使您可以像这样编写代码:

    map.get(something).get(something).add(somethingElse);

And then, when you see the problems, you are ready to implement the classes that make sense in your domain model and compare the two approaches.然后,当您发现问题时,您准备好实施在您的域中有意义的类 model 并比较这两种方法。

Disclaimer: understanding decomposition is a crucial thing but class design is a very broad topic , there are lots of things to study in this area like classic principles and design patterns.免责声明:理解分解是至关重要的事情,但 class 设计是一个非常广泛的话题,在这个领域有很多东西需要研究,比如经典原则和设计模式。 But before diving into these topics, you have to have a firm understanding of decomposition and OOP .但在深入探讨这些主题之前,您必须对分解OOP有深入的了解。 Without this knowledge even with an object-oriented approach, your solution could become convoluted and difficult to manage.如果没有这些知识,即使使用面向对象的方法,您的解决方案也可能变得复杂且难以管理。 But this is a step in the right direction.但这是朝着正确方向迈出的一步。 The fact alone that you are using an object-oriented language doesn't automatically make your solution object-oriented.仅凭您使用面向对象语言这一事实并不会自动使您的解决方案成为面向对象的。 It's a skill, and it has to be exercised.这是一种技能,必须加以锻炼。

It was a very long digression, now let's get to the point.说了很长的题外话,现在进入正题。

As I already said, I'm convinced that the post author had in mind some kind of natural use case.正如我已经说过的,我相信博文作者考虑到了某种自然用例。 Instead of names that describe the system in this maze of data structures we can see only dump get() and put() .我们只能看到转储get()put() ,而不是描述这个数据结构迷宫中系统的名称。 But there's a clue in the usage of map. An id as a key is a clear indicator that it has to be an object which is substituted by a map.但是在 map 的用法中有一个线索。一个id作为是一个明确的指示,它必须是一个object ,它被一个 map 代替。

在此处输入图像描述

That is a start of a journey, I'll try to provide a scenario that makes sense (at least a bit) and pieces of a system that fits into a structure depicted in the scheme provided at the start of this post.这是旅程的开始,我将尝试提供一个有意义(至少有一点)的场景和适合本文开头提供的方案中描述的结构的系统部分。

Let's consider an organization that sells something (I'm not trying to guess what was the author's intention, but providing a use case that will allow to reason about the code).让我们考虑一个销售东西的组织(我不是想猜测作者的意图,而是提供一个允许对代码进行推理的用例)。 There are a bunch of departments , each with a unique identifier .有很多部门,每个部门都有唯一的标识符

Each department has a collection of products that it sells.每个部门都有其销售的一系列产品 Department gets different products from different suppliers.部门从不同的供应商处获得不同的产品。 And in turn, each product has a unique id a collection of suppliers represented by plain string (it looks contrived, but keep in mind it's just an illustration of what the code does).反过来,每个产品都有一个唯一的 id ,一个由纯字符串表示的供应商集合(它看起来是人为的,但请记住,这只是代码功能的一个示例)。

As a use-case, let's assume that the company launches a new product and it must be accessible in all its departments.作为一个用例,我们假设公司推出了一种新产品,并且它的所有部门都必须可以访问它。 The code checks whether the department has this product already, if not , the product will be added with a default set of suppliers , otherwise it merges the existing set of suppliers and the default one.该代码检查该部门是否已经有该产品,如果没有,该产品将添加一组默认供应商否则合并现有供应商和默认供应商。

As you can see the code in the main method is very concise.正如您所看到的,main 方法中的代码非常简洁。 Note that all the miscellanies of data structures are still there , but we are not accessing them directly .请注意,数据结构的所有杂项仍然存在,但我们不直接访问它们 As the information expert principle suggests, this logic is hidden inside the objects.正如信息专家原则所暗示的那样,这种逻辑隐藏在对象内部。 That makes this solution reusable and less error-prone.这使得该解决方案可重用且不易出错。

    public static void main(String[] args) {
        // this list is a rough equivalent of the "List<Map<String, List<Map<String, Object>>>> txnvalues"
        List<Department> departments =
                List.of(new Department("dep11"), new Department("dep12"));
        
        Product newProd = new Product("id123"); // a NEW Product with id = "id123"
        newProd.addAllSuppliers(List.of("supplierA", "supplierB"));
        
        for (Department dep: departments) { // launching the new Product
            dep.mergeProduct(newProd);
        }
    }
public class Department {
    private final String departmentId;
    private final Map<String, Product> idToProduct;

    public Department(String departmentName) {
        this.departmentId = departmentName;
        this.idToProduct = new HashMap<>();
    }

    public void mergeProduct(Product prod) {
        idToProduct.merge(prod.getId(), prod, Product::merge);
    }

    public void mergeAllProducts(Iterable<Product> products) {
        for (Product prod: products) {
            mergeProduct(prod);
        }
    }

    public void addProduct(Product prod) {
        idToProduct.put(prod.getId(), prod);
    }

    public void addAllProducts(Iterable<Product> products) {
        for (Product prod: products) {
            addProduct(prod);
        }
    }

    public String getId() {
        return departmentId;
    }

    public Map<String, Product> getIdToProduct() {
        return Collections.unmodifiableMap(idToProduct);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof Department other) {
            return departmentId.equals(other.departmentId);
        } else return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(departmentId);
    }
}
public class Product {
    private final String productId;
    private final Set<String> suppliers;

    public Product(String id) {
        this.productId = id;
        this.suppliers = new HashSet<>();
    }

    public boolean addSupplier(String newSup) {
        return suppliers.add(newSup);
    }

    public boolean addAllSuppliers(Collection<String> newSup) {
        return suppliers.addAll(newSup);
    }

    public Product merge(Product other) {
        if (!this.equals(other)) throw new IllegalArgumentException();

        Product merged = new Product(productId);
        merged.addAllSuppliers(this.suppliers);
        merged.addAllSuppliers(other.suppliers);
        return merged;
    }

    public String getId() {
        return productId;
    }

    public Set<String> getSuppliers() {
        return Collections.unmodifiableSet(suppliers);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof Product other) {
            return this.productId.equals(other.productId);
        } else return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(productId);
    }
}

Further steps:进一步的步骤:

  • First of all make sure that you don't have gaps in the core concepts of OOP : encapsulation , inheritance , and polymorphism .首先确保你在OOP的核心概念上没有差距:封装inheritance多态
  • Draw before you start to code, it's not necessary to create a full-blown UML diagram.在开始编码之前画图,没有必要创建完整的 UML 图。 Even a rough set of named boxes with arrows will help you understand better how your system is structured and how its parts interact with each other.即使是一组粗略的带箭头的命名框也可以帮助您更好地了解系统的结构及其各部分之间的交互方式。
  • Read and apply .阅读并应用 Extend your knowledge gradually and try to apply it.逐渐扩展您的知识并尝试应用它。 High cohesion , Low coupling , SOLID , and lots of helpful reading can be found here, for instance this recent post可以在这里找到高内聚低耦合SOLID和许多有用的阅读材料, 例如最近的这篇文章
  • Write a bit, test a bit : don't wait until your code became a beast.写一点,测试一点:不要等到你的代码变成了野兽。 Write a bit and give it a try, add something else and take a look at how these parts fit together.写一点试试看,再添加一些东西,看看这些部分是如何组合在一起的。

In the else block, you call get method of txnvalues which a list of HashMap s and thus it expects an integer index.else块中,您调用HashMapget方法,它是txnvalues的列表,因此它需要一个 integer 索引。 I believe you assume that at this point you've got a reference to the HashMap to which you would add the values.我相信您假设此时您已获得对HashMap的引用,您将向其添加值。 But you don't.但你没有。

So, you need to find the index where to add the values, which means you have to look through the txnvalues list again.因此,您需要找到添加值的索引,这意味着您必须再次查看txnvalues列表。

For this reason, you should use a different approach:因此,您应该使用不同的方法:

txnvalues.stream()
         .filter(m -> m.get("tID").equals(txnID))
         .findFirst()
         .ifPresentOrElse(
                 m -> m.get("Values").add(attrData),
                 () -> {
                     HashMap data = new HashMap<>();
                     // Other stuff to fill the data
                     txnvalues.add(data);
                 }
         );

Here .filter(m -> m.get("tID").equals(txnID)) corresponds to your .anyMatch(list -> list.containsValue(txnID)) (the parameter list is actually instance of HashMap ).这里.filter(m -> m.get("tID").equals(txnID))对应于你的.anyMatch(list -> list.containsValue(txnID)) (参数list实际上是HashMap的实例)。

I changed the condition: according to your data sample, you looking for Map which has txnID value for the "tID" key, therefore getting the value of this key is faster than looking through all the values in the HashMap .我更改了条件:根据您的数据样本,您正在寻找Map ,它具有"tID"键的txnID值,因此获取此键的值比查看HashMap中的所有值更快。 (It may return null .) (它可能返回null 。)

So filter will return only the entries which contain match the required value of the "tID" key.因此, filter将仅返回包含匹配"tID"键所需值的条目。 Then .findFirst() “returns” the reference to that HashMap .然后.findFirst() “返回”对HashMap的引用。 Now .ifPresentOrElse performs the actions you want:现在.ifPresentOrElse执行您想要的操作:

  1. m.get("Values").add(attrData) into the list; m.get("Values").add(attrData)到列表中; this corresponds your one line of code in the else block;这对应于您在else块中的一行代码;
  2. the other code is what you had in the if block: if nothing is found, create the new instance.其他代码是您在if块中的代码:如果未找到任何内容,则创建新实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM