首页 > 其他分享 >List集合之元素和对象去重

List集合之元素和对象去重

时间:2023-04-04 12:12:34浏览次数:34  
标签:Night 元素 List Morning l1 集合 new Midday

目录

1 List元素去重

1.1 移除List中指定某一元素

1.1.1 For循环移除

1.1.1.1 For移除不彻底问题

假如去除List中的Morning元素

 @Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
       for (int i = 0; i < l1.size(); i++) {
            if("Morning".equals(l1.get(i))){
                l1.remove(i);
            }
        }
        System.out.println(l1);
    }
执行结果:
[Midday, Evening, Night, Morning, Midday]

查看执行结果发现还有元素Morning没有去掉

1.1.1.2 用 i-- 解决问题

产生的原因就是:ArrayList 是一个数组元素的集合当删掉第一个元素Morning后,集合后面的元素会往前移动,但是此时 i 又指向下一个元素
解决办法:在 list.remove(i)i--
修改后:

 @Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
       for (int i = 0; i < l1.size(); i++) {
            if("Morning".equals(l1.get(i))){
                l1.remove(i);
                i--;
            }
        }
        System.out.println(l1);
    }
执行结果:
[Midday, Evening, Night, Midday]

1.1.1.3 倒序遍历移除元素

 @Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
       for (int i = l1.size()-1; i >=0 ; i--) {
            if("Morning".equals(l1.get(i))){
                l1.remove(i);
            }
        }
        System.out.println(l1);
    }
执行结果:
[Midday, Evening, Night, Midday]

1.1.2 ForEach移除

1.1.2.1 ConcurrentModificationException异常

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
       for (String str:l1) {
            if("Morning".equals(str:l1)){
                l1.remove(i);
            }
        }
        System.out.println(l1);
    }
执行结果:
[Midday, Evening, Night, Midday]

会抛出 ConcurrentModificationException 异常
ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
原因:ArraayList 迭代器有个Itr,其内部有个属性expectedModCount。而集合有个fail-fast 快速失败检测机制,当进行remove()操作时,会比对expectedModCount是否与modCount相等,而前者一般不会改变,但 remove 操作会导致 modCount 发生改变。一旦两者不等,就会抛出ConcurrentModificationException异常。

解决办法:使用迭代器remove()方法

1.1.2.2 iterator遍历

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        Iterator<String> iterator = l1.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if("Morning".equals(next)){
                iterator.remove();
            }
        }
        System.out.println(l1);
    }
执行结果:
[Midday, Evening, Night, Midday]

1.2 移除List中重复元素

1.2.1 ForEach添加去重

这个是创建一个空的List,添加前判断下存在与否,不存在才添加,这样就抱着了元素不重复

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
         List<String> l2 = new ArrayList<>();
        for(String str:l1){
            if(!l2.contains(str)){
                l2.add(str);
            }
        }
        System.out.println(l2);
    }  
执行结果:
[Midday, Evening, Night, Midday]

1.2.2 For双循环去重

从头和尾一起遍历,判断是否有相等,再移除

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        for (int i = 0; i < l1.size()-1; i++) {
            for(int j=l1.size()-1;j>i;j--){
                if(l1.get(j).equals(l1.get(i))){
                    l1.remove(j);
                    System.out.println("i=:"+i+",j=:"+j+l1);
                }
            }
        }
        
        System.out.println(l1);
    }
执行结果:
i=:0,j=:6[Morning, Midday, Evening, Night, Morning, Morning, Midday]
i=:0,j=:5[Morning, Midday, Evening, Night, Morning, Midday]
i=:0,j=:4[Morning, Midday, Evening, Night, Midday]
i=:1,j=:4[Morning, Midday, Evening, Night]
[Morning, Midday, Evening, Night]   

1.2.3 ForEach循环重复坐标去重

这个方式需要先复制出一个List2,再循环遍历List2,判断List中的元素首尾出现的坐标位置是否一致,若一致,则说明没有重复的,否则重复,并移除重复位置的元素

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        List<String> l2= new ArrayList<>(strings);
        for(String s2:l2){
            if(l1.indexOf(s2)!=l1.lastIndexOf(s2)){
                l1.remove(l1.lastIndexOf(s2));
            }
        }     
        System.out.println(l1);
    }
