List.stream 和 自定义lamabda表达式 和 putIfAbsent()、computeIfAbsent()


多字段分组

Map<String,Map<String, List<CampaignSendMessageListForFile>>> map = uploadSuccessList.stream().collect(Collectors.groupingBy( CampaignSendMessageListForFile::getChannel,Collectors.groupingBy(CampaignSendMessageListForFile::getContent) )); 


.skip()和.limit()


List的强转

List<Date> dateList = (List<Date>)(List)strings;
List<Date> dates = strings.stream()
        .map(x->(Date)x)
        .collect(Collectors.toList());
//Arrays.asList()方法返回的List不能add对象
//receiverList.toArray(new String[receiverList.size()])

.filter 过滤

List<String> tableFieldList = priorityModelList.stream()
        .filter(s -> s.getRuleTableName().toLowerCase().equals("person"))
        .map(a -> a.getRuleTableField()).collect(Collectors.toList());

.distinct()去重

1.简单包装类型去重:

List<ImportMemberExcel> tid_Msid_LidList = excelUtilMember.userModelMapper.getToupointidMembershipidLoyaltyprogramidByLoginUserName(loginUserName);
  List<Long> tidList = tid_Msid_LidList.stream().map(e -> e.getTouchPointID()).distinct().collect(Collectors.toList());
  List<Long> msidList = tid_Msid_LidList.stream().map(e -> e.getMembershipID()).distinct().collect(Collectors.toList());
  List<Long> lidList = tid_Msid_LidList.stream().map(e -> e.getLoyaltyProgramID()).distinct().collect(Collectors.toList());

 2.根据对象内某字段去重--Map.putIfAbsent()

List列表运用Java8的stream流按某字段去重 - cdfive - 博客园

通过stream的filter方法来去重,不定义去重方法,在外面创建HashMap

代码:

Map<Object, Boolean> map = new HashMap<>();
List<Book> distinctNameBooks4 = books.stream().filter(e -> map.putIfAbsent(e.getName(), Boolean.TRUE) == null).collect(Collectors.toList());
System.out.println(distinctNameBooks4);

总结:
仍然是配合filter方法实现去重,没有单独创建方法,临时定义一个HashMap,保持了原列表的顺序,不足之处是有一定内存占用。

putIfAbsent() 方法会先判断指定的键(key)是否存在,不存在则将键/值对插入到 HashMap 中。 putIfAbsent() 方法的语法为:hashmap.putIfAbsent(K key, V value)

如果所指定的 key 已经在 HashMap 中存在,返回和这个 key 值对应的 value, 如果所指定的 key 不在 HashMap 中存在则将键/值对插入到 HashMap 中,并返回 null

注意:如果指定 key已经和一个 null的值相关联存在于map中 ,则该方法也返回 null。

3. Map.computeIfAbsent()用法

该方法为java8 新增。

//Map接口中此方法源码:
public interface Map<K,V> {
//......

   default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        //第二个参数为null会抛出空指针异常
        Objects.requireNonNull(mappingFunction);
        V v;
        //map中不存在这个key,或者存在这个key它的值为null
        if ((v = get(key)) == null) {
            V newValue;
            //把1参key传入2参映射函数处理,返回值newValue的类型为map的value的类型V
            //映射函数执行结果newValue!=null时,才把key-newValue放入map且return newValue,【此时map.computeIfAbsent(key,mappingFunction)相当于把key传入mappingFunction中执行相关逻辑返回newValue(给key准备的非null值),put(key, newValue);return newValue;两/三步】。
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }
        //map中存在这个key且其值不为null,v = get(key)返回原value(不会把第一个参数交给第二个参数),【此时map.computeIfAbsent(key,mappingFunction)相当于map.get(key)】。
        //map中不存在这个key,或者存在这个key它的值为null ==> 把1参key交给2参映射函数处理,映射函数执行结果newValue==null ==> v = get(key)即return null,【此时map.computeIfAbsent(key,mappingFunction)也相当于map.get(key)】。
        return v;
    }

//......
}


//不管if判断是否为真,其对值的改变都是真实不虚的(不会回退!)
String a = "hello";
if((a = a.substring(0,2)) == null){
}
System.out.println(a);//he

