我如何从嵌套在 hashmap 和 Java 中的列表中访问和添加值
How I can access and add value from the list which is nested in the hashmap and list in Java
我正在尝试为存储在 HashMap 中且具有一个父列表的列表添加值。
当我尝试这样做时,我得到“获取类型的方法与列表不兼容”
我正在尝试以下代码,逻辑是:
- 如果我在 txnValue 列表中得到 tID 的匹配值,我只是添加“Values" 列出否则我正在创建新的 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
}
}
示例:
[{
"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}]
}
]
我决定参数化所有各种可迭代对象。下面是参数化代码。
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
}
}
首先,您的代码中有多个错误,例如试图将字符串键和对象值放入 data
,这是一个仅采用字符串键和列表(字符串的 HashMaps)的 HashMap和对象)的价值。另一个是尝试通过字符串从 txnvalues
中获取项目,而 txnvalues
是一个列表,因此需要一个整数索引参数。
其次,这里有一个从未定义的变量:resultset
。我们不知道它是什么或如何使用它,因为它从未在其他地方被引用过。
第三,处理嵌套集的方法有很多种。这 >-> List<HashMap<String, List<HashMap<String, Object>>>>
简直太可怕了。
请re-write您的代码以可读、参数化并且可以正确编译而不会出错的方式编写。仅参数化将帮助您跟踪哪些可迭代对象采用哪些参数,并有助于防止您来这里寻求帮助时遇到的问题。
在 else
块中,您调用 txnvalues
的 get
方法,这是一个 HashMap
列表,因此它需要一个整数索引。我相信您假设此时您已经获得了要向其添加值的 HashMap
的引用。但你没有。
因此,您需要找到添加值的索引,这意味着您必须再次查看 txnvalues
列表。
因此,您应该使用不同的方法:
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);
}
);
这里.filter(m -> m.get("tID").equals(txnID))
对应你的.anyMatch(list -> list.containsValue(txnID))
(参数list
其实是HashMap
的实例)
我改变了条件:根据你的数据样本,你在寻找Map
,其中"tID"
键的值为txnID
,因此获取该键的值更快而不是查看 HashMap
中的所有值。 (可能returnnull
。)
因此 filter
将 return 仅包含匹配 "tID"
键所需值的条目。然后 .findFirst()
“returns” 引用 HashMap
。现在 .ifPresentOrElse
执行您想要的操作:
m.get("Values").add(attrData)
进入列表;这对应于 else
块中的一行代码;
- 其他代码是您在
if
块中的代码:如果未找到任何内容,请创建新实例。
我可能回答晚了。不过,我会介绍一种可能的补救措施,并附上详细的解释。
乍一看,如此深层次的嵌套collection似乎做作且难以理解。但是您在这段代码中看到的问题并不罕见,它们可以在 Whosebug 和许多存储库中的许多问题中观察到。唯一不同的是浓度。
让我们仔细检查一下。映射是一种常被初学者误用的数据结构,因为它允许组合不同性质的 object。我很确定提供的代码模型或多或少是有形的。您是否注意到 PO 试图访问一个条目,该条目具有一个名为 "id" 的字符串键?这清楚地表明此处使用 collections 代替 objects。
如果我说 object 图形可以复杂得多,它可能不会是新事物。但是这样写的代码怎么推理呢?
让我们暂时搁置一下,考虑一下以下任务:
- 有许多帆船,您需要确定其中哪艘将赢得比赛,并return其名称;
- 以纯文本形式提供的输入,包含以下参数:唯一名称、位移和重量(为简单起见,仅这三个);
- 船只的速度取决于其排水量和重量(即提供公式,我们只需要解析值);
很有可能有人能想出这样的解决方案:
- 创建一个
Map<String, List<Double>>
,其中key是帆船的名字,value是一个包含排水量和重量的列表;
- 然后遍历条目集,应用公式,找到最快的船只。
只有几种方法,似乎单独的class帆船据称增加了整体复杂性和代码量。对于许多学生来说,这是一种常见的 错觉 。创建一个单独的 class 将为代码提供一个逻辑结构,如果您希望扩展或重用它,将会得到回报。请注意,不仅帆船的属性必须属于此class,而且允许计算帆船速度并基于它比较帆船的方法也属于此。
分解是一门功夫,是要练出来的。对于那些从一开始就没有意识到前面示例中的帆船必须由 object 表示的人,我建议尝试下一个 练习 :在不使用 objects 的情况下描述一所大学、一家糖果店、一家杂货店、一只猫,以及 任何你喜欢的东西。首先,考虑一些 use-cases,它们需要访问您尝试建模的系统元素的某些属性。然后用warriorcollection和array画图写代码,注意你的系统越复杂,嵌套的maps和lists越麻烦,导致你的代码写成这样:
map.get(something).get(something).add(somethingElse);
然后,当您看到问题时,您就可以实施在您的域模型中有意义的 classes 并比较这两种方法。
免责声明:理解分解是至关重要的,但class设计是一个非常广泛的话题,这方面有很多东西要研究classic 原则和设计模式等领域。但在深入探讨这些主题之前,您必须对 decomposition 和 OOP 有深入的了解。如果没有这些知识,即使使用 object-oriented 方法,您的解决方案也可能变得复杂且难以管理。但这是朝着正确方向迈出的一步。仅凭您使用 object-oriented 语言这一事实并不会自动使您的解决方案成为 object-oriented。是一门学问,是要练出来的。
说了很长的题外话,现在进入正题。
正如我已经说过的,我相信 post 作者考虑到了某种自然用例。我们只能看到 dump get()
和 put()
,而不是描述这个数据结构迷宫中系统的名称。但是在地图的使用中有一个线索。 id 作为 key 是一个明确的指标,表明它必须是 object用地图代替。
这是旅程的开始,我将尝试提供一个有意义(至少有一点)的场景和适合本文开头提供的方案中描述的结构的系统部分post.
让我们考虑一个销售东西的组织(我不是想猜测作者的意图,而是提供一个允许对代码进行推理的用例)。有一堆部门,每个部门都有一个唯一标识符
每个部门 销售 collection 种 产品。部门从不同的供应商处获得不同的产品。反过来,每个 product 都有一个 unique id a collection of suppliers represented by纯字符串(看起来做作,但请记住,这只是代码功能的一个示例)。
作为 use-case,我们假设公司推出了 新产品,并且所有部门都必须可以访问它。该代码检查该部门是否已经有该产品,如果没有,该产品将添加默认供应商集,否则它合并现有供应商组和默认供应商组。
可以看到main方法中的代码非常简洁。请注意,所有数据结构的杂项仍然存在,但我们不直接访问它们。正如 information expert principle 所暗示的那样,此逻辑隐藏在 object 中。这使得该解决方案可重用且更少 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);
}
}
进一步的步骤:
- 首先确保你在OOP的核心概念上没有空白:encapsulation, 继承和多态.
- 画 在开始编码之前,没有必要创建 full-blown UML 图。即使是一组带有箭头的粗略命名框也可以帮助您更好地了解系统的结构及其各部分之间的交互方式。
- 阅读并申请。逐渐扩展您的知识并尝试应用它。 High cohesion, Low coupling, SOLID, and lots of helpful reading can be found here, for instance this recent post
- 写一点,测试一点:不要等到你的代码变成了野兽。写一点试一试,再添加一些东西,看看这些部分是如何组合在一起的。
我正在尝试为存储在 HashMap 中且具有一个父列表的列表添加值。
当我尝试这样做时,我得到“获取类型的方法与列表不兼容”
我正在尝试以下代码,逻辑是:
- 如果我在 txnValue 列表中得到 tID 的匹配值,我只是添加“Values" 列出否则我正在创建新的 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
}
}
示例:
[{
"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}]
}
]
我决定参数化所有各种可迭代对象。下面是参数化代码。
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
}
}
首先,您的代码中有多个错误,例如试图将字符串键和对象值放入 data
,这是一个仅采用字符串键和列表(字符串的 HashMaps)的 HashMap和对象)的价值。另一个是尝试通过字符串从 txnvalues
中获取项目,而 txnvalues
是一个列表,因此需要一个整数索引参数。
其次,这里有一个从未定义的变量:resultset
。我们不知道它是什么或如何使用它,因为它从未在其他地方被引用过。
第三,处理嵌套集的方法有很多种。这 >-> List<HashMap<String, List<HashMap<String, Object>>>>
简直太可怕了。
请re-write您的代码以可读、参数化并且可以正确编译而不会出错的方式编写。仅参数化将帮助您跟踪哪些可迭代对象采用哪些参数,并有助于防止您来这里寻求帮助时遇到的问题。
在 else
块中,您调用 txnvalues
的 get
方法,这是一个 HashMap
列表,因此它需要一个整数索引。我相信您假设此时您已经获得了要向其添加值的 HashMap
的引用。但你没有。
因此,您需要找到添加值的索引,这意味着您必须再次查看 txnvalues
列表。
因此,您应该使用不同的方法:
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);
}
);
这里.filter(m -> m.get("tID").equals(txnID))
对应你的.anyMatch(list -> list.containsValue(txnID))
(参数list
其实是HashMap
的实例)
我改变了条件:根据你的数据样本,你在寻找Map
,其中"tID"
键的值为txnID
,因此获取该键的值更快而不是查看 HashMap
中的所有值。 (可能returnnull
。)
因此 filter
将 return 仅包含匹配 "tID"
键所需值的条目。然后 .findFirst()
“returns” 引用 HashMap
。现在 .ifPresentOrElse
执行您想要的操作:
m.get("Values").add(attrData)
进入列表;这对应于else
块中的一行代码;- 其他代码是您在
if
块中的代码:如果未找到任何内容,请创建新实例。
我可能回答晚了。不过,我会介绍一种可能的补救措施,并附上详细的解释。
乍一看,如此深层次的嵌套collection似乎做作且难以理解。但是您在这段代码中看到的问题并不罕见,它们可以在 Whosebug 和许多存储库中的许多问题中观察到。唯一不同的是浓度。
让我们仔细检查一下。映射是一种常被初学者误用的数据结构,因为它允许组合不同性质的 object。我很确定提供的代码模型或多或少是有形的。您是否注意到 PO 试图访问一个条目,该条目具有一个名为 "id" 的字符串键?这清楚地表明此处使用 collections 代替 objects。
如果我说 object 图形可以复杂得多,它可能不会是新事物。但是这样写的代码怎么推理呢?
让我们暂时搁置一下,考虑一下以下任务:
- 有许多帆船,您需要确定其中哪艘将赢得比赛,并return其名称;
- 以纯文本形式提供的输入,包含以下参数:唯一名称、位移和重量(为简单起见,仅这三个);
- 船只的速度取决于其排水量和重量(即提供公式,我们只需要解析值);
很有可能有人能想出这样的解决方案:
- 创建一个
Map<String, List<Double>>
,其中key是帆船的名字,value是一个包含排水量和重量的列表; - 然后遍历条目集,应用公式,找到最快的船只。
只有几种方法,似乎单独的class帆船据称增加了整体复杂性和代码量。对于许多学生来说,这是一种常见的 错觉 。创建一个单独的 class 将为代码提供一个逻辑结构,如果您希望扩展或重用它,将会得到回报。请注意,不仅帆船的属性必须属于此class,而且允许计算帆船速度并基于它比较帆船的方法也属于此。
分解是一门功夫,是要练出来的。对于那些从一开始就没有意识到前面示例中的帆船必须由 object 表示的人,我建议尝试下一个 练习 :在不使用 objects 的情况下描述一所大学、一家糖果店、一家杂货店、一只猫,以及 任何你喜欢的东西。首先,考虑一些 use-cases,它们需要访问您尝试建模的系统元素的某些属性。然后用warriorcollection和array画图写代码,注意你的系统越复杂,嵌套的maps和lists越麻烦,导致你的代码写成这样:
map.get(something).get(something).add(somethingElse);
然后,当您看到问题时,您就可以实施在您的域模型中有意义的 classes 并比较这两种方法。
免责声明:理解分解是至关重要的,但class设计是一个非常广泛的话题,这方面有很多东西要研究classic 原则和设计模式等领域。但在深入探讨这些主题之前,您必须对 decomposition 和 OOP 有深入的了解。如果没有这些知识,即使使用 object-oriented 方法,您的解决方案也可能变得复杂且难以管理。但这是朝着正确方向迈出的一步。仅凭您使用 object-oriented 语言这一事实并不会自动使您的解决方案成为 object-oriented。是一门学问,是要练出来的。
说了很长的题外话,现在进入正题。
正如我已经说过的,我相信 post 作者考虑到了某种自然用例。我们只能看到 dump get()
和 put()
,而不是描述这个数据结构迷宫中系统的名称。但是在地图的使用中有一个线索。 id 作为 key 是一个明确的指标,表明它必须是 object用地图代替。
这是旅程的开始,我将尝试提供一个有意义(至少有一点)的场景和适合本文开头提供的方案中描述的结构的系统部分post.
让我们考虑一个销售东西的组织(我不是想猜测作者的意图,而是提供一个允许对代码进行推理的用例)。有一堆部门,每个部门都有一个唯一标识符
每个部门 销售 collection 种 产品。部门从不同的供应商处获得不同的产品。反过来,每个 product 都有一个 unique id a collection of suppliers represented by纯字符串(看起来做作,但请记住,这只是代码功能的一个示例)。
作为 use-case,我们假设公司推出了 新产品,并且所有部门都必须可以访问它。该代码检查该部门是否已经有该产品,如果没有,该产品将添加默认供应商集,否则它合并现有供应商组和默认供应商组。
可以看到main方法中的代码非常简洁。请注意,所有数据结构的杂项仍然存在,但我们不直接访问它们。正如 information expert principle 所暗示的那样,此逻辑隐藏在 object 中。这使得该解决方案可重用且更少 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);
}
}
进一步的步骤:
- 首先确保你在OOP的核心概念上没有空白:encapsulation, 继承和多态.
- 画 在开始编码之前,没有必要创建 full-blown UML 图。即使是一组带有箭头的粗略命名框也可以帮助您更好地了解系统的结构及其各部分之间的交互方式。
- 阅读并申请。逐渐扩展您的知识并尝试应用它。 High cohesion, Low coupling, SOLID, and lots of helpful reading can be found here, for instance this recent post
- 写一点,测试一点:不要等到你的代码变成了野兽。写一点试一试,再添加一些东西,看看这些部分是如何组合在一起的。