执行结果:
[Morning, Midday, Evening, Night]   

1.2.4 Set去重

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        List<String> l2= new ArrayList<>(new HashSet(l1)); 
        System.out.println(l2);
    }
执行结果:
[Evening, Night, Morning, Midday]

这个方式去重重复元素最简单,但是不能保证顺序,可以把HashSet替换成LinkedHashSet,就可以保证原来的顺序了

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        List<String> l2= new ArrayList<>(new LinkedHashSet(l1)); 
        System.out.println(l2);
    }
执行结果:
[Morning, Midday, Evening, Night]

1.2.5 Stream去重

@Test
    public void testRemoveDuplicate(){
        List<String> strings = Arrays.asList("Morning", "Midday", "Evening", "Night","Morning","Morning","Morning","Midday");
        List<String> l1= new ArrayList<>(strings);
        List<String> collect = l1.stream().distinct().collect(Collectors.toList());
        System.out.println(collect);
    }
执行结果:
[Morning, Midday, Evening, Night]

2 List对象去重

2.1 使用的实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@String
public class User(){
	private String userId;
	private String userName;
	private Integer userAge;
}

2.2 利用Collectors.toMap去重

2.2.1 toMap去重说明

List<User> userList = new ArrayList<>();
        userList.add(new User("a", "xiaoming",12));
        userList.add(new User("b", "xiaoming",13));
        userList.add(new User("d", "xiaoming",15));
        userList.add(new User("a", "xiaoming",14));
       
        System.out.println("利用Collectors.toMap去重:");
        //利用Collectors.toMap去重
        userList.stream()
        		//或者这样写 Collectors.toMap(m -> m.getUserId(), 
                .collect(Collectors.toMap(User::getUserId, 
                			Function.identity(), (oldValue, newValue) -> oldValue))
                .values()
                .stream()
                .forEach(System.out::println); //打印

输出结果:

[{"userAge":12,"userId":"a","userName":"xiaoming"},
{"userAge":13,"userId":"b","userName":"xiaoming"},
{"userAge":15,"userId":"d","userName":"xiaoming"}]

其中,Collectors.toMap需要使用三个参数的版本,前两个参数一个是keyMapper函数一个是valueMapper函数的,用过toMap的都知道,去重的关键在于第三个参数BinaryOperator函数接口。

这个BinaryOperator函数接收两个参数,如上面代码,一个oldValue,一个newValue,字面意思,第一个旧值,第二个是新值。当stream构造Map时,会先调用Map的get方法获取该key对应节点的旧值,如果该值为null,则不会调用BinaryOperator函数,如果不为null,则会把获取的旧值与新值作为参数传给函数执行,然后把函数的返回值作为新值put到Map中。如果看不懂,请看源码吧

2.2.2 Funcion.identity()解释

Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法,identity()就是Function接口的一个静态方法
Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t形式的Lambda表达式。
identity() 方法JDK源码如下:

static  Function identity() {
    return t -> t;
}

下面的代码中,Task::getTitle需要一个task并产生一个仅有一个标题的key
task -> task是一个用来返回自己的lambda表达式,上例中返回一个task

private static Map<String, Task> taskMap(List<Task> tasks) {
  return tasks.stream().collect(toMap(Task::getTitle, task -> task));
}

可以使用Function接口中的默认方法identity来让上面的代码代码变得更简洁明了、传递开发者意图时更加直接,下面是采用identity函数的代码。

import static java.util.function.Function.identity;
private static Map<String, Task> taskMap(List<Task> tasks) {
  return tasks.stream().collect(toMap(Task::getTitle, identity()));
}

2.3 利用Collectors.toCollection和TreeSet去重

List<User> userList = new ArrayList<>();
        userList.add(new User("a", "xiaoming",12));
        userList.add(new User("b", "xiaoming",13));
        userList.add(new User("d", "xiaoming",15));
        userList.add(new User("a", "xiaoming",14));
       
        System.out.println("利用Collectors.toMap去重:");
        //利用Collectors.toMap去重
        userList.stream()
                .collect(Collectors.toCollection(() ->
                	 new TreeSet<>(Comparator.comparing(User::getUserId))))
                .values()
                .stream()
                .forEach(System.out::println); //打印

输出结果:

