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

Android中Map<String,String>转List<Map<String,String>>循环仅存当前值问题

问题:List<Map<String, String>>添加元素后所有元素被最后一次数据覆盖

我尝试通过以下代码向List<Map<String, String>>中添加元素:

@NonNull
public static List<Map<String, String>> mysqlSelect(@NonNull String select, String where) {
    Map<String, String> info = new HashMap<String,String>();
    List<Map<String, String>> list = new ArrayList<>();
    int selectCnt = select.replaceAll("[^,]","").length();
    final boolean[] b = {true};

    new Thread(new Runnable() {
        @Override
        public void run(){
            try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
                String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser";
                PreparedStatement statement = connection.prepareStatement(sql);
                ResultSet resultSet = statement.executeQuery();

                while (resultSet.next()) {
                    info.put("name", resultSet.getString("fb_firstname"));
                    info.put("address", resultSet.getString("fb_lastname"));
                    info.put("phone_number", resultSet.getString("fb_id"));

                    list.add(info);
                }

                b[0] = false;
            } catch (Exception e) {
                Log.e("InfoAsyncTask", "Error reading school information", e);
                b[0] = false;
            }
        }
    }).start();

    while (b[0]) { }

    return list; //result.get("name")
}

在while循环执行时,列表会被填充所需的3个字符串值,但每次循环后,之前所有元素的值都会被替换为最后一次读取的数据。例如:
第一次循环后:

name: bob barker
address: something
phone-number = 4235421212

第二次循环后:

name: steve jobs
address: something again
phone-number = 8546521111

此时列表中第一次循环的数据也变成了第二次的内容:

name: steve jobs
address: something again
phone-number = 8546521111

请问我忽略了什么?

更新1

我原本的代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new InfoAsyncTask().execute();
}

@SuppressLint("StaticFieldLeak")
public class InfoAsyncTask extends AsyncTask<Void, Void, Map<String, String>> {
    @Override
    protected Map<String, String> doInBackground(Void... voids) {
        Map<String, String> info = new HashMap<>();

        try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
            String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser LIMIT 1";
            PreparedStatement statement = connection.prepareStatement(sql);
            ResultSet resultSet = statement.executeQuery();
            if (resultSet.next()) {
                info.put("name", resultSet.getString("fb_firstname"));
                info.put("address", resultSet.getString("fb_lastname"));
                info.put("phone_number", resultSet.getString("fb_id"));
            }
        } catch (Exception e) {
            Log.e("InfoAsyncTask", "Error reading school information", e);
        }

        return info;
    }

    @Override
    protected void onPostExecute(Map<String, String> result) {
        if (!result.isEmpty()) {
            TextView textViewName = findViewById(R.id.textViewName);
            TextView textViewAddress = findViewById(R.id.textViewAddress);
            TextView textViewPhoneNumber = findViewById(R.id.textViewPhone);

            textViewName.setText(result.get("name"));
            textViewAddress.setText(result.get("address"));
            textViewPhoneNumber.setText(result.get("phone_number"));
        }
    }
}

但我想将这段逻辑放到独立的类文件中,并在onCreate中调用。


解答

核心问题:重复引用同一个Map对象

你只创建了一个Map<String, String> info实例,每次循环只是修改这个实例里的键值对,然后把同一个对象的引用添加到List中。List里的所有元素本质上都是指向同一个Map对象的指针,所以最后一次修改会覆盖所有元素的显示内容。

修复方案

把Map<String, String> info = new HashMap<>();的创建放到while (resultSet.next())循环内部,每次循环都生成一个新的Map实例:

while (resultSet.next()) {
    // 每次循环新建一个Map对象
    Map<String, String> info = new HashMap<>();
    info.put("name", resultSet.getString("fb_firstname"));
    info.put("address", resultSet.getString("fb_lastname"));
    info.put("phone_number", resultSet.getString("fb_id"));

    list.add(info);
}

额外优化建议

  1. 避免忙等待(空循环):你用while (b[0]) { }实现线程等待,这会占用CPU资源,推荐用CountDownLatch来实现异步任务的结果等待:
public static List<Map<String, String>> mysqlSelect(@NonNull String select, String where) {
    List<Map<String, String>> list = new ArrayList<>();
    CountDownLatch latch = new CountDownLatch(1);

    new Thread(new Runnable() {
        @Override
        public void run(){
            try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
                String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser";
                PreparedStatement statement = connection.prepareStatement(sql);
                ResultSet resultSet = statement.executeQuery();

                while (resultSet.next()) {
                    Map<String, String> info = new HashMap<>();
                    info.put("name", resultSet.getString("fb_firstname"));
                    info.put("address", resultSet.getString("fb_lastname"));
                    info.put("phone_number", resultSet.getString("fb_id"));
                    list.add(info);
                }
            } catch (Exception e) {
                Log.e("InfoAsyncTask", "Error reading school information", e);
            } finally {
                latch.countDown();
            }
        }
    }).start();

    try {
        latch.await(); // 等待线程执行完成
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

    return list;
}
  1. 独立类的实现:如果要把数据库逻辑放到独立类,推荐用回调来传递结果(避免阻塞主线程),示例如下:
// 独立的数据库操作类
public class DbHelper {
    private static final String URL = "你的数据库URL";
    private static final String USER = "用户名";
    private static final String PASSWORD = "密码";

    // 用回调返回结果
    public interface OnDataLoadedListener {
        void onDataLoaded(List<Map<String, String>> data);
        void onError(Exception e);
    }

    public void fetchUserList(OnDataLoadedListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<Map<String, String>> list = new ArrayList<>();
                try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
                    String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser";
                    PreparedStatement statement = connection.prepareStatement(sql);
                    ResultSet resultSet = statement.executeQuery();

                    while (resultSet.next()) {
                        Map<String, String> info = new HashMap<>();
                        info.put("name", resultSet.getString("fb_firstname"));
                        info.put("address", resultSet.getString("fb_lastname"));
                        info.put("phone_number", resultSet.getString("fb_id"));
                        list.add(info);
                    }
                    // 主线程回调结果
                    new Handler(Looper.getMainLooper()).post(() -> listener.onDataLoaded(list));
                } catch (Exception e) {
                    Log.e("DbHelper", "Error fetching user data", e);
                    new Handler(Looper.getMainLooper()).post(() -> listener.onError(e));
                }
            }
        }).start();
    }
}

在Activity的onCreate中调用:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DbHelper dbHelper = new DbHelper();
    dbHelper.fetchUserList(new DbHelper.OnDataLoadedListener() {
        @Override
        public void onDataLoaded(List<Map<String, String>> data) {
            // 处理返回的用户列表,比如更新UI
            if (!data.isEmpty()) {
                // 示例:显示第一个用户数据
                Map<String, String> firstUser = data.get(0);
                TextView textViewName = findViewById(R.id.textViewName);
                TextView textViewAddress = findViewById(R.id.textViewAddress);
                TextView textViewPhoneNumber = findViewById(R.id.textViewPhone);

                textViewName.setText(firstUser.get("name"));
                textViewAddress.setText(firstUser.get("address"));
                textViewPhoneNumber.setText(firstUser.get("phone_number"));
            }
        }

        @Override
        public void onError(Exception e) {
            // 处理错误
            Toast.makeText(MainActivity.this, "数据加载失败", Toast.LENGTH_SHORT).show();
        }
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 03:35:17