List集合你怎么去重?4年开发还在用双重循环,其实一行代码也行

[啤酒]满怀忧思,不如先干再说!做干净纯粹的技术分享!欢迎评论区或私信交流!

List集合特点是允许存在重复元素,在开发中从数据库获取批量数据时往往就是用List接收,去重就变成了一个重要的操作。

无论是老开发还是新手,对于去重的写法五花八门,身边的4年前端用JS去重时使用双重for循环,我当场直接笑趴,直呼高手,现已号称自己是实习生[狗头]!

去重是一个基本操作,并不会刻意思考,抱着能用就行的心态,往往写出不尽人意的代码,本章说一下List去重和多个List去除相同数据也就是【求差集】的实现方式!有复杂的也有一行代码搞定的,随君挑选

去重方式

  • 单循环,新建集合去重,源集合不受影响
  • 双循环,在源集合基础上去重
  • 单循环坐标判断去重
  • set去重
  • stream去重
  • stream求两个list差集返回正确推荐信息

有如下集合

// 1、创建原始集合
List<String> list = new ArrayList<>();
list.add("山兔");
list.add("阿米娅");
list.add("小团团");
list.add("原神派蒙");
list.add("阿米娅");
list.add("原神可莉");
list.add("小团团");

循环去重

此方案是新建集合,循环原集合,如果当前元素不存在于新集合中,则添加元素到新集合中,最后的新集合就是需要的

// 2、创建新集合存储去重后数据
List<Object> finallList = new ArrayList<>();
// 3、循环遍历元素,如果不在新集合中就新增
for (String item : list) {
    // 判断
    if(!finallList.contains(item)) {
        finallList.add(item);
    }
}
System.out.println("==================原集合==================");
System.out.println(list);

System.out.println("==================去重后集合==================");
System.out.println(finallList);

输出结果

==================原集合==================
[山兔, 阿米娅, 小团团, 原神派蒙, 阿米娅, 原神可莉, 小团团]
==================去重后集合==================
[山兔, 阿米娅, 小团团, 原神派蒙, 原神可莉]

双重循环去重

这种方式不需要创建新集合,使用双重循环判断删除重复元素

  • 外循环从前往后遍历,逐个取出元素
  • 内循环从后往前遍历,循环到外循环当前元素处即可,之前的元素就不需要再做判断了【也可以从前往后遍历,只是已经比较过的区间还会再比较一次】
// 2、双重循环对比,删除已存在数据
for (int i = 0; i < list.size() - 1; i++) {
    // 内循环从后到外循环起始位置
    for (int j = list.size() - 1; j > i; j--) {
        // 内外循环当前元素相同就删除内循环的元素,因为它在后边,保障列表前半部分数据不重复
        if(list.get(j).equals(list.get(i))) {
            // 删除
            list.remove(j);
        }
    }
}
System.out.println("==================去重后集合==================");
System.out.println(list);
  • 此时不能使用asList生成集合,因为asList生成的集合是不可变的,不能做删除元素和新增元素操作
  • 直接在源集合上做变更操作,如果不希望修改源集合则不推荐此用法

for循环重复坐标去重

根据元素从前获取首次出现的坐标和从后获取首次出现的坐标,如果坐标值不同,则认为重复,移除后边的元素,保障集合前半部分数据不重复

// 2、复制一个集合
List<String> copyList = new ArrayList<>(list);
for (String item : list) {
    // 判断首次出现的坐标和从后往前出现的坐标是否一致
    if(copyList.indexOf(item) != copyList.lastIndexOf(item)) {
        // 根据索引删除数据
        copyList.remove(copyList.lastIndexOf(item));
    }
}
System.out.println("==================去重后集合==================");
System.out.println(copyList);

注意:一定要再复制一个集合,因为在使用for循环迭代集合时,不可以对集合做修改操作,否则会出现ConcurrentModificationException也就是并发修改异常,解决方案有几种,在接下来的文章中发出

Set 去重

Set集合自带去重,但是需要对象重写hashCodeequals方法判断是否为同一个对象,例子中使用的String类已经实现了hashCode和equals方法,下边会再使用自定义的对象

Set<String> set = new HashSet<>(list);
List<String> finalList = new ArrayList<>(set);

System.out.println("==================去重后集合==================");
System.out.println(finalList);

使用Set就会变得无序,即元素的添加顺序和存储顺序不一致

==================原集合==================
[山兔, 阿米娅, 小团团, 原神派蒙, 阿米娅, 原神可莉, 小团团]
==================去重后集合==================
[阿米娅, 原神派蒙, 山兔, 小团团, 原神可莉]

如果想保障有序,则可以使用LinkedHashSet

Set<String> set = new LinkedHashSet<>(list);
List<String> finalList = new ArrayList<>(set);

System.out.println("==================去重后集合==================");
System.out.println(finalList);