对上面源码中自添加注释的总结:

map中存在这个key且其值不为null,v = get(key)返回原value(不为null)(不会把第一个参数交给第二个参数),【此时map.computeIfAbsent(key,mappingFunction)相当于map.get(key)】。


map中不存在这个key,或者存在这个key它的值为null ==> 把1参key传入2参映射函数处理,映射函数执行结果newValue==null ==> v = get(key)即return null(相当于return newValue),【此时map.computeIfAbsent(key,mappingFunction)也相当于map.get(key)】。

map中不存在这个key,或者存在这个key它的值为null ==> 把1参key传入2参映射函数处理,映射函数执行结果newValue!=null把key-newValue放入mapreturn newValue

只要映射函数符合执行条件且执行完毕,computeIfAbsent方法返回值就是第二个参数--映射函数的执行结果,

1.此时computeIfAbsent方法返回值为null/映射函数的执行结果为null则说明computeIfAbsent方法未对map进行任何修改,

2.此时computeIfAbsent方法返回值不为null/映射函数的执行结果不为null则说明computeIfAbsent方法对map进行过put操作。

映射函数不符合执行条件,则computeIfAbsent方法返回值是map中此键的值且值不为null,且computeIfAbsent方法未对map进行任何修改。

如果映射函数符合执行条件但执行过程中抛出异常,将不添加任何键值对到map,也不会return任何值。

只知道computeIfAbsent方法返回值不为null,并不能直接推出computeIfAbsent方法返回值来自原值还是映射函数计算出来的新值。​​​​​​

有下面一则程序片段:

//映射函数是key -> new ArrayList<>()时,demoValue永不为null,不会出现空指针

List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
//只知道computeIfAbsent方法返回值不为null,并不能直接推出computeIfAbsent方法返回值来自原值还是映射函数计算出来的新值(原值可能也是无元素的非null集合)。

//不管demoValue来自原值【通过源码中get(key)得到--此时demoValue中或许已经有值

】,还是映射函数计算出来的新值【通过源码中put(key, newValue);return newValue;得到--此时demoValue中无元素,因为映射函数是key -> new ArrayList<>()】,但只要demoValue是对象类型,下面两行add操作都会修改map中"demoKey"键的值因为demoValue和map中"demoKey"键的值是有同一内存地址的同一对象请看下面示例3
demoValue .add(true);
demoValue .add(false);

示例1:

Map<String, List<Boolean>> map = new HashMap<>();

//接收的类型必须是map的值类型即List<Boolean>
List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> {
  /*map中不存在这个key或者存在这个key它的值为null,才会进入此方法!!
  不进入此方法computeIfAbsent将直接返回原key的键值给demoValue*/
  List<Boolean> newValueList = new ArrayList<>();
  //方法体内可以拿到key
  System.out.println(key);//"demoKey"
  System.out.println("原key值:" + map.get(key));//必然打印:原key值:null
  newValueList.add(false);
  newValueList.add(true);
  newValueList.add(true);
  newValueList.add(false);
  System.out.println("新key值:" + newValueList);
  //return的类型必须是map的值类型即List<Boolean>,return的newValueList!=null时会在源码中间接被demoValue接收
  //newValueList!=null时的map.put(key,newValueList)操作也是在底层源码执行的!
  return newValueList;
  //return的newValueList==null时,底层源码将不执行map.put(key,newValueList)操作,且return null给demoValue,所以可以理解为demoValue接收的是newValueList(因为newValueList也是null)
});

上面写法和下面写法逻辑一致!推荐下面写法

List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
//映射函数是key -> new ArrayList<>(),则demoValue永不为null。
//映射函数符合执行条件的话,demoValue接收的是new ArrayList<>(),map放的是("demoKey",demoValue),则下面几行修改了map中此键的值
//映射函数不符合执行条件即在computeIfAbsent执行前map的key中已经有"demoKey"且键值不为null,不进行put操作仅返回map中此键的值,则下面几行也会修改到map中此键的值!
demoValue.add(false);
demoValue.add(true);
demoValue.add(false);
demoValue.add(true); 

