关于jdk8相关(stream、lambda)

本文深入探讨了Java 8的Stream API和Lambda表达式,包括findAny()、findFirst()的区别,reduce方法的使用,如何使用Lambda进行数据过滤和排序,以及Stream与Optional的结合。文章还介绍了如何对Map进行排序,处理数值运算,并提供了模糊查询的示例。通过这些内容,读者可以更好地理解和运用Java 8的新特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:对于简单的数字(list-Int)遍历,普通for循环效率的确比Stream串行流执行效率高(1.5-2.5倍)。但是Stream流可以利用并行执行的方式发挥CPU的多核优势,因此并行流计算执行效率高于for循环。
对于list-Object类型的数据遍历,普通for循环和Stream串行流比也没有任何优势可言,更不用提Stream并行流计算。
虽然在不同的场景、不同的数据结构、不同的硬件环境下。Stream流与for循环性能测试结果差异较大,甚至发生逆转。但是总体上而言:

Stream并行流计算 >> 普通for循环 ~= Stream串行流计算 (之所以用两个大于号,你细品)
数据容量越大,Stream流的执行效率越高。
Stream并行流计算通常能够比较好的利用CPU的多核优势。CPU核心越多,Stream并行流计算效率越高。

https://blog.youkuaiyun.com/weixin_37948888/article/details/96995312在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

findany() 和 findfirst()

2.使用Stream.findAny()
顾名思义,findAny()方法允许您从Stream中查找任何元素。 在寻找元素时使用时不会关注元素顺序。

该方法返回一个Optional实例,如果Stream为空,则该实例为空:

  @Test
    public void createStream_whenFindAnyResultIsPresent_thenCorrect() {
        List<String> list = Arrays.asList("A","B","C","D");

        Optional<String> result = list.stream().findAny();

        assertTrue(result.isPresent());
        assertThat(result.get(), anyOf(is("A"), is("B"), is("C"), is("D")));
    }

在非并行操作中,它很可能返回Stream中的第一个元素,但不保证这一点。

为了在处理并行操作时获得最佳性能,也无法返回确定结果:

 @Test
    public void createParallelStream_whenFindAnyResultIsNotFirst_thenCorrect() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> result = list
                .stream().parallel()
                .filter(num -> num < 4).findAny();

        assertTrue(result.isPresent());
        assertThat(result.get(), anyOf(is(1), is(2), is(3)));
    }
  1. 使用Stream.findFirst()
    findFirst()方法查找Stream中的第一个元素。 当您特别需要序列中的第一个元素时,可以使用此方法。

某些流的返回的元素是有确定顺序的,我们称之为 encounter order。这个顺序是流提供它的元素的顺序,比如数组的encounter order是它的元素的排序顺序,List是它的迭代顺序(iteration order),

但是对于HashSet,它本身就没有encounter order。

所以如果流没有encounter order的时候,它返回Stream中的任何元素。

java.util.streams包文档:

Streams may or may not have a defined encounter order. It depends on the source and the intermediate operations.

一个流是否是encounter order主要依赖数据源和它的中间操作。

返回类型也是一个Optional实例,如果Stream也为空,则该实例为空:

  @Test
    public void createStream_whenFindFirstResultIsPresent_thenCorrect() {

        List<String> list = Arrays.asList("A", "B", "C", "D");

        Optional<String> result = list.stream().findFirst();

        assertTrue(result.isPresent());
        assertThat(result.get(), is("A"));
    }

findFirst方法的行为在并行方案中也不会更改。 如果是encounter order,则它将始终具有确定性。

findAny()方法返回Stream中的任何元素,而findFirst()方法返回Stream中的第一个元素。

reduce

这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于

Integer sum = integers.reduce(0, (a, b) -> a+b); 或

Integer sum = integers.reduce(0, Integer::sum);

也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。

清单 15. reduce 的用例
// 字符串连接,concat = “ABCD”
String concat = Stream.of(“A”, “B”, “C”, “D”).reduce("", String::concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = “ace”
concat = Stream.of(“a”, “B”, “c”, “D”, “e”, “F”).
filter(x -> x.compareTo(“Z”) > 0).
reduce("", String::concat);

显示更多
上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别。

使用lambda表达式对List<Map>、List<Model>的过滤

1.对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>中对象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()));