自定义对象使用Set去重

自定义对象:重写hashCodeequals方法,当身份证号码一样时认为是同一个对象

import java.io.Serializable;
import java.util.Objects;

public class Driver implements Serializable {

    private long id;
    // 姓名
    private String name;
    // 身份证号码
    private String idNumber;

    public Driver(long id, String name, String idNumber) {
        this.id = id;
        this.name = name;
        this.idNumber = idNumber;
    }
    // 省略getter、setter
    @Override
    public boolean equals(Object o) {
        // 如果引用相等,则是同一个对象
        if (this == o) return true;
        // 如果为null,不是同一个类就直接返回false
        if (o == null || getClass() != o.getClass()) return false;
        Driver driver = (Driver) o;
        // 根据身份证号码判断,如果相同就是同一个司机
        return Objects.equals(idNumber, driver.idNumber);
    }

    /**
     * 通过身份证号码计算哈希值
     * @return
     */
    @Override
    public int hashCode() {
        return Objects.hash(idNumber);
    }
    // toString方法
}

去除重复数据

// 1、创建原始集合
List<Driver> list = new ArrayList<>();
list.add(new Driver(1L,"山兔","411481XXX"));
list.add(new Driver(2L,"阿米娅","412481XXX"));
list.add(new Driver(3L,"小团团","413481XXX"));
list.add(new Driver(4L,"原神派蒙","414481XXX"));
list.add(new Driver(5L,"阿米娅","412481XXX"));
list.add(new Driver(6L,"原神可莉","416481XXX"));
list.add(new Driver(7L,"小团团","413481XXX"));
System.out.println("==================原集合==================");
System.out.println(list);

Set<Driver> set = new HashSet<>(list);
List<Driver> finalList = new ArrayList<>(set);

System.out.println("==================去重后集合==================");
System.out.println(finalList);

Stream去重

JDK8之后可以使用stream的distinct方法去重,非常清爽,前提是对象需要重写hashCode和equals方法,确定判重机制

List<Driver> list = new ArrayList<>();
list.add(new Driver(1L,"山兔","411481XXX"));
list.add(new Driver(2L,"阿米娅","412481XXX"));
list.add(new Driver(3L,"小团团","413481XXX"));
list.add(new Driver(4L,"原神派蒙","414481XXX"));
list.add(new Driver(5L,"阿米娅","412481XXX"));
list.add(new Driver(6L,"原神可莉","416481XXX"));
list.add(new Driver(7L,"小团团","413481XXX"));
// 去重
List<String> finalList = list.stream().distinct().collect(Collectors.toList());

两个List获取差集

有以下场景,比如推荐的书单中有你不感兴趣的类型,则该类型的书就不会推荐给你,实现思路:

  • 从数据库中查询到一堆数据【这里直接使用List初始化数据】
  • 从数据库查询到你不感兴趣的类型【这也是用List初始化】
  • 判断查出来的书的类型如果存在于不感兴趣列表则过滤掉

书类

import java.io.Serializable;

public class Book implements Serializable {

    private long id;
    private String name;
    private String type;

    public Book(long id, String name, String type) {
        this.id = id;
        this.name = name;
        this.type = type;
    }

    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;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

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

测试类

import java.util.*;
import java.util.stream.Collectors;

public class ArrayTest {
    public static void main(String[] args) {
        // 1、创建原始集合
        List<Book> list = new ArrayList<>();
        list.add(new Book(1L,"我与地坛","文学"));
        list.add(new Book(2L,"生死疲劳","文学"));
        list.add(new Book(3L,"一地鸡毛","文学"));
        list.add(new Book(4L,"明朝那些事儿","历史"));
        list.add(new Book(5L,"长安的荔枝","历史"));
        list.add(new Book(6L,"高效能人士的七个习惯","成功学"));
        list.add(new Book(7L,"励志生存","成功学"));

        // 2、不感兴趣的类型
        List<String> indiffTypeList = Arrays.asList("成功学","心理");

        // 3、使用stream的filter过滤数据
       //  通过contains方法判断该类型的书是否存在与不感兴趣列表中,【取反】即返回有用的数据
        List<Book> resultList = list.stream().filter(item -> !indiffTypeList.contains(item.getType())).collect(Collectors.toList());
        for (Book book : resultList) {
            System.out.println(book);
        }
    }
}

输出结果:已经排除掉不感兴趣的类型

Book{id=1, name='我与地坛', type='文学'}
Book{id=2, name='生死疲劳', type='文学'}
Book{id=3, name='一地鸡毛', type='文学'}
Book{id=4, name='明朝那些事儿', type='历史'}
Book{id=5, name='长安的荔枝', type='历史'}


这就是List去重的几种方案和项目开发时会用到的数据过滤,多多练习解锁更多玩法,赶紧使用起来吧,如有任何问题欢迎评论区交流

举报
评论 0