第二种写法要注意避免空指针(第一种写法也要避免空指针):
List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> null);
demoValue.add(false);//此处会抛出空指针异常,demoValue接收的是null
demoValue.add(true);
demoValue.add(false);
demoValue.add(true);
下面这样写则是在computeIfAbsent方法内部抛出的空指针异常:
List<Boolean> demoValue = map.computeIfAbsent("demoKey", null);

示例2: 

  【"demoKey"键不存在】
  public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();

    /*List list = new ArrayList();
    list.add(true);
    map.put("demoKey", list);*/

    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
  }
//打印结果:
//0
//{demoKey=[false, true, false, true]}
//总结:走映射函数


  【"demoKey"键存在但值为null】
  public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();
    map.put("demoKey", null);

    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
  }
//打印结果:
//0
//{demoKey=[false, true, false, true]}
//总结:走映射函数


  【"demoKey"键存在且值不为null且有元素】
  public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();

    List list = new ArrayList();
    list.add(true);
    map.put("demoKey", list);

    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
  }
//打印结果:
//1
//{demoKey=[true, false, true, false, true]}
//总结:打印1说明computeIfAbsent返回原键值且不走映射函数,打印{demoKey=[true, false, true, false, true]}说明后面的add操作是对原键值list的add操作


【"demoKey"键存在且值不为null但无元素】
public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();
    List list = new ArrayList();
    map.put("demoKey", list);//list无元素

    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    System.out.println(list==demoValue);
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
  }
//打印结果:
//true
//0
//{demoKey=[false, true, false, true]}
//总结:为true,说明demoValue来自原键值不走映射函数,且demoValue和原键值是同一对象,后面的add是对原键值的操作

示例3:

源码中get(key)得到一个对象/键值赋给一个引用--变量名【List<Boolean> demoValue =                     get(key);】;

           或者

           put(key, newValue);return newValue;返回一个对象/键值赋给一个引用--变量名                       【List<Boolean> demoValue = return newValue;】;

所以,对此变量demoValue进行的操作就是对key键的值进行的操作

public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();

    List<Boolean> demoValue1 = new ArrayList<>();
    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    demoValue1.addAll(demoValue);
    System.out.println("是否同一对象:"+ (demoValue == map.get("demoKey")));
    System.out.println("是否同一对象1:"+ (demoValue1 == map.get("demoKey")));
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
}
//打印结果:
//是否同一对象:true
//是否同一对象1:false
//0
//{demoKey=[false, true, false, true]}


public static void main(String[] args) {
    Map<String,List<Boolean>> map = new HashMap();
    List list = new ArrayList();
    list.add(true);
    map.put("demoKey", list);

    List<Boolean> demoValue1 = new ArrayList<>();
    List<Boolean> demoValue = map.computeIfAbsent("demoKey", key -> new ArrayList<>());
    demoValue1.addAll(demoValue);
    System.out.println("是否同一对象:"+ ((demoValue == map.get("demoKey")) && (list == map.get("demoKey"))));
    System.out.println("是否同一对象1:"+ (demoValue1 == map.get("demoKey")));
    System.out.println(demoValue.size());
    demoValue.add(false);
    demoValue.add(true);
    demoValue.add(false);
    demoValue.add(true);
    System.out.println(map.toString());
}
//打印结果:
//是否同一对象:true
//是否同一对象1:false
//1
//{demoKey=[true, false, true, false, true]}


 .sorted()排序

Comparator.comparing(TSysRoleAndMenuDTO::getOrderIndex).reversed()是降序

