You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android HashSet多线程添加User对象去重失效问题咨询

问题现象

发起网络请求并完成数据库数据校验后,Set集合无法正常过滤重复数据。
具体场景:发起网络请求拉取全部帖子数据后,计划将用户数据存入Set以实现天然去重,但实际运行时重复用户也会被成功添加,总计10条包含重复条目的用户数据全部打印了"ok user add"的添加成功日志。

现有实现代码

网络请求核心逻辑

public static void send_request_post(String sid, int did, Activity activity, AppCompatActivity appCompatActivity) {
        RequestQueue requestQueue = Volley.newRequestQueue(activity);
        JSONObject requestParam = new JSONObject();
        String GET_POST = "https://helme/cc/helpme.php";
        Set<User> user= new HashSet<User>();
        Log.d("main_activity", "fino a qui funziona");
        try {
            requestParam.put("sid", sid);
            requestParam.put("did", did);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        JsonObjectRequest req = new JsonObjectRequest(
                Request.Method.POST,
                GET_POST,
                requestParam,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) { 
                        try {
                            JSONArray sid_user111 = response.getJSONArray("posts");
                            for (int j = 0; j < sid_user111.length(); j++) {
                                JSONObject postss = sid_user111.getJSONObject(j);            
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        try {
                                            if(!(Model_user.getInstance
                                                    (activity.getApplicationContext()).getcontainsuid
                                                    (postss.getString("author")))){
                                                user.add(new User(postss.getString("author"),
                                                        postss.getString("authorName"), String.valueOf(0),
                                                        postss.getString("pversion")));
                                                Log.d("main_activity_user","ok user add");

                                            }} catch (JSONException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }).start();
                            }
                            } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                    }
                });
        MainActivity.requestQueue.add(req);
    }

User DAO 校验方法

@Query("select Exists(select uid from User where uid =:useruid)")
Boolean loadAllByuid(String useruid);

该方法用于校验指定作者uid是否已存在于本地数据库中。

Model_user 单例实现

public class Model_user {
    private static Model_user theinstance = null;
    private static AppDatabase userDAOs = null;
    public static synchronized Model_user getInstance() {
        if (theinstance == null) {
            theinstance = new Model_user();
        }
        return theinstance;
    }
    public static synchronized Model_user getInstance(Context c) {
        if (theinstance == null) {
            userDAOs = Room.databaseBuilder(c, AppDatabase.class, "user_db").build();
            theinstance = new Model_user();
        }
        return theinstance;
    }
}
问题根因

重复数据被成功添加由三个核心问题共同导致:

  • User类未重写equals()和hashCode()方法
    HashSet去重的判断逻辑是先比对对象的hashCode值,hashCode一致时再调用equals()确认对象是否相等。如果自定义User类没有重写这两个方法,会默认继承Object类的实现,仅通过对象内存地址判定是否相等。哪怕两个User实例的uid、用户名等属性完全一致,只要是不同的new出来的对象,就会被判定为不同元素,直接加入Set。
  • 多线程并发操作非线程安全集合
    HashSet本身是线程不安全的容器,代码中在循环内为每一条帖子都新建了独立线程,并发操作同一个Set实例。多线程同时调用add()方法时,没有加锁也没有使用线程安全的集合实现,会出现写入覆盖、判断逻辑失效的问题,直接导致去重逻辑完全不可靠。
  • 数据库校验存在竞态条件
    子线程的执行顺序是不可控的,所有线程几乎同时启动:如果两条同作者的帖子对应的查库请求同时执行,此时数据库中还没有该用户的记录,两个线程都会拿到"用户不存在"的校验结果,后续都会执行add操作,最终导致重复数据写入。
修复方向
  • 为User类重写equals()和hashCode()方法,以唯一标识用户的uid(即author字段)作为相等判定的核心依据。
  • 避免在循环中随意创建无管控的子线程,要么在当前回调线程顺序遍历处理数据,要么使用线程池统一调度任务;同时将HashSet替换为线程安全的实现,比如用Collections.synchronizedSet()包装Set,或直接使用ConcurrentHashMap.newKeySet()。
  • 调整去重流程:先遍历所有帖子数据,在内存层按uid做第一轮去重,再和数据库做批量校验,最后将不存在的用户批量入库、存入Set,从流程上避免多线程竞态问题。

内容的提问来源于stack exchange,提问作者Elly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 20:12:17