Spring Boot GET API列表值重复问题求助
Spring Boot GET API列表值重复问题:复用POJO对象导致的引用重复
你遇到的重复问题核心原因很明确:列表中存储的是对象的引用,而你一直在复用同一个getAllRoomsobj对象。每次循环你只是修改这个对象的属性值,然后把它的引用添加到列表里,最终列表里的所有元素其实都是指向同一个对象的指针,所以最后所有元素都会显示最后一次循环设置的属性值。
下面给你提供几种符合你“不想每次创建新对象”需求的解决方案:
方案1:使用对象克隆实现复用
如果你只是想避免每次显式new对象,可以通过克隆当前对象的副本添加到列表,这样既复用了基础对象,又能保证列表元素独立。
步骤1:让GetAllRooms实现克隆能力
在你的POJO类里实现Cloneable接口并重写clone方法:
public class GetAllRooms implements Cloneable { private int roomId; private String roomName; // 省略getter、setter方法 @Override protected GetAllRooms clone() throws CloneNotSupportedException { // 浅克隆:适合属性都是基本类型的场景 return (GetAllRooms) super.clone(); // 如果有引用类型属性,需要手动实现深克隆,比如: // GetAllRooms clone = (GetAllRooms) super.clone(); // clone.setSomeRefField(this.getSomeRefField().clone()); // return clone; } }
步骤2:修改循环代码添加克隆副本
List<GetAllRooms> getallroomslistobj = new ArrayList<GetAllRooms>(); // 只初始化一次对象 GetAllRooms getAllRoomsobj = new GetAllRooms(); for (int i = 0; i < getroomslistobj.size(); i++) { int dbroomId = getroomslistobj.get(i).getRoomId(); String dbroomName = getroomslistobj.get(i).getRoomName(); // 修改复用对象的属性 getAllRoomsobj.setRoomId(dbroomId); getAllRoomsobj.setRoomName(dbroomName); // 添加克隆后的独立副本到列表 try { getallroomslistobj.add(getAllRoomsobj.clone()); } catch (CloneNotSupportedException e) { // 异常降级:克隆失败时新建对象兜底 GetAllRooms fallbackObj = new GetAllRooms(); fallbackObj.setRoomId(dbroomId); fallbackObj.setRoomName(dbroomName); getallroomslistobj.add(fallbackObj); } }
方案2:用对象池管理复用(适合高并发场景)
如果你的服务是高并发场景,担心频繁创建对象的性能开销,可以用对象池来统一管理GetAllRooms对象的复用,比如使用Apache Commons Pool2:
步骤1:添加依赖(Maven)
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.12.0</version> </dependency>
步骤2:创建对象池配置与工厂
import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; public class GetAllRoomsPool { private static final GenericObjectPool<GetAllRooms> pool; static { // 配置对象池参数 GenericObjectPoolConfig<GetAllRooms> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(20); // 池内最大对象数 config.setMaxIdle(10); // 最大空闲对象数 pool = new GenericObjectPool<>(new GetAllRoomsFactory(), config); } // 从池里借对象 public static GetAllRooms borrowObject() throws Exception { return pool.borrowObject(); } // 归还对象到池里(注意:如果列表需要长期持有对象,不能归还) public static void returnObject(GetAllRooms obj) { // 归还前重置属性,避免残留数据影响下次复用 obj.setRoomId(0); obj.setRoomName(null); pool.returnObject(obj); } } // 对象工厂:负责创建和包装池内对象 class GetAllRoomsFactory extends BasePooledObjectFactory<GetAllRooms> { @Override public GetAllRooms create() { return new GetAllRooms(); } @Override public PooledObject<GetAllRooms> wrap(GetAllRooms obj) { return new DefaultPooledObject<>(obj); } }
步骤3:循环中使用对象池
List<GetAllRooms> getallroomslistobj = new ArrayList<GetAllRooms>(); for (int i = 0; i < getroomslistobj.size(); i++) { int dbroomId = getroomslistobj.get(i).getRoomId(); String dbroomName = getroomslistobj.get(i).getRoomName(); try { // 从池里借对象 GetAllRooms obj = GetAllRoomsPool.borrowObject(); obj.setRoomId(dbroomId); obj.setRoomName(dbroomName); getallroomslistobj.add(obj); // 注意:如果后续列表需要长期保留对象,不要归还到池里,否则池复用对象会修改列表数据 // 若对象用完即弃,记得在合适时机调用GetAllRoomsPool.returnObject(obj); } catch (Exception e) { // 异常降级:借对象失败时新建对象 GetAllRooms fallbackObj = new GetAllRooms(); fallbackObj.setRoomId(dbroomId); fallbackObj.setRoomName(dbroomName); getallroomslistobj.add(fallbackObj); } }
额外建议
其实在大多数普通业务场景下,每次循环新建GetAllRooms对象的性能开销几乎可以忽略——JVM的垃圾回收机制对这类短生命周期的对象处理非常高效,代码复杂度也更低。如果没有特殊的性能要求,直接在循环内new对象是最简单且不易出错的方案。
内容的提问来源于stack exchange,提问作者GURU
相关产品推荐
相关产品推荐