3.从stuList中过滤出符合条件的结果。

	public static List<Student> stuList() {
	List<Student> stuList = new ArrayList<Student>();
	Student stu1 = new Student();
	stu1.setStuNum(20180823);
	stu1.setName("张三");
	stu1.setAge(20);
	stuList.add(stu1);
 
	Student stu2 = new Student();
	stu2.setStuNum(20180824);
	stu2.setName("李四");
	stu2.setAge(21);
	stuList.add(stu2);
 
	Student stu3 = new Student();
	stu3.setStuNum(20180825);
	stu3.setName("王二");
	stu3.setAge(21);
	stuList.add(stu3);
 
	Student stu4 = new Student();
	stu4.setStuNum(20180826);
	stu4.setName("张五");
	stu4.setAge(22);
	stuList.add(stu4);
	return stuList;
}

public static void main(String[] args) {
	List<Student> stuList = stuList();
 
	// 需要筛选的条件:从stuList中筛选出年龄为21和22的学生
	List<Integer> ageList = new ArrayList<Integer>();
	ageList.add(21);
	ageList.add(22);
 
	// JDK1.8提供了lambda表达式, 可以从stuList中过滤出符合条件的结果。
	// 定义结果集
	List<Student> result = null;
	result = stuList.stream()
			.filter((Student s) -> ageList.contains(s.getAge()))
			.collect(Collectors.toList());
 

lambda优雅取出对象list中某个属性重复的集合数据

public class Test {
    public static  List<Dog> dogs = null;
    static {
            dogs = new ArrayList<Dog>(){
            {
                add(new Dog("黄一",11));
                add(new Dog("黄一",22));
                add(new Dog("黄三",33));
            }
        };

    }
    public static void main(String[] args) {

        List<String> list =dogs.stream().
                collect(Collectors.groupingBy(dog->dog.getName(),Collectors.counting()))
                .entrySet().stream()
                .filter(entry->entry.getValue()>1)
                .map(entry->entry.getKey())
                .collect(Collectors.toList());
        System.out.println(list.toString());

tips:在调用 Collectors.groupingBy(Function<? super T, ? extends K> classifier) 方法时,实际上调用的是
Collectors.groupingBy(Function<? super T, ? extends K> classifier, HashMap::new, Collectors.toList()) 方法,
中间的参数 HashMap::new 指定返回的是HashMap类型,而HashMap集合底层是哈希表,所以是无序集合,也就是存取顺序不同。

解决方法:
Map接口提供了有序的实现类LinkedHashMap,也就是链表+哈希表的结构来实现有序。
所以在之后使用groupingBy方法来实现List和Map的转换时,可以直接使用:
Collectors.groupingBy(Function<? super T, ? extends K> classifier, LinkedHashMap::new, Collectors.toList()) 方法。

Stream API 与 Optional配合使用

两者配合使用进行stream提供了更加优雅的方法:

利用Stream API 中的 filter将队列中的空对象过滤掉,filter(Objects::nonNull)的意思是,list中的每个元素执行Objects的nonNull()方法,返回false的元素被过滤掉,保留返回true的元素。

 public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person());
        personList.add(null);
        personList.add(new Person("小明",10));
        personList.add(new Person("小红",12));

        personList.stream().filter(Objects::nonNull).forEach(person->{
            System.out.println(person.getName());
            System.out.println(person.getAge());
        });
    }

lambda排序

对map的排序

map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等。其中这四者的区别如下(简单介绍):

   HashMap:我们最常用的Map,它根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度。HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null。非同步的。

  TreeMap: 能够把它保存的记录根据key排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。

  Hashtable: 与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。

  LinkedHashMap: 保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。

hashmap针对map的key进行排序:

map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(x -> resultMap.put(x.getKey(),(x.getValue())));

comparingByKey():根据key排序.

comparingByValue():根据value排序
自定义比较器:

 Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
            //升序排序
            public int compare(Entry<String, String> o1,
                    Entry<String, String> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
            
        });
        
        for(Map.Entry<String,String> mapping:list){ 
               System.out.println(mapping.getKey()+":"+mapping.getValue()); 
          } 
     }

