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

Android联系人APP中如何去除列表内重复联系人?

解决联系人APP中重复手机号的去重问题

我看到你在开发联系人APP时遇到了重复联系人去重的问题,尤其是像98*******33和+9198********33这类格式不同但实际是同一号码的情况,而且你尝试的Set和列表检查方法都没生效。问题主要出在手机号未标准化和ContactVO的equals/hashCode方法逻辑错误这两个点上,咱们一步步来解决:

一、当前代码的核心问题

1. equals方法逻辑完全写反了

你的equals方法里,当两个ContactVO的号码和姓名匹配时,居然返回false,这完全搞反了equals的语义——equals应该在两个对象相等时返回true,否则返回false。另外,字符串比较用==是错误的,应该用equals()或者equalsIgnoreCase(),==比较的是对象引用,不是字符串内容。

2. 手机号未做标准化处理

不同格式的同一手机号(比如带国家码、空格、符号)会被当成不同的字符串,比如+9198****33和98****33在未处理前是两个不同的字符串,自然无法被识别为重复。

3. contains方法使用错误

你用contactVOList.contains(phoneNumber)来判断列表是否包含某个手机号,但列表里存的是ContactVO对象,不是字符串,这个判断永远会返回false,导致所有联系人都被添加进去。

4. hashCode方法依赖未标准化的号码

hashCode是Set判断对象是否重复的关键之一,如果两个实际相同的联系人因为号码格式不同导致hashCode不同,Set也无法识别它们是重复的。

二、修复步骤和代码示例

1. 先写一个手机号标准化工具方法

这个方法会把手机号转换成统一格式,比如去掉所有非数字字符,或者针对印度号码(+91)去掉国家码,只保留核心号码:

private String normalizePhoneNumber(String phoneNumber) {
    if (phoneNumber == null) return "";
    // 去掉所有非数字字符
    String cleaned = phoneNumber.replaceAll("[^0-9]", "");
    // 如果是印度号码,去掉开头的91(因为+91去掉非数字后就是91开头)
    if (cleaned.startsWith("91") && cleaned.length() == 12) {
        return cleaned.substring(2);
    }
    return cleaned;
}

2. 修复ContactVO的equals和hashCode方法

现在基于标准化后的手机号来判断两个ContactVO是否相等,同时修正equals的逻辑:

public class ContactVO {
    private String contactImage;
    private String contactName;
    private String contactNumber;
    private boolean clicked;
    private String id;

    // 构造方法、getter/setter保持不变,建议变量名遵循Java小驼峰规范

    // 添加获取标准化号码的方法
    private String getNormalizedContactNumber() {
        if (contactNumber == null) return "";
        String cleaned = contactNumber.replaceAll("[^0-9]", "");
        if (cleaned.startsWith("91") && cleaned.length() == 12) {
            return cleaned.substring(2);
        }
        return cleaned;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        ContactVO contactVO = (ContactVO) obj;
        // 基于标准化后的号码判断是否为同一联系人
        return getNormalizedContactNumber().equals(contactVO.getNormalizedContactNumber());
    }

    @Override
    public int hashCode() {
        // 用标准化后的号码生成hashCode
        return getNormalizedContactNumber().hashCode();
    }
}

3. 修正联系人列表的填充逻辑

现在你可以直接用Set来存储联系人,利用Set的特性自动去重:

Cursor phones = getContext().getContentResolver().query(
        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
        null, null, null, null);

Set<ContactVO> contactSet = new HashSet<>(); // 用Set自动去重

if (phones != null) {
    while (phones.moveToNext()) {
        String name = phones.getString(phones.getColumnIndex(
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
        String phoneNumber = phones.getString(phones.getColumnIndex(
                ContactsContract.CommonDataKinds.Phone.NUMBER)).replace(" ", "");
        String imageUri = phones.getString(phones.getColumnIndex(
                ContactsContract.CommonDataKinds.Phone.PHOTO_URI));

        ContactVO contact = new ContactVO(imageUri, name, phoneNumber, false);
        contactSet.add(contact); // Set会自动判断是否重复,不会添加重复项
    }
    phones.close(); // 记得关闭Cursor,避免内存泄漏
}

// 把Set转成ArrayList使用
List<ContactVO> contactVOList = new ArrayList<>(contactSet);
System.out.println("去重后的列表大小:" + contactVOList.size());

三、额外说明

  • 如果你的需求是姓名和号码都相同才视为重复,可以在equals里同时判断标准化后的号码和姓名,但通常手机号唯一就能代表同一联系人。
  • 记得处理Cursor为null的情况,避免空指针异常,并且用完Cursor后要关闭它。
  • 标准化手机号的逻辑可以根据你的目标用户调整,比如针对不同国家的号码做不同的处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:40:57