关于List,ArrayList中remove(object obj)方法不能删除对象问题及解决方法

本文深入探讨Java中List集合的remove方法原理,通过具体代码示例解释为何未重写equals方法会导致意料之外的结果。文章还介绍了如何通过重写equals和hashCode方法来确保对象在List中的正确删除与去重。

先看下面代码,大家猜猜打印的结果是什么?为什么?

package shiyan;  

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

public class ListTest {  

    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        List<A> list = new ArrayList<A>();  
        A a1 = newA(1,"a1");  
        A a2 = newA(2,"a2");  
        A a3 = newA(3,"a3");  
        A a4 = newA(4,"a4");  
        A a5 = newA(5,"a5");  

        list.add(a1);  
        list.add(a2);  
        list.add(a3);  
        list.add(a4);  
        list.add(a5);  

        list.remove(a2);  

        System.out.println(list.size());  
    }  

}  

class A{  
    int id;  
    String name;  
    public A(int id,String name){  
        this.id = id;  
        this.name = name;  
    }  

    public boolean equals(Object obj) {  
        return false;  
    }  

}  

如果您的回答是:4,我想您就有必要看看本文了,因为正确答案是5

先贴上:ArrayList上的remove源码:

public boolean remove(Object o) {  
    if(o == null) {  
        for(int index = 0; index < size; index++)  
        if(elementData[index] == null) {  
            fastRemove(index);  
            return true;  
        }  
    }else{  
        for(int index = 0; index < size; index++)  
        if(o.equals(elementData[index])) {  
            fastRemove(index);  
            return true;  
        }  
    }  
    return false;  
}  

看源码可知,List在删除对象时,先判断这个对象是否在自己的队列中?而这种判断指的是是否equals

因此,List在删除对象时,如果使用删除对象方法,应该最好重写equals方法,否则最好使用删除下标的方法。

注:删除下标时一定要确保下标的类型是int类型,若是Integer类型,List会默认匹配remove(Object o)方法,而不是remove(int index)方法。

解决方法:

比如现在有一个 Person类:

public class Person {
    private Long id;

    private String name;

    public Person(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

重写Person对象的equals()方法和hashCode()方法:

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

        Person person = (Person) o;

        if (!id.equals(person.id)) return false;
        return name.equals(person.name);

    }

    @Override
    public int hashCode() {
        int result = id.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }

下面对象去重的代码:

 Person p1 = new Person(1l, "jack");
        Person p2 = new Person(3l, "jack chou");
        Person p3 = new Person(2l, "tom");
        Person p4 = new Person(4l, "hanson");
        Person p5 = new Person(5l, "胶布虫");

        List<Person> persons = Arrays.asList(p1, p2, p3, p4, p5, p5, p1, p2, p2);

        List<Person> personList = new ArrayList<>();
        // 去重
        persons.stream().forEach(
                p -> {
                    if (!personList.contains(p)) {
                        personList.add(p);
                    }
                }
        );
        System.out.println(personList);

List 的contains()方法底层实现使用对象的equals方法去比较的,其实重写equals()就好,但重写了equals最好将hashCode也重写了。

可以参见:https://blog.youkuaiyun.com/xyr05288/article/details/53736378

https://blog.youkuaiyun.com/jiaobuchong/article/details/54412094

 

在 Java 中,将对象转换为 `List<Map<String, Object>>` 格式是常见的需求,通常用于数据转换或序列化场景。以下是 **多种实现方式**,根据不同的场景选择合适的方法: --- ### **1. 手动转换(适用于简单对象)** 如果对象结构简单,可以直接通过反射或手动映射: ```java public static List<Map<String, Object>> convertObjectToListMap(List<?> objects) { List<Map<String, Object>> result = new ArrayList<>(); for (Object obj : objects) { Map<String, Object> map = new HashMap<>(); // 假设对象有 getId() 和 getName() 方法 if (obj instanceof YourClass) { YourClass yourObj = (YourClass) obj; map.put("id", yourObj.getId()); map.put("name", yourObj.getName()); // 添加其他字段... } result.add(map); } return result; } ``` --- ### **2. 使用反射(通用性强)** 通过反射动态获取对象字段和值,适合任意对象: ```java public static List<Map<String, Object>> convertToMapList(List<?> objects) throws IllegalAccessException { List<Map<String, Object>> result = new ArrayList<>(); for (Object obj : objects) { Map<String, Object> map = new HashMap<>(); Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 允许访问私有字段 map.put(field.getName(), field.get(obj)); } result.add(map); } return result; } ``` --- ### **3. 使用 Jackson/Gson(推荐,支持复杂对象)** 通过 JSON 库实现对象到 `Map` 的转换: #### **Jackson 示例** ```java import com.fasterxml.jackson.databind.ObjectMapper; public static List<Map<String, Object>> convertWithJackson(List<?> objects) { ObjectMapper mapper = new ObjectMapper(); return objects.stream() .map(obj -> mapper.convertValue(obj, Map.class)) .collect(Collectors.toList()); } ``` #### **Gson 示例** ```java import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; public static List<Map<String, Object>> convertWithGson(List<?> objects) { Gson gson = new Gson(); String json = gson.toJson(objects); return gson.fromJson(json, new TypeToken<List<Map<String, Object>>>(){}.getType()); } ``` --- ### **4. 使用 Spring 的 `BeanUtils`(适合 Spring 项目)** ```java import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; public static List<Map<String, Object>> convertWithBeanUtils(List<?> objects) { List<Map<String, Object>> result = new ArrayList<>(); for (Object obj : objects) { Map<String, Object> map = new HashMap<>(); BeanWrapper wrapper = new BeanWrapperImpl(obj); for (PropertyDescriptor pd : wrapper.getPropertyDescriptors()) { String key = pd.getName(); if (!"class".equals(key)) { // 跳过 Class 属性 map.put(key, wrapper.getPropertyValue(key)); } } result.add(map); } return result; } ``` --- ### **5. 使用 Apache Commons BeanUtils** ```java import org.apache.commons.beanutils.BeanUtils; public static List<Map<String, Object>> convertWithApacheBeanUtils(List<?> objects) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { List<Map<String, Object>> result = new ArrayList<>(); for (Object obj : objects) { Map<String, Object> map = BeanUtils.describe(obj); // 返回 Map<String, String> map.remove("class"); // 移除 Class 属性 result.add(map); } return result; } ``` --- ### **性能与场景对比** | 方法 | 优点 | 缺点 | 适用场景 | |--------------------|-----------------------------|-----------------------------|----------------------| | **手动转换** | 无依赖,性能高 | 代码冗余,维护成本高 | 简单固定结构对象 | | **反射** | 通用性强 | 性能较低,需处理异常 | 动态字段需求 | | **Jackson/Gson** | 支持复杂嵌套对象,代码简洁 | 需要引入第三方库 | JSON 序列化场景 | | **Spring BeanUtils**| 与 Spring 生态集成 | 仅限 Spring 项目 | Spring 应用 | | **Apache BeanUtils**| 功能全面 | 性能较差,API 老旧 | 遗留系统兼容 | --- ### **最佳实践建议** 1. **简单对象** → 手动转换(性能最优) 2. **动态字段** → 反射或 Jackson 3. **Spring 项目** → 优先用 `BeanUtils` 4. **需要 JSON 兼容** → 直接使用 Jackson/Gson ```java // 示例:用 Jackson 转换(推荐) List<Map<String, Object>> result = objectList.stream() .map(obj -> new ObjectMapper().convertValue(obj, Map.class)) .collect(Collectors.toList()); ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值