TreeMap根据key进行排序:
默认是升序的,如果我们需要改变排序方式,则需要使用比较器:Comparator。
Comparator可以对集合对象或者数组进行排序的比较器接口,实现该接口的public compare(T o1,To2)方法即可实现排序,该方法主要是根据第一个参数o1,小于、等于或者大于o2分别返回负整数、0或者正整数。如下:

public class TreeMapTest {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<String, String>(
                new Comparator<String>() {
                    public int compare(String obj1, String obj2) {
                        // 降序排序
                        return obj2.compareTo(obj1);
                    }
                });
        map.put("c", "ccccc");
        map.put("a", "aaaaa");
        map.put("b", "bbbbb");
        map.put("d", "ddddd");
        
        Set<String> keySet = map.keySet();
        Iterator<String> iter = keySet.iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            System.out.println(key + ":" + map.get(key));
        }
    }
}
  运行结果如下:

  d:ddddd 
  c:ccccc 
  b:bbbbb 
  a:aaaaa

根据TreeMap的value来进行排序:
对value排序需要借助于Collections的sort(List list, Comparator<? super T> c)方法,该方法根据指定比较器产生的顺序对指定列表进行排序。但是有一个前提条件,那就是所有的元素都必须能够根据所提供的比较器来进行比较。如下:

public class TreeMapTest {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<String, String>();
        map.put("d", "ddddd");
        map.put("b", "bbbbb");
        map.put("a", "aaaaa");
        map.put("c", "ccccc");
        
        //这里将map.entrySet()转换成list
        List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
        //然后通过比较器来实现排序
        Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
            //升序排序
            public int compare(Entry<String, String> o1,
                    Entry<String, String> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }            
        });
        
        for(Map.Entry<String,String> mapping:list){ 
               System.out.println(mapping.getKey()+":"+mapping.getValue()); 
          } 
    }
}
  运行结果

  a:aaaaa 
  b:bbbbb 
  c:ccccc 
  d:ddddd

对list排序

对num进行排序:

List<TestBean> listNew = list.stream().filter(testBean -> testBean != null && testBean.getSortNum() != null)
        .sorted(Comparator.comparing(TestBean::getSortNum)).collect(Collectors.toList());

这种排序一旦有sortNum为空的对象,就会抛出空指针异常。

如果确实要排序带有空值的对象、并不想把这些对象剔除、又不想有NPE怎么办呢?

1)正序,且空值排前面

list.sort(Comparator.comparing(TestBean::getSortNum, Comparator.nullsFirst(Comparator.naturalOrder())));

2)正序,且空值排后面

list.sort(Comparator.comparing(TestBean::getSortNum, Comparator.nullsLast(Comparator.naturalOrder())));

3)倒序,且空值排前面,并取前两个元素

list.sort(Comparator.comparing(TestBean::getSortNum, Comparator.nullsFirst(Comparator.reverseOrder()))).limit(2);

4)倒序,且空值排后面

list.sort(Comparator.comparing(TestBean::getSortNum, Comparator.nullsLast(Comparator.reverseOrder())));

5)多条件排序

//先配置再排序

Comparator<Person> ageComparator = (o1, o2)->o1.getName().compareTo(o2.getName());
    persons.sort(ageComparator);                //按上面配置的顺序取值
    System.out.println("Name升序排序后:");
    persons.forEach((person)->System.out.println(person));
 
    System.out.println("Name降序排序后:");
    persons.sort(ageComparator.reversed());     //按上面配置的顺序反向取值
    persons.forEach((person)->System.out.println(person));
 
 
    // 多条件排序第二个写法,先按Age排序,再根据name排序
    persons.sort(Comparator.comparing(Person::getAge).thenComparing(Person::getName));
    System.out.println("多条件排序后:");
    persons.sort(ageComparator.reversed());     //按上面配置的顺序反向取值
    persons.forEach((person)->System.out.println(person));
 

