如何将自定义Counting类作为容器使用Stream.collect方法?
我是一名学生,您可能觉得我在代码中做了不必要的冗余操作,但我的目的只是加深对collect方法的理解。所有提及的组件都属于本次主题范畴,我的目标是解决报错问题,而非寻找实现相同结果的其他方法,请勿提供替代方案!提前感谢。
以下是我的代码:
package konekcija; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.JToolBar; import java.awt.EventQueue; import java.sql.*; import java.util.HashMap; import java.util.Queue; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.Map.Entry; public class app { private JFrame okvir; private JList lst; private JLabel[] conn = new JLabel[20]; private JTextField txt; private JPanel[] pnl = new JPanel[20]; private JButton query[] = new JButton[20]; private JToolBar box; public static void main(String[] args) { EventQueue.invokeLater(() -> { var app = new app(); app.okvir.setVisible(true); }); } public app() { this.initialize(); } record Counting(HashMap<JPanel,Integer> hm){ public Counting() { this(new HashMap<JPanel,Integer> ());// odnos glavnog yapisa i adicionih svojstava u nekanonskom konstrunktu } public void accumulator(JPanel p) { this.hm.put(p, 1); } // samo da ukazem na to da se iako su polja kao u nekoj POJO oznakom final, ne znaci da je sadrzaj ref konacan public void combiner(Counting cn) { var key = cn.hm().keySet().stream().findFirst().get(); this.hm.put(key, (int)this.hm.getOrDefault(key,0)+1); } } private void initialize() { okvir = new JFrame(); okvir.getContentPane().setLayout(null); okvir.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); okvir.setBounds(000, 0000, 1880, 1880); okvir.setResizable(false); okvir.setTitle("PREDUZEĆE"); Stream.of( (Stream.of(this.pnl).limit(3) .collect(new Counting(),(c,p)->((Counting) c).accumulator(p), (c1,c2)->((Counting) c2).combiner((Counting) c1))).hm().entrySet()).peek((entry)->{ var idx=entry.getValue(); var pnl=entry.getKey(); switch(idx) { case 1: pnl.setBounds(0,0,idx*100,100); break; case 2: pnl.setBounds(0,0,idx*100,100); break; case 3: pnl.setBounds(0,0,idx*100,100); break; default: break; } }).close(); } }
问题描述
问题出在collect相关部分,程序无法识别自定义的Counting record作为容器供应者。我在书中见过类似的用法,或是使用HashMap、List等预定义类,但原理应该一致。我哪里出错了?需要说明的是,collect部分出现错误后,我凭记忆编写了后续的流操作,只是为了代码完整,现在我需要修复collect相关部分。这里我使用了两个流,一个从传入的Counting实例中获取hm字段,提取entrySet并生成新的外部流。
核心错误点
collect的供应者参数类型错误:
collect方法的第一个参数需要的是Supplier<R>(无参构造器引用或Lambda),而非直接传入一个实例。你直接传new Counting()不符合方法签名,应该用Counting::new来提供容器的创建逻辑。流的创建错误:
Stream.of(this.pnl)会生成Stream<JPanel[]>(把整个数组当成单个元素),而你需要的是数组中每个JPanel的流,应该用Arrays.stream(this.pnl)。累加器/组合器的冗余强制转换:
由于供应者指定了类型,累加器和组合器的参数类型可以被自动推断,不需要强制转换(Counting) c,这会导致类型推断混乱。Combiner逻辑缺陷:
当前的combiner只处理了传入Counting实例中的第一个key,应该遍历所有entry进行合并,否则并行流场景下会丢失数据。
修复后的代码片段
修正Counting的combiner方法
public void combiner(Counting cn) { // 遍历传入的Counting中的所有entry,合并到当前实例的HashMap中 for (Entry<JPanel, Integer> entry : cn.hm().entrySet()) { this.hm.put(entry.getKey(), this.hm.getOrDefault(entry.getKey(), 0) + entry.getValue()); } }
修正initialize中的collect调用
// 先导入Arrays import java.util.Arrays; // 替换原collect相关代码 Counting countingResult = Arrays.stream(this.pnl) .limit(3) .collect(Counting::new, Counting::accumulator, Counting::combiner); // 后续处理entrySet的流 Stream.of(countingResult.hm().entrySet()) .peek(entry -> { var idx = entry.getValue(); var pnl = entry.getKey(); switch (idx) { case 1: pnl.setBounds(0, 0, idx * 100, 100); break; case 2: pnl.setBounds(0, 0, idx * 100, 100); break; case 3: pnl.setBounds(0, 0, idx * 100, 100); break; default: break; } }).close();
说明
- 用
Arrays.stream正确遍历数组元素 collect的三个参数分别为:容器供应者(Counting::new)、累加器方法引用(Counting::accumulator)、组合器方法引用(Counting::combiner),完全符合collect的方法签名- 修正后的combiner能正确合并所有元素,不管是串行还是并行流都能正常工作
内容的提问来源于stack exchange,提问作者Saerujeji Reebu CHINA

