Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

Java如何对HashMap按值进行排序

HashMap的值是没有顺序的,它是按照key的HashCode来实现的。对于这个无序的HashMap我们要怎么来实现排序呢?(TreeMap类似)

Map<String, Long> map = new HashMap<String, Long>();
map.put("c", 33333L);
map.put("a", 11111L);
map.put("d", 44444L);
map.put("e", 55555L);
map.put("b", 22222L);

//将map.entrySet()转换成list
List<Map.Entry<String, Long>> list = new ArrayList<Map.Entry<String, Long>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Long>>() {
	//降序排序
	@Override
	public int compare(Entry<String, Long> o1, Entry<String, Long> o2) {
		//return o1.getValue().compareTo(o2.getValue());
		return o2.getValue().compareTo(o1.getValue());
	}
});

for (Map.Entry<String, Long> mapping : list) {
	System.out.println(mapping.getKey() + ":" + mapping.getValue());
}

运行结果
e:55555
d:44444
c:33333
b:22222
a:11111

关于 多条件 sorted

错误写法

下面代码实际上是,所有内容根据 A 排序,然后 所有内容根据 B 排序,最后所有内容根据 C 排序,所以这样写 实际上只有最后的根据 C 排序是有效的

List<xxx> sortedResults = results.stream()
					.sorted(Comparator.comparing(xxx::getA, Comparator.nullsLast(String::compareTo)))
					.sorted(Comparator.comparing(xxx::getB, Comparator.nullsLast(String::compareTo)))
					.sorted(Comparator.comparing(xxx::getC))
					.collect(Collectors.toList());

多字段排序

Comparator.comparing,此时会对第一字段进行排序如果第一字段相等,此时进入第二字段的排序Comparator.comparing().thenComparing(),接着,第三个字段···第n个字段均可使用.thenComparing()来实现

value.stream()
     .sorted(Comparator.comparing(TreeNodeAndIndex::getRow)
        	.thenComparing(TreeNodeAndIndex::getNodeVal)
字段排序的自定义排序

value.stream()
     .sorted(Comparator.comparing(TreeNodeAndIndex::getRow)
               .thenComparing(TreeNodeAndIndex::getNodeVal,(x,y)-> y-x))

对map<key,List> 中 list 进行排序

foreach使用时,其得到的参数是形参,并非实参
在这样的一个写法下,TreeMap<Integet,List>中的list并不会被真正的排序,因为操作的并不是其真正的内存地址

treeMap.forEach((key,value)-> value.stream()
           .sorted(Comparator.comparing(TreeNodeAndIndex::getRow)
                   .thenComparing(TreeNodeAndIndex::getNodeVal,(x,y)-> y-x)));

要使得list真正的被排序应该按下面的方式写:

treeMap.forEach((key,value)-> {
            List<TreeNodeAndIndex> sortedList = value.stream()
                    .sorted(Comparator.comparing(TreeNodeAndIndex::getRow)
                            .thenComparingInt(TreeNodeAndIndex::getNodeVal))
                    .collect(Collectors.toList());
            treeMap.put(key,sortedList);
        });

将JSON对象中的某个字段进行分组

{
    "systemid": "123",
    "productid": "123",
    "dataArray": [
        {
            "line_num": 2,
            "subjectcodecv": 1,
            "subjectname": "公司",
            "subjectid": "1001",
            "resource_members_id": "003",
            "resource_members_name": "xx"
        },
        {
            "line_num": 2,
            "subjectcodecv": 2,
            "subjectname": "产品",
            "subjectid": "10",
            "resource_members_id": "002",
            "resource_members_name": "11xx"
        },
        {
            "line_num": 2,
            "subjectcodecv": 3,
            "subjectname": "产品",
            "subjectid": "10",
            "resource_members_id": "001",
            "resource_members_name": "22xx"
        },
        {
            "line_num": 1,
            "subjectcodecv": 1,
            "subjectname": "公司",
            "subjectid": "1001",
            "resource_members_id": "005",
            "resource_members_name": "xx"
        },
        {
            "line_num": 1,
            "subjectcodecv": 2,
            "subjectname": "产品",
            "subjectid": "10",
            "resource_members_id": "004",
            "resource_members_name": "11xx"
        },
        {
            "line_num": 1,
            "subjectcodecv": 3,
            "subjectname": "产品",
            "subjectid": "10",
            "resource_members_id": "003",
            "resource_members_name": "22xx"
        }
    ]
}

将JSON字符串中的dataArray数组中的json对象按line_num字段进行分组,然后将每组中的json对象按照resource_members_id字段进行排序(后续还有对每组排序后的json对象进行业务操作,这里不做)

String jsonStr = "";
JSONObject originalJsonData = JSONObject.parseObject(jsonStr);
JSONArray dataArray = originalJsonData.getJSONArray("dataArray");
List<JSONObject> jsonObjectList = new ArrayList<>();
//将原始数据中的json数组中的json对象存到一个list集合中
for (Object o : dataArray) {
    jsonObjectList.add((JSONObject) o);
}
//对集合中的json对象进行分组
//然后返回一个map集合,key代表组名,value代表该组中的数据
Map<String, List<JSONObject>> groupByLineNumData = jsonObjectList.stream()
        .collect(Collectors.groupingBy(x -> x.getString("line_num")));

过滤

Java集合Stream 出自 Java8。
可谓是加量不加价,丰富了使用场景,还精简了代码。虽然牺牲了一点可读性,但总体来说是很好用的。以下总结其中 filter 的一些用法,整理相关的例子。

简单匹配过滤

public class TestFilter {
    public static void main(String[] args) {

        List<String> list = Arrays.asList("AAB","BBB","ACC");

        System.out.println("过滤相等-------------------------------------------");
        List<String> result1 = list.stream().filter(v -> v.equals("AAB")).collect(Collectors.toList());
        result1.forEach(v-> System.out.println(v));

        System.out.println("过滤 模糊相等(包含)-------------------------------------------");
        List<String> result2 = list.stream().filter(v -> v.contains("B")).collect(Collectors.toList());
        result2.forEach(v-> System.out.println(v));

        System.out.println("多条件 过滤 与:&&; 或||-------------------------------------------");
        List<String> result3 = list.stream().filter(v -> v.contains("A") && v.contains("C")).collect(Collectors.toList());
        result3.forEach(v-> System.out.println(v));
    }
}

在这里插入图片描述

对象匹配过滤

public class TestFilter1 {
    public static void main(String[] args) {

        List<User> userList = new ArrayList<>();
        userList.add(new User("小明", 18));
        userList.add(new User("王姐", 21));

        System.out.println("过滤相等-------------------------------------------");
        List<User> result1 = userList.stream().filter(v -> v.getName().equals("小明")).collect(Collectors.toList());
        result1.forEach(v-> System.out.println(v));
    }
}

在这里插入图片描述

自定义匹配过滤

看看filter接口的源码

public interface Stream<T> extends BaseStream<T, Stream<T>> {

    /**
     * Returns a stream consisting of the elements of this stream that match
     * the given predicate.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                  <a href="package-summary.html#Statelessness">stateless</a>
     *                  predicate to apply to each element to determine if it
     *                  should be included
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);

可以看到,filter接收的参数是Predicate,这是一个布尔接口,用于各种匹配,所以自定义filter实际上我们是你把Predicate的用法丰富起来

public class TestFilter1 {
    public static void main(String[] args) {

        List<User> userList = new ArrayList<>();
        userList.add(new User("小明", 18));
        userList.add(new User("王姐", 21));

        System.out.println("过滤相等-------------------------------------------");
        List<User> result2 = userList.stream().filter(new Predicate<User>() {
            @Override
            public boolean test(User user) {
                if (user.getAge() > 18) {
                    return true;
                }else{
                    return false;
                }
            }
        }).collect(Collectors.toList());
        result2.forEach(v-> System.out.println(v));
    }
}

在这里插入图片描述
但是Predicate只能传入一个参数

从List集合中删除指定元素

public class ListRemove {
   class User {
       public String name;
       public Integer age;
       public String classNo;

       @Override
       public String toString() {
           return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   ", classNo='" + classNo + '\'' +
                   '}';
       }
   }

   public List<User> getUserList(int num) {
       List<User> list = new ArrayList<>();
       for (int i = 0; i < num; i++) {
           User u = new User();
           u.age = i + 10;
           u.name = "a" + i;
           u.classNo = String.valueOf(i);
           list.add(u);
       }
       return list;
   }

   public static void main(String[] args) {
       ListRemove listRemove = new ListRemove();
       //数据生成
       List<User> userList = listRemove.getUserList(5);
       System.out.println("原数据:" + Arrays.toString(userList.toArray()));
       
       //测试过滤掉 name = a2 或者 classNo = 4
       
       //方法一:Iterator删除
       List<User> iteratorList = new ArrayList<>(userList);
       Iterator<User> iterator = iteratorList.iterator();
       while (iterator.hasNext()) {
           User next = iterator.next();
           if ("a2".equals(next.name) || "4".equals(next.classNo)) {
               iterator.remove();
           }
       }
       System.out.println("Iterator删除:" + Arrays.toString(iteratorList.toArray()));

       //方法二:removeIf删除
       List<User> removeIfList = new ArrayList<>(userList);
       removeIfList.removeIf(next -> "a2".equals(next.name) || "4".equals(next.classNo));
       System.out.println("removeIf删除:" + Arrays.toString(removeIfList.toArray()));

       //方法三:使用stream    注意:此方法测试未达到预期结果, 不适用
       List<User> streamList = new ArrayList<>(userList);
       streamList.stream().findFirst().map(e -> {
           if ("a2".equals(e.name) || "4".equals(e.classNo)) {
               streamList.remove(e);
           }
           return e;
       });
       System.out.println("stream删除:" + Arrays.toString(streamList.toArray()));

       //方法四:使用stream+索引   注意:此方法测试未达到预期结果,不适用
       List<User> streamIndexList = new ArrayList<>(userList);
       IntStream.range(0, streamIndexList.size()).filter(i ->
               "a2".equals(streamIndexList.get(i).name) || "4".equals(streamIndexList.get(i).classNo)).
               boxed().findFirst().map(i -> streamIndexList.remove((int) i));
       System.out.println("stream+index删除:" + Arrays.toString(streamIndexList.toArray()));

       //方法三:使用filter
       List<User> filterList = new ArrayList<>(userList);
       filterList = filterList.stream().filter(e -> !"a2".equals(e.name) && !"4".equals(e.classNo)).collect(Collectors.toList());
       System.out.println("filter删除:" + Arrays.toString(filterList.toArray()));

       System.out.println("原数据:" + Arrays.toString(userList.toArray()));
   }
}

去重

去除对象中的部分元素的重复
工作中遇到返回个一个list集合中,存在重复数据的问题,这里使用stream流的衍生功能,去除一个对象中的部分元素的重复如下:

ArrayList<ProductProcessDrawbackDto> collect = records1.stream().collect(Collectors.collectingAndThen(
             Collectors.toCollection(() -> new TreeSet<>(
                     Comparator.comparing(
                             ProductProcessDrawbackDto::getId))), ArrayList::new));

其中records1是处理的对象,改对象的list集合,collect是处理后返回的结果
其中的ProductProcessDrawbackDto是处理的list中每一个对象,id是判断是否重复的条件(去除id相同的重复元素,只保留一条)

多个字段或者多个条件去重

 ArrayList<PatentDto> collect1 = patentDtoList.stream().collect(Collectors.collectingAndThen(
             Collectors.toCollection(() -> new TreeSet<>(
                     Comparator.comparing(p->p.getPatentName() + ";" + p.getLevel()))), ArrayList::new));

其原理利用了以下几点:
1.TreeSet里面不会有重复的元素,所以当把一个List放进TreeSet里面后,会自动去重
2.TreeSet去重也是有条件的,它依靠放入其中的元素的排序规则,所以放入其中的元素要有一个自定义的排序规则

也许有人说遍历,比较,这样自然也可以,但是当元素很多很多时,或者字段非常多时,那比较久非常麻烦了,因为涉及到foreach循环和字段的筛选.
编程不就是为了让一切更简单嘛,所以我们不用那种原始方法,我们用现成的简单方法,jdk给我们提供的TreeSet和Comparator搭配,特别适合做这种"比较"的事.

List<Person> persons = new ArrayList<Person>();
Person p1 = new Person("a",10,100);
Person p2 = new Person("a",10,100);
Person p3 = new Person("b",10,100);
persons.add(p1);
persons.add(p2);
persons.add(p3);

public static ArrayList<Person> removeDuplicated(List<Person> persons){
    //1.创建一个带比较规则的set,这里使用匿名内部类创建了一个比较器
    Set<Person> set = new TreeSet(new Comparator<Person>() {
        public int compare(Person o1, Person o2) {
            int a = o1.getName().compareTo(o2.getName());//比较name的自然顺序,0表示相同
            int b = o1.getAge().compareTo(o2.getAge());//比较age的自然顺序,0表示相同
            if(a==0 && b==0){
                return 0;//如果name和age同时都相同,则返回0,0表示Person对象是相同的
            }else{
                return 1;
            }
        }

    });
    //2.将list放进Set中,自动去重
    set.addAll(persons);
    //3.将去重后的集合set再放进一个新的list中返回
    return new ArrayList<Person>(set);
}
测试:
System.out.println(persons);
ArrayList<Person> newPersons = removeDuplicated(persons);
System.out.println(newPersons);

结果:
  [Person{name='a', age=10, height=100}, Person{name='a', age=10, height=100}, Person{name='b', age=10, height=100}]
  [Person{name='a', age=10, height=100}, Person{name='b', age=10, height=100}]

扩展:
以上是去除list中完全一样的元素,大家有没有发现,Person有三个字段,但我比较的时候却只使用了两个,为什么没使用第三个?
有意思的就在这里,因为你可以随心所欲去决定:只要对象的某些特征相同,我就可以判它们是相同对象

注意点: new Comparator<>()时一定要加泛型

对list中map中某个指定的key的value的去重,得到新的list:去除了重复出现指定key的value值得map

过滤掉电话相同的map

List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
Map<String, Object> mapStr1 = new HashMap<String, Object>();
mapStr1.put("name", "丽丽");
mapStr1.put("sex", "女");
mapStr1.put("age", 22);
mapStr1.put("tel", "110");
Map<String, Object> mapStr2 = new HashMap<String, Object>();
mapStr2.put("name", "丽丽");
mapStr2.put("sex", "女");
mapStr2.put("age", 23);
mapStr2.put("tel", "120");
Map<String, Object> mapStr3 = new HashMap<String, Object>();
mapStr3.put("name", "丽丽");
mapStr3.put("sex", "女");
mapStr3.put("age", 24);
mapStr3.put("tel", "110");
mapList.add(mapStr1);
mapList.add(mapStr2);

mapList.add(mapStr3);

ArrayList<Map<String, Object>> cs = mapList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(new Comparator<Map<String, Object>>() {
       @Override    //重写比较器
       public int compare(Map<String, Object> o1, Map<String, Object> o2) {
           if (o1.get("tel").equals(o2.get("tel"))) {
               return 0;
           }
           return 1;
       }
})), ArrayList::new));

//循环取出结果
cs.forEach(m -> {
   System.out.println("======map========");
   m.keySet().forEach(n -> System.out.println(n + "-->" + m.get(n)));
});

对list中对象T的指定属性的去重,比如唯一ID,tel,获取新的list:去除了重复出现的指定属性值的对象T

要求过滤掉电话相同的user

User u1 = new User(1, "小米1", "001号", "女", 21, "110");
User u2 = new User(2, "小米2", "002号", "男", 11, "120");
User u3 = new User(3, "小米3", "003号", "女", 21, "119");
User u4 = new User(4, "小米4", "004号", "女", 34, "110");

List<User> persons = Arrays.asList(u1, u2, u3, u4);
List<User> unique = persons.stream().collect(Collectors.collectingAndThen(
        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getTel))), ArrayList::new));
//循环取出结果
unique.forEach(p -> System.out.println(p.toString()));

Stream实现List集合分页

private static List<User> getUserPageList(String searchKey,Integer pageIndex,Integer pageSize) {
    List<User> userList = getUsers();
    //先 filter 再分页(.skip(记录条数).limit(每页记录数))
    List<User> users = userList.stream().filter(user -> user.getName().contains(searchKey)).skip((pageIndex-1)*pageSize).limit(pageSize).collect(Collectors.toList());
    return users;
}

private static List<User> getUsers() {
    List<User> userList = new ArrayList<>();
    userList.add(new User(1,1,"北京1"));
    ....
    return userList;
}

转int数组

如果转化的是存Integer类型数据的集合,以下的所有intValue都可以用valueOf代替
但如果是存Long或Double等数据类型的数据须用对应类型的intValue()方法

intValue()java.lang.Number类的方法。Java中所有的数值类都继承它。也就是说 IntegerDoubleLong等都有intValue()方法
//hashset转化为Integer数组
Set<Integer> set = new HashSet<>();
set.toArray(new Integer[set.size()]);

//ArrayList转化为Integer数组
List<Integer> list = new ArrayList<>();
list.toArray(new Integer[list.size()]);


不难发现: set 或 list 转化为 Integer 数组或者 int 数组的方式是一样的


//Integer[] 转换为 int[]
Integer[] arr = new Integer[10];
int[] intArr = Arrays.stream(arr).mapToInt(Integer::intValue).toArray();

//hashset用stream流转化为int数组
Set<Integer> set = new HashSet<>();
int[] arr = set.stream().mapToInt(Integer::intValue).toArray();

//ArrayList用stream流转化为int数组
List<Integer> list = new ArrayList<>();
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();

多个list合并成一个

public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2, 3);
    List<Integer> list2 = Arrays.asList(4, 5, 6);
    List<Integer> list3 = Arrays.asList(7, 8, 9);

    List<Integer> mergedList = Stream.of(list1, list2, list3).flatMap(Collection::stream).collect(Collectors.toList());

    System.out.println(mergedList);// [1, 2, 3, 4, 5, 6, 7, 8, 9]

}
List<String> fileIdList = ListIntegration(businessLicense,agencyDocument,otherDocuments,productsReport,threeYearsAchievements);
       
public List<String> ListIntegration(List<String> ...lists){
      return Arrays.stream(lists).flatMap(List::stream).collect(Collectors.toList());
}

常用

1、一个集合根据主键msisdn去重,得到一个新的集合
List<Card> newList = list.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() ->new TreeSet<>(Comparator.comparing(Card::getMsisdn))), 
                ArrayList::new));

2、获取一个集合中,某个对象指定属性的集合
List<Long> msisdnList = kqCardList.stream().map(KqCard::getMsisdn).collect(Collectors.toList());

 3、将一个long类型的集合转换为String类型的集合
 List<String> msisdnStrList = msisdnList.stream().map(String::valueOf).collect(Collectors.toList());

4、遍历一个集合中,对每个对象的指定属性进行赋值(这里我对每个对象赋值了三个参数,最后一个是使用UUID做主键)
List<CmiDataAmount> cmiDataAmountList = cmiDataAmountList.stream().map(
	cmiDataAmount ->{
			cmiDataAmount.setCreateTime(createTime);
   			cmiDataAmount.setCurrentMonth(currentMonth);
       		cmiDataAmount.setId(UUID.randomUUID().toString());
       }).collect(Collectors.toList());

5、将String类型的字符串(比如用“,”分割)转换为集合
List<String> msisdnsList = Arrays.asList(msisdns.split(",")).stream().
	map(s -> String.format(s.trim())).collect(Collectors.toList());

6、遍历集合,获取类型为A的元素,并放入新集合
List<CmiDataAmount> list = cmiDataAmountList.stream().
	filter(item -> item.getType().equals("A")).
	collect(Collectors.toList());

7、取list集合中的两个元素,转换为一个map        参照 2
Map<Integer, String> collect = list.stream().
	collect(Collectors.toMap(Student::getAge, Student::getName));

8、如何将一个元素(对象、属性等)优雅的转换为一个集合
	List<K> authcChannels = Stream.of(K).collect(Collectors.toList());
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值