[{"userAge":12,"userId":"a","userName":"xiaoming"},
{"userAge":13,"userId":"b","userName":"xiaoming"},
{"userAge":15,"userId":"d","userName":"xiaoming"}]

利用TreeSet原理去重,TreeSet内部使用的是TreeMap,使用指定Comparator比较元素,如果元素相同,则新元素代替旧元素,
TreeMapput方法来放入元素的,有兴趣可以自己找源码
如果不想要返回TreeSet类型,那也可以使用Collectors.collectingAndThen转换成ArrayList,也可以用new ArrayList(set),原理一样,如下:

List<User> distinctList = userList.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() 
                	-> new TreeSet<>(Comparator.comparing(User::getUserId))), ArrayList::new));

注意:如果想根据两个或三个字段去重,可以在上述三个方法中的Comparator.comparing(User::getUserId))修改为Comparator.comparing(u -> u.getUserId() +"#" + u.getUserName() )) 这样就是根据两个字段去重,中间的#作用就是为了增加辨识度,也可以不加这个#,无论多少个字段去重只用在这里用+连接就可以了

标签:Night,元素,List,Morning,l1,集合,new,Midday
From: https://www.cnblogs.com/jingzh/p/17285976.html

相关文章

  • 51nod 1551 集合交易
    1551 集合交易题目来源: CodeForces基准时间限制:1 秒空间限制:131072 KB分值: 320 难度:7级算法题 收藏 关注市场中有n个集合在卖。我们想买到满足以下要求的一些集合,所买到集合的个数要等于所有买到的集合合并后的元素的个数......
  • 阿里巴巴为什么这样强制从List中删除元素
    阿里巴巴为什么这样强制从List中删除元素 还是先举个例子,你侄女对天文知识感兴趣,然后你就用程序写了太阳系九大星系(水星、金星、地球、火星、木星、土星、天王星、海王星、冥王星)的运行轨迹图,然后拿给侄女看。然后她说错了错了,你的知识太旧了,多了一颗星。根据2006年8月24日国......
  • 快慢指针-leetcode27移除元素
    给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。说明:为什么返回数值是整数,但输......
  • java lambda List 查找 anyMatch() allMatch() noneMatch()
    packagelambda.list;importcn.hutool.core.util.ObjectUtil;importlombok.extern.slf4j.Slf4j;importorg.junit.Test;importpojo.Dome;importjava.util.ArrayList;importjava.util.List;/***@Author:xxx*@date2021/5/14**/@Slf4jpublicclassSe......
  • java lambda List 分组 Collectors.groupingBy
    packagelambda.list;importlombok.extern.slf4j.Slf4j;importorg.junit.Test;importpojo.Dome;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;importjava.util.stream.Collectors;/***@Author:xxx*@date2021/5/14**/@Sl......
  • c# list删除元素
    新建一个集合:删除其中一个元素List<String>tempList=newList<string>{"水星","金星","地球","火星","木星","土星","天王星","海王星","冥王星","冥王星"};tempList.Remove("冥王星&qu......
  • 制作图标,设置简单元素
    #1、批量制作数据透视表'''file_path='商品销售表'file_list=os.listdir(file_path)forjinfile_list:ifos.path.splitext(j)[1]=='.xslx':workbook=app.books.open('销售表.xlsx')worksheet=workbook.s......
  • <Android> ListView 列表控件的使用-李国庆-专题视频课程
    ListView列表控件的使用—15573人已学习课程介绍        1,ListView介绍;2,原理讲解;3,简单实现;4,ListView扩展;课程收益    通过学习本课程,具有一定的Android开发技能和知识,熟练掌握这一专题中集成组件与布局属性、实现listview基本用法,及简单扩展。讲师介绍    ......
  • android 解决ScrollView嵌套ListView的问题,不能全屏,全屏不能显示下面控件
    在开发中遇到ScrollView嵌套ListView的问题,最开始发出不能全屏,效果是这样的;但我想要的效果是这样的:下面看一下布局文件:<?xmlversion="1.0"encoding="utf-8"?><ScrollViewxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_p......
  • List接口和常用方法
    P2List接口和常用方法一、List接口基本介绍List接口是Collection接口的子接口List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复List集合类中的每一个元素都有其对应的顺序索引,即支持索引。List容器中都对应一个整数型的序号记载其在容器中的位置,可以根据序......