精华内容
下载资源
问答
  • 匿名内部类,lambda表达式list.forEach的时候引用变量必须是final修饰的问题。 开发中遇到过以下问题,使用list.forEach循环的时候,不能对外部变量进行操作,而且提示: Variable used in lambda expression should...

    匿名内部类,lambda表达式list.forEach的时候引用变量必须是final修饰的问题。
    开发中遇到过以下问题,使用list.forEach循环的时候,不能对外部变量进行操作,而且提示:
    Variable used in lambda expression should be final or effectively final
    意思是:lambda表达式中使用的变量应该是final或实际上是final
    在这里插入图片描述
    这是因为lambda表达式list.forEach也是属于匿名内部类,匿名内部类实际上也是一个对象,只不过是匿名的,只要是对象就会存放到堆里面,而JVM堆里面的对象是由GC垃圾回收负责回收的,回收的时间不确定,对象的生命周期比方法长,这个时候就要求对象未被回收前,对象里面引用的其他变量也不能消失,否则就会有问题。如果这个对象还在,里面引用的变量却消失了肯定不行,直接定义int i = 0;这个变量是随着方法的结束而消失,加上final修饰后,这个变量就会被jvm放入到常量池中,不会因为方法结束就消失,所以匿名内部类引用外部变量的时候要使用final修饰,来保证自己这个对象没有消失前,所引用的变量还在。

    展开全文
  • Lambda内部类的使用

    2019-02-21 15:42:02
    1.1 匿名内部类 无参函数 jdk8之前 new Thread(new Runnable() { @Override public void run() { System.out.println("jdk8 之前"); } }).start(); 使用lambda new Thread(() -&...

    1.1 匿名内部类

    1. 无参函数

    jdk8之前

    new Thread(new Runnable() {
    	   @Override
    	   public void run() {
    	       System.out.println("jdk8 之前");
    	   }
    }).start();
    

    使用lambda

    new Thread(() -> System.out.println("jdk8 之后")).start();
    
    1. 有参函数

    之前

     List<Integer> list = Arrays.asList(4, 3, 5, 2, 7, 1);
    
      Collections.sort(list, new Comparator<Integer>() {
          @Override
          public int compare(Integer o1, Integer o2) {
              return o1 - o2;
          }
      });
    

    使用lambda 排序

    List<Integer> list = Arrays.asList(4, 3, 5, 2, 7, 1);
    Collections.sort(list, (o1,o2) -> o1 - o2);
    
    1. 自定义函数接口

    定义抽象方法接口

    @FunctionalInterface //校验是否是接口,只能有一个一方法没有被实现
    public interface a<T>{
    	void accept(T t);
    }
    

    实现接口

    ConsumerInterface<String> consumer = s -> System.out.println(s);
    consumer.accept("accept 被实现");
    

    由此看出,lambda表达式可以自动根据参数找寻方法

    1. 编写myForEach方法
    public class MyStream<T> {
    
    
        private List<T> list;
    
        public void myForEach(ConsumerInterface<T> consumer) {
            for (T t : list) {
                consumer.accept(t);
            }
        }
        public MyStream(List<T> list) {
            this.list = list;
        }
        public static void main(String[] args) {
            MyStream<String> stream = new MyStream<String>(Arrays.asList("12","34"));
            stream.myForEach(s -> System.out.println(s));
        }
    }
    
    展开全文
  • Talk is cheap, show me your ... * 在lambda表达式内部,修改lambda表达式外部的局部变量的值 */ private synchronized void updateLocalVariable() { List<String> list = new ArrayList<>(); ...

    Talk is cheap, show me your code!


    看一段代码:

     

    /**
     * 在lambda表达式内部,修改lambda表达式外部的局部变量的值
     */
    private synchronized void updateLocalVariable() {
    
        List<String> list = new ArrayList<>();
        list.add("444");
        list.add("4444");
    
        int count = 0;
        // 这样写会报错。Local variable result defined in an enclosing scope must be final or effectively final
        list.stream().forEach(str -> System.out.println("当前是第" + (count++) + "次循环" + str));
    }

     

    如果你试图在lambda表达式内部修改外部局部变量的值,请注意,这里的2个限定条件:
    1,count在lambda外部。
    2,count是局部变量。

    那么jvm会无情的告诉你:
    Local variable result defined in an enclosing scope must be final or effectively final


    原因其实也很简单,本质上就是因为lambda表达式在方法内部,那么lambda表达式的内存分配就是在栈上。栈内存不存在线程安全问题,因为栈内存存的都是变量的副本。
    对于局部变量count而言,它的生命周期就是所在方法的生命周期。这就决定了count无法被位于同一个栈帧上的lambda修改,因为这种修改毫无意义,
    你无法将你的修改传递出当前栈帧。栈内存不会被共享,也就意味着你没有权利和其他栈帧通信。

    如果非要在lambda内部修改lambda表达式外部的局部变量的值呢?
    有两种方式:使用数组或者把局部变量定义为全局变量。

    这2种方式,其实本质是一样的:内存都分配在堆上。这就决定了,使用这2种方式来修改变量的值,是可行的。

     

    使用数组代码如下:

        /**
         * 使用数组
         */
        private synchronized void updateLocalVariableArray() {
    
            List<String> list = new ArrayList<>();
            list.add("444");
            list.add("4444");
    
            int[] count = {0};
            list.stream().forEach(str -> System.out.println(count[0]++));
        }

     

    使用全局变量也可以,很简单,这里不再列出代码。但是有个问题,全局变量需要考虑线程安全问题。

     

    接下来我们讨论另外一个问题:如何用lambda替换for循环,实现计数功能。
    我要知道当前处理的是第几行,比如每100条记录提交一次什么的,类似这种功能,平时需求还是很常见的。
    代码实例如下:

     

    private void updateLocalVariableForLoop() {
    
        List<String> list = new ArrayList<>();
        list.add("444");
        list.add("4444");
        list.add("090");
        // 传统for循环方式
        int count = 0;
        for (String str : list) {
            System.out.println("当前是第" + (count++) + "次循环" + str);
        }
    }

     

    这里提供3种方式:
    1,内部类方式 参考方法 updateLocalVariableInnerClass
    2,lambda方式 参考方法 updateLocalVariableLambda
    3,外部类方式 参考方法 updateLocalVariableOuterClass

     

    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Stream;
    
    /**
     * 如何用lambda替换for循环,实现计数功能。
     */
    public class LambdaLocalVariableUpdate {
    
        /**
         * 全局变量
         */
        private static int GLOBAL_COUNT = 0;
    
        public static void main(String[] args) {
    
            LambdaLocalVariableUpdate lambdaLocalVariableUpdate = new LambdaLocalVariableUpdate();
    //        lambdaLocalVariableUpdate.updateLocalVariableInnerClass(); // 通过对象来实现计数,绕开本地变量,但是存在 线程安全问题
    //        lambdaLocalVariableUpdate.updateLocalVariableLambda(); // 通过对象来实现计数,绕开本地变量,但是存在 线程安全问题
    //        System.out.println(Thread.currentThread().getName() + "-lambda外部变量GLOBAL_INT = " + GLOBAL_INT);
            // 启动多线程,测试是否存在线程安全问题。
            for (int i = 0; i < 30; i++) {
                Thread thread = new Thread(() -> lambdaLocalVariableUpdate.updateLocalVariableInnerClass());
                thread.setName("线程-" + (i + 1));
                thread.start();
            }
        }
    
    
        /**
         * 使用lambda方式
         * 不存在线程安全问题
         */
        private void updateLocalVariableLambda() {
    
            List<String> list = new ArrayList<>();
            list.add("444");
            list.add("4444");
            list.add("090");
    
            Stream.iterate(0, i -> i + 1).limit(list.size()).forEach(index -> {
                String str = list.get(index);
                System.out.println("当前是第" + index + "次循环" + str);
            });
        }
    
        /**
         * 内部类方式,不存在线程安全问题,但是不易扩展。
         *
         */
        private void updateLocalVariableInnerClass() {
    
            CountInner count = new CountInner();
            List<String> list = new ArrayList<>();
            list.add("444");
            list.add("4444");
            list.add("090");
    
            list.stream().forEach(str -> System.out.println("当前是第" + count.count() + "次循环" + str));
        }
    
        /**
         * 外部类方式,不存在线程安全问题,但是变量要定义为非静态,如果定义为静态,则存在线程安全问题。
         *
         */
        private void updateLocalVariableOuterClass() {
    
            CountOuter count = new CountOuter();
            List<String> list = new ArrayList<>();
            list.add("444");
            list.add("444");
            list.add("555");
    
            list.stream().forEach(str -> System.out.println("当前是第" + count.count() + "次循环" + str));
        }
    
        /**
         * 内部类
         */
        class CountInner {
    
            private int i = 0;
            public Integer count() {
    
                return i++;
            }
        }
    
    }
    
    class CountOuter {
    
        private int i = 0;
        public Integer count() {
    
            return i++;
        }
    }

     

     

    最后,要特别说明一下,如果你要修改的是某个对象的值,比如List集合,那是完全没问题的。

     

    /**
     * 在lambda内部修改List集合
     */
    private void updateLocalVariableCollection() {
    
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(100);
    
        List<Integer> list2 = new ArrayList<>(list.size());
    
        list.stream().forEach(intVal -> {
            if (intVal > 0) {
                // 在lambda内部,修改list2
                list2.add(intVal);
            }
        });
    }

     

    展开全文
  • JAVA8 - LambdaList排序

    千次阅读 2019-03-12 14:31:44
    使用LambdaList排序 使用JAVA8函数式方式的比较器 (Human h1, Human h2) -> h1.getName().compareTo(h2.getName()) 下面是使用JAVA8函数式的比较的例子 @Test public void testSortByName_with_...

    先定义一个实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Human {
    
        private String name;
        private int age;
        
    }
    

    下面的操作都基于这个类来进行操作。这里面使用了Lombok类库,它用注解的方式实现了基本的get和set等方法,让代码看起来更加的优雅。

    JAVA8之前的List排序操作

    在Java8之前,对集合排序只能创建一个匿名内部类

    new Comparator<Human>() {
        @Override
        public int compare(Human h1, Human h2) {
            return h1.getName().compareTo(h2.getName());
        }
    }
    

    下面是简单的对Humans进行排序(按名称正序)

    @Test
    public void testSortByName_with_plain_java() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
    
       Collections.sort(humans, new Comparator<Human>() {
             
           public int compare(Human h1, Human h2) {
               return h1.getName().compareTo(h2.getName());
           }
       });
    
       Assert.assertThat(humans.get(0), equalTo(new Human("li", 25)));
    }
    

    使用Lambda的List排序

    使用JAVA8函数式方式的比较器

    (Human h1, Human h2) -> h1.getName().compareTo(h2.getName())
    

    下面是使用JAVA8函数式的比较的例子

    @Test
    public void testSortByName_with_lambda() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
       humans.sort((Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));
    
       Assert.assertThat("tomy", equalTo(humans.get(1).getName()));
    
    }
    
    

    没有类型定义的排序

    对于上面的表达式还可以进行简化,JAVA编译器可以根据上下文推测出排序的类型:

    (h1, h2) -> h1.getName().compareTo(h2.getName())
    

    简化后的比较器是这样的:

    @Test
    public void testSortByNameSimplify_with_lambda() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
       humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
    
       Assert.assertThat("tomy", equalTo(humans.get(1).getName()));
    
    }   
    
    

    使用静态方法引用

    JAVA8还可以提供使用Lambda表达式的静态类型引用,我们在Human类增加一个静态比较方法,如下:

    public static int compareByNameThenAge(Human h1, Human h2) {
    
       if (h1.getName().equals(h2.getName())) {
           return Integer.compare(h1.getAge(), h2.getAge());
       }
       return h1.getName().compareTo(h2.getName());
    }
    
    

    然后就可以在humans.sort使用这个引用

    @Test
    public void testSort_with_givenMethodDefinition() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
       humans.sort(Human::compareByNameThenAge);
       Assert.assertThat("tomy", is(equalTo(humans.get(1).getName())));
    }
    
    

    使用单独的Comparator

    JAVA8已经提供了很多方便的比较器供我们使用,比如Comparator.comparing方法,所以可以使用Comparator.comparing方法来实现根据Human的name进行比较的操作:

    @Test
    public void testSort_with_givenInstanceMethod() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
    
       Collections.sort(humans, Comparator.comparing(Human::getName));
       Assert.assertThat("tomy", equalTo(humans.get(1).getName()));
    }
    
    

    反序

    JDK8中也提供了一个支持倒序排序的方法方便我们更快的进行倒序

    @Test
    public void testSort_with_comparatorReverse() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
    
       Comparator<Human> comparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
       humans.sort(comparator.reversed());
       Assert.assertThat("tomy", equalTo(humans.get(0).getName()));
    
    }
    
    

    使用多个条件进行排序

    Lambda提供了更复杂的表达式,还可以先对name排序再根据age进行排序:

    @Test
    public void testSort_with_multipleComparator() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("li", 25)
       );
    
       Comparator<Human> comparator = (h1, h2) -> {
    
           if (h1.getName().equals(h2.getName())) {
               return Integer.compare(h1.getAge(), h2.getAge());
           }
           return h1.getName().compareTo(h2.getName());
       };
       humans.sort(comparator.reversed());
       Assert.assertThat("tomy", equalTo(humans.get(0).getName()));
    
    }
    

    使用多个条件进行排序-组合的方式

    Comparator对这种组合的排序有更优雅实现,从JDK8开始,我们可以使用链式操作进行复合操作来构建更复杂的逻辑:

    @Test
    public void testSort_with_multipleComparator_composition() throws Exception {
    
       ArrayList<Human> humans = Lists.newArrayList(
               new Human("tomy", 22),
               new Human("tomy", 25)
       );
    
       humans.sort(Comparator.comparing(Human::getName).thenComparing(Human::getAge));
       Assert.assertThat(humans.get(0), equalTo(new Human("tomy", 22)));
    
    }
    

    总结

    JDK8真的是一个非常值得我们学习的版本,它提供了Lambda表达式,带来了函数式编程的理念,让JAVA代码更优雅。

    展开全文
  • 实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。具体原理放到下一篇。本篇我们首先感受一下使用Lambda表达式带来的便利之处。 取代某些匿名内部类 本节将...
  • lambda

    千次阅读 2019-09-19 18:55:40
    package lambda; /** lambda表达式 JDK8之后推出的新特性 lambda旨在让我们可以"以函数式编程"....使用lambda创建的匿名内部类实现的接口只能有一个抽象方法 @author ChenYi @date 2019.09.19 / public class Lam...
  • 二、Lambda表达式 Lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。 Lambda表达式允许你通过表达式来代替功能接口(接口的实现放在Lambda表达式中,可以取代匿名类),Lambda表达式就和方法一样,它...
  • 一、Lambda 表达式 Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。 Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。 使用 Lambda 表达式可以使代码变的更加简洁紧凑。 ...
  • 实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。具体原理放到下一篇。本篇我们首先感受一下使用Lambda表达式带来的便利之处。 取代某些匿名内部类 本节将...
  • Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。 Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。 使用 Lambda 表达式可以使代码变的更加简洁紧凑。 知名大神博客,可以...
  • Lambda表达式中将List中对象的某个对象的属性转化为List 今天在开发中遇到了一个需求,需要将返回值的某个属性单独捞出来返回成List对象,再进行判断。 1、lambda的foreach用法: //将panelSnList的进行for循环,...
  • Java8 Lambda表达式教程

    万次阅读 多人点赞 2013-10-16 16:08:07
    Java8新特性,Lambda表达式与集合类bulk operation教程。迄今为止最全面的中文原创java lambda表达式教程。
  • Lambda

    2020-01-08 15:30:13
    其中,lambda是Python预留的关键字,argument_list和expression由用户自定义。具体介绍如下。 1. 这里的argument_list是参数列表,它的结构与Python中函数(function)的参数列表是一样的。具体来说,...
  • Java Lambda表达式入门

    万次阅读 多人点赞 2014-04-27 21:17:58
    原文链接:Start Using Java Lambda Expressions 下载示例程序 Examples.zip 。 原文日期: 2014年4月16日 翻译日期: 2014年4月27日 翻译人员: 铁锚 简介 (译者认为: 超过3行的逻辑就不适用Lambda表达式了。虽然看着...
  • lambda可以用更简短的方式创建匿名内部类 该语法使得我们可以以“函数式编程” 只是编译器认可,编译器会为我们编译为匿名内部类 注意:lambda创建匿名内部类时,实现的接口必须只能有一个抽象方法,否则不可以使用 ...
  • 实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。具体原理放到下一篇。本篇我们首先感受一下使用Lambda表达式带来的便利之处。 取代某...
  • List分组、List转Map、String转List、mapKey转list、循环map、过滤Filter、List转String 1、分组 List里面的对象元素,以某个属性来分组,将属性相同的放在一起: //List 以名称分组 Map<String, List<...
  • 匿名内部类 前提 存在一个类或者接口,这里的类可以是具体类也可以是抽象类; 格式 格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 } new Inter(){ ...Lambda表达式 前提 使用Lambd
  • list、数组使用Comparator(比较器,一般jdk内部的类有比较的都实现了此接口,比如Long、Integer、List等)排序 使用例子如下: package lambda; import com.google.common.collect.Lists; import java.util....
  • Lambda Lambda表达式支持将代码块作为方法参数, 允许使用更简洁的代码来创建只有一个抽象方法的接口的实例. Lambda的三部分: >形参列表....Lambda表达式和匿名内部类主要存在以下区别: >匿名内部
  • 前言 最近项目上,进行数据采集,通过采集的时间,获取结束时间,时间间隔为1分钟,获取的数据放到list集合里面,开始时间是当参数传入,如果数组中两个数据存在间隔大于1,证明是下一个采集任务的开始,所以,通过...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,993
精华内容 19,197
关键字:

lambda内部list