用 List.contains(Object obj)判断是否包含指定对象,避坑实践总结

一、需求

往ES中存储通讯录信息,增量操作。

思路:每一次插入前,先查询ES中的数据,看看是否有记录:没有记录则直接插入;有记录则遍历记录,看看每一个record是否包含在要新增的List里面,不在则add进去。

二、实现

1、最初方式及问题

1)bean类

package com.xxx.cash.entity.deviceinfo;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 设备通讯录信息
 */
public class ContactsInfo extends DeviceInfoBase {

    private List records = new ArrayList<>();

    public List getRecords() {
        return records;
    }

    public void setRecords(List records) {
        if (records == null)
            this.records = Collections.emptyList();
        else
            this.records = records;
    }


    public static class AddressBook {
        private String name; // 姓名
        private String phone;
        @JsonProperty(value = "home_phone")
        private String homePhone; // 住宅电话
        @JsonProperty(value = "work_phone")
        private String workPhone; // 工作电话
        @JsonProperty(value = "last_updated_time")
        private String lastUpdatedTime;
        @JsonProperty(value = "last_time_contacted")
        private String lastTimeContacted;

        public String getLastTimeContacted() {
            return lastTimeContacted;
        }

        public void setLastTimeContacted(String lastTimeContacted) {
            this.lastTimeContacted = lastTimeContacted;
        }

        public String getLastUpdatedTime() {
            return lastUpdatedTime;
        }

        public void setLastUpdatedTime(String lastUpdatedTime) {
            this.lastUpdatedTime = lastUpdatedTime;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        public String getHomePhone() {
            return homePhone;
        }

        public void setHomePhone(String homePhone) {
            this.homePhone = homePhone;
        }

        public String getWorkPhone() {
            return workPhone;
        }

        public void setWorkPhone(String workPhone) {
            this.workPhone = workPhone;
        }

    }
}

2)实现类

package com.xxx.cash.service.impl.deviceinfo;


import com.xxx.cash.commons.EsEnum;
import com.xxx.cash.entity.deviceinfo.ContactsInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service("ContactsSaveServiceImpl")
public class ContactsSaveServiceImpl extends AbstractDeviceDataSaveService {
    @Override
    Class getEntityClass() {
        return ContactsInfo.class;
    }

    @Override
    String getESType() {
        return EsEnum.CONTACT.getType();
    }

    @Override
    void processEntity(ContactsInfo contactsInfo) {
        String key = contactsInfo.getUserID() + "-" + String.valueOf(contactsInfo.getType()) + "-" + contactsInfo.getImei();
        //查询ES中记录
        ContactsInfo oldContacts = esdao.getMapByKey(getIndex(), getESType(), key, new TypeReference() {
        });
        if (oldContacts != null && oldContacts.getRecords() != null) {
            List newRecords = contactsInfo.getRecords();
            List oldRecords = oldContacts.getRecords();

            for (ContactsInfo.AddressBook record : oldRecords) {
                //新纪录不包含有老数据,则add
                if (!newRecords.contains(record)) {
                    newRecords.add(record);
                }
            }

        }
    }
}

3)问题

ES中的同一个人的通讯录数据一直在增大,查询发现里面很多重复数据。经过校验发现,每一次新存储请求都全量存储,所以数据随着请求次数的增加而增大。

4)排查

通过debug发现,这个newRecords.contains(record)一直为false,newRecords.add(record)操作一直在做。

①查看源码

java.util.ArrayList的contains()方法
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
indexOf()方法中用到equals()方法,在看看equals()怎么实现的:
    public boolean equals(Object obj) {
        return (this == obj);
    }

从源码能看到,equals方法用的是Object类的,==双等号比较的是对象的地址值。

所以,这下子知道为什么newRecords.contains(record)一直为false了,对象的地址值不一样

2、修复

刚开始的思路有几个:

1)重写equals方法,然后想着什么去调用这个indexOf怎么调用equals,contains怎么调用indexOf

2)把新老数据先弄到一个list里面,然后再去遍历,存到set里面去重,然后在存到newRecords里面。

 

实践:

1)越想越复杂,就没实现了,但是在bean类重写了equals方法

2)在处理中,发现set进去后,对象不会去重,原因还没排查。但是在调试中,有了意外收获。

 

最终:

重写equals方法:

package com.xxx.cash.entity.deviceinfo;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 设备通讯录信息
 */
public class ContactsInfo extends DeviceInfoBase {

    private List records = new ArrayList<>();

    public List getRecords() {
        return records;
    }

    public void setRecords(List records) {
        if (records == null)
            this.records = Collections.emptyList();
        else
            this.records = records;
    }


    public static class AddressBook {
        private String name; // 姓名
        private String phone;
        @JsonProperty(value = "home_phone")
        private String homePhone; // 住宅电话
        @JsonProperty(value = "work_phone")
        private String workPhone; // 工作电话
        @JsonProperty(value = "last_updated_time")
        private String lastUpdatedTime;
        @JsonProperty(value = "last_time_contacted")
        private String lastTimeContacted;

        public String getLastTimeContacted() {
            return lastTimeContacted;
        }

        public void setLastTimeContacted(String lastTimeContacted) {
            this.lastTimeContacted = lastTimeContacted;
        }

        public String getLastUpdatedTime() {
            return lastUpdatedTime;
        }

        public void setLastUpdatedTime(String lastUpdatedTime) {
            this.lastUpdatedTime = lastUpdatedTime;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        public String getHomePhone() {
            return homePhone;
        }

        public void setHomePhone(String homePhone) {
            this.homePhone = homePhone;
        }

        public String getWorkPhone() {
            return workPhone;
        }

        public void setWorkPhone(String workPhone) {
            this.workPhone = workPhone;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
            AddressBook add = (AddressBook) obj;

            return
                    StringUtils.equals(homePhone, add.homePhone) &&
                            StringUtils.equals(name, add.name) &&
                            StringUtils.equals(phone, add.phone) &&
                            StringUtils.equals(workPhone, add.workPhone) &&
                            StringUtils.equals(lastUpdatedTime, add.lastUpdatedTime) &&
                            StringUtils.equals(lastTimeContacted, add.lastTimeContacted);

        }

    }
}

在调试中发现,contains方法下的indexOf方法会自动去调用重写的equals方法,真是爽,之前的纠结都解决了。

再次运行代码,同一个请求,发现es的存储结果不会增加了,达到了去重的效果了。

注意:

在此处,判断两个属性是否相等时,不要用==(==比较的是对象的地址值),也不要用homePhone.equals(add.homePhone)(容易报空指针异常)

-完-

你可能感兴趣的:(JAVA,代码实践)