6)中文排序

 Collections.sort(persons, (Persono1, Persono2) -> Collator.getInstance(Locale.CHINESE).compare(o1.getName(), o2.getName()));

(7)自定义复杂排序
https://www.jb51.net/article/197158.htm

数值运算

BigDecimal:

BigDecimal result2 = userList.stream()
            // 将user对象的age取出来map为Bigdecimal
            .map(User::getAge)
            // 使用reduce()聚合函数,实现累加器
            .reduce(BigDecimal.ZERO,BigDecimal::add);

reduce是一个终结操作,它能够通过某一个方法,对元素进行削减操作。该操作的结果会放在一个Optional变量里返回。可以利用它来实现很多聚合方法比如count,max,min等。
T reduce(T identity, BinaryOperator accumulator);
第一个参数是我们给出的初值,第二个参数是累加器,可以自己用实现接口完成想要的操作,这里使用Bigdecimal的add方法 最后reduce会返回计算后的结果

模糊查询用以筛选list中数据

 List<IOTDeviceStatus>  list = returnList.stream().filter(d -> d.getName().contains(iotMindDevice.getDeviceName())).collect(Collectors.toList());

anyMatch\allMatch\noneMatch

boolean anyMatch(Predicate<? super T> predicate)
只要有一个条件满足即返回true

boolean allMatch(Predicate<? super T> predicate)
必须全部都满足才会返回true

boolean noneMatch(Predicate<? super T> predicate)
全都不满足才会返回true

anyMatch(谓词谓词)返回此流的任何元素是否与提供的谓词匹配。如果不一定要确定结果,则可能不会评估所有元素上的谓词。这是短路端子操作。如果出现无限输入时,端子操作可能会在有限时间内终止,则该端子操作会发生短路
对于 allMatch 和 noneMatch 方法,“流为空将返回 true 且不再评估谓词”,因此两种方法中的谓词可以是任何值。而对于 anyMatch 方法,流为空将返回 false,这可能导致错误诊断异常困难,所以应用时务须谨慎。

Safes.of()

optional的返回形式 为空返回空集合

Map

修改map中的value结构

原理:Collectors.toMap的两个参数的方法,第一个参数代表如何获取key,第二个代表如何获取value,因为key没有变,所以直接取entry的key,value的话可根据需要进行转换。

Map<String, Java> collect = map.entrySet().stream()
                .collect(Collectors.toMap(
                        stringListEntry -> stringListEntry.getKey(), 
                        stringListEntry -> new Java(stringListEntry.getValue())));

map过滤

/**
 * 统计map中包含某一类值的个数,如果是以前的话,这里的代码应该会多好几行
 * contains:只要对应的String中包含,返回值就是true,否则false
 * */
private static long mapTest(Map<String,String> map,String contains){
    long count = map.entrySet().stream().filter(
            entry -> (entry.getValue().contains(contains))
    ).count();
    System.out.println(count);
    return count;
}
/**
 * 过滤掉map中包含指定的value,然后返回过滤之后的map
 * */
private static  Map<String,String> filterMapValue(Map<String,String> map,String contains){
    if(map == null || map.isEmpty()){
        return null;
    }
    return map.entrySet().stream().filter(entry -> (
            ! entry.getValue().contains(contains)
    )).collect(Collectors.toMap(
            entry1 -> entry1.getKey(),
            entry2 -> entry2.getValue()
    ));
}
/**
 * 使用并行流过滤掉map中包含指定的value,然后返回过滤之后的map
 * todo 测试发现 parallelStream在数据大时速度明显优于stream
 * todo 代码简便,速度又快,为什么不用呢。。。。。
 * */
private static  Map<String,String> filterMapValue1(Map<String,String> map,String contains){
    if(map == null || map.isEmpty()){
        return null;
    }
    return map.entrySet().parallelStream().filter(entry ->
           ! entry.getValue().contains(contains)
    ).collect(Collectors.toMap(
            entry1 -> entry1.getKey(),
            entry2 -> entry2.getValue()
    ));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值