菜单表查询结果的封装 

    //过滤 排序 设置
    List<TSysRoleAndMenuDTO> collect = Lists.newArrayList();
    if (!CollectionUtils.isEmpty(list)) {
      // 过滤得一级菜单,
      collect = list.stream().filter(e -> e.getLevels().equals(1))
              .sorted(Comparator.comparing(TSysRoleAndMenuDTO::getOrderIndex)).collect(Collectors.toList());
      // 遍历一级菜单,
      for (TSysRoleAndMenuDTO tSysRoleAndMenuDTO : collect) {
        if (!CollectionUtils.isEmpty(collect)) {
          // 过滤得二级菜单--查询父级id和一级菜单id相等的集合,
          List<TSysRoleAndMenuDTO> collect2 = list.stream().filter(e -> e.getParentId().equals(tSysRoleAndMenuDTO.getId()))
                  .sorted(Comparator.comparing(TSysRoleAndMenuDTO::getOrderIndex)).collect(Collectors.toList());
          /*// 把二级菜单set到一级菜单实体中。
           * 放上面也可以--【list如果后面变化,前面set进对象属性中的该list也会随之改变!】*/
          tSysRoleAndMenuDTO.setMenuDTOList(collect2);
          if (!CollectionUtils.isEmpty(collect2)) {
            // 遍历二级菜单,
            for (TSysRoleAndMenuDTO sysRoleAndMenuDTO2 : collect2) {
              // 过滤得三级菜单,
              List<TSysRoleAndMenuDTO> collect3 = list.stream().filter(e -> e.getParentId().equals(sysRoleAndMenuDTO2.getId()))
                      .sorted(Comparator.comparing(TSysRoleAndMenuDTO::getOrderIndex)).collect(Collectors.toList());
              // 把三级菜单set到二级菜单实体中。
              sysRoleAndMenuDTO2.setMenuDTOList(collect3);/*此时collect2也会随之更新*/
            }
          }
        }
      }
    }

求和

double sum1pay_amount = tmpList.stream().mapToDouble(e -> e.getPay_amount() == null ? 0 : e.getPay_amount()).sum();//【注意相加的元素不能是null,否则空指针!】


自定义lambda表达式

1.定义一个接口,其中有且只能有一个方法

package com.kaytune.sizzled;

public interface Eatable {//提供一个接口,对于lamabda式其中有且只能有一个方法
    String eat(String s, String b);
}

2.定义一个类,其中某一个公共的静态方法的参数是定义好的接口,相当于设计了一个lamabda表达式

package com.kaytune.sizzled;


public class LamabdaDemo {
    public static String useEatable(Eatable e){
        //【准备通用的接口方法入参】,使用lamabda表达式时只需输入形参即可
        String bbb = "别BB";
        String jbb = "就BB";

        String res = e.eat(bbb, jbb);//【方法体由使用lamabda式的地方定义】

        //【通用后置代码】
        String bbb1 = "别AA";
        String jbb1 = "就AA";
        String h1 = bbb1 + jbb1;

        return res + h1;
    }
}

3.在某一个类中使用lamabda表达式

package com.kaytune.sizzled;

/**
 * @beLongProjecet: theExampleProject
 * @beLongPackage: com.kaytune.sizzled
 * @author: sizzled
 * @createTime: 2022/08/24 11:48
 * @description: 使用定义好的lamabda表达式
 * @version: v1.0
 */
public class UserLamabda {

    public static void main(String[] args) {
        //lamada表达式
        String result = LamabdaDemo.useEatable(/*注意形参不是Eatable e*/ /*String*/ s, /*String*/ b) -> {//使用lamabda表达式只需输入形参(实际上是准备好的实参)

            String jkjkjk = s + b;/*【这里放利用形参(实际上是准备好的实参),做的一些事情】*/

            return jkjkjk;
            //这里并不是return给result,而是LamabdaDemo.useEatable(Eatable e){}中String res = e.eat(bbb, jbb);的res
            //result接收的是LamabdaDemo.useEatable(Eatable e){}的返回值
        });

        System.out.println(result);
        //结果:别BB就BB别AA就AA
    }
}

总计一下自定义lambda表达式的使用场景:共同的入参,不同的中间处理逻辑,共同的后处理逻辑。


Collectors.toMap

1.想获得一个id和name对应的Map<String, String> :

Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getId,Student::getName)); 

2.注意:name可以为空字符串但不能为null,否则会报空指针,解决方案:

Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getId, e->e.getName()==null?"":e.getName()));

3.假如存在id重复,两个vaue可以这样映射到同一个id:

Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getId,Student::getName,(e1,e2)->e1+","+e2));

4.JSONArray类型返回Map


anyMatch()、allMatch()、noneMatch()

// anyMatch():匹配到任何一个元素和指定的元素相等,返回 true
// allMatch():匹配到全部元素和指定的元素相等,返回 true
// noneMatch():与 allMatch() 效果相反


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值