Firestore嵌套HashMap值求和报错及最优实现方案求助
解决Firestore嵌套HashMap求和问题及页面传值方案
先给你理清楚当前的两个核心问题:一是Long::longValue的编译错误,二是你填充外层HashMap的逻辑有bug(会覆盖之前的chapter数据),最后再讲怎么把求和结果传到图表页面。
1. 修复Non-static method cannot be referenced from static content错误
这个错误的根源是:你stream处理的是Object类型的值,直接用Long::longValue(这是Long类的实例方法)编译器不认——它不知道这个Object一定是Long。而且Firestore返回的数值可能是Long或Integer,所以得做安全转型:
修改你的doSumm()方法里的求和逻辑:
// 先定义一个全局Map存每个手机号的总和,方便后续传值 private Map<String, Long> phoneToTotalSum = new HashMap<>(); private void doSumm() { Toast.makeText(getApplicationContext(), "Outer Size " + outer.size(), Toast.LENGTH_SHORT).show(); for (Map.Entry<String, Map<String, Object>> entry : outer.entrySet()) { Map<String, Object> innerChapterMap = entry.getValue(); // 安全转型并求和:兼容Long和Integer类型 long total = innerChapterMap.values().stream() .mapToLong(val -> { if (val instanceof Long) { return (Long) val; } else if (val instanceof Integer) { return ((Integer) val).longValue(); } return 0; // 遇到非数值类型时的默认值,可根据需求调整 }) .sum(); // 把结果存入全局Map phoneToTotalSum.put(entry.getKey(), total); } }
2. 修复外层HashMap的填充逻辑
你现在的代码每次遇到chapter字段都会新建一个inner HashMap并覆盖原来的,导致每个手机号最后只保留一个chapter的值,这完全不符合需求!应该先获取已有的inner Map,再添加新的chapter:
修改Firestore的回调代码:
CollectionReference chemistry = db.collection("RESULTS").document("Summary").collection("ChemistryVII"); chemistry .whereEqualTo("school", "Test School") .get() .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> task) { if (!task.isSuccessful()) { // 别忘了处理查询失败的情况 Toast.makeText(getApplicationContext(), "查询失败:" + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); return; } outer.clear(); // 先清空旧数据 for (DocumentSnapshot document : task.getResult()) { Map<String, Object> map = document.getData(); if (map == null) continue; // 处理空文档 // 先获取手机号,做空值判断 Object phoneObj = map.get("phonenumb"); if (phoneObj == null) continue; String phonenumb = phoneObj.toString(); // 拿到当前手机号对应的inner Map,没有就新建 Map<String, Object> innerMap = outer.getOrDefault(phonenumb, new HashMap<>()); // 遍历所有chapter字段,加入innerMap for (Map.Entry<String, Object> entry : map.entrySet()) { // 如果是chapter开头的字段,才加入(比contains更准确) if (entry.getKey().startsWith("chapter")) { innerMap.put(entry.getKey(), entry.getValue()); } } // 把更新后的innerMap放回outer outer.put(phonenumb, innerMap); } // 所有文档处理完再调用求和,不要在循环里重复调用 doSumm(); Toast.makeText(getApplicationContext(), "数据处理完成", Toast.LENGTH_SHORT).show(); } });
3. 把求和结果传到图表页面
因为phoneToTotalSum是Map<String, Long>,而String和Long都是可序列化的,所以可以用Intent直接传递:
跳转页面时:
// 假设你的图表页面叫ChartDisplayActivity Intent intent = new Intent(this, ChartDisplayActivity.class); // 把Map转成Serializable传递 intent.putExtra("PHONE_CHAPTER_SUM", (Serializable) phoneToTotalSum); startActivity(intent);
在图表页面接收:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chart_display); // 获取传递过来的求和Map Map<String, Long> sumMap = (Map<String, Long>) getIntent().getSerializableExtra("PHONE_CHAPTER_SUM"); // 接下来就可以用sumMap来生成图表了,比如遍历key和value构建数据集 if (sumMap != null) { for (Map.Entry<String, Long> entry : sumMap.entrySet()) { String phone = entry.getKey(); long total = entry.getValue(); // 这里添加到图表数据集中 } } }
额外小建议
- 不要在Firestore的循环里频繁调用
doSumm(),等所有文档都处理完再调用一次,减少不必要的计算。 - 处理Firestore数据时一定要加空值判断,避免NullPointerException炸锅。
- 如果chapter字段是固定前缀(比如chapter1、chapter2),用
startsWith("chapter")比contains("chapter")更准确,防止匹配到类似"xxchapterxx"的意外字段。
内容的提问来源于stack exchange,提问作者gputhige




