part1. jdk8新特性详解

1.Lambda

//1.匿名内部类
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("新线程中执行的代码:"+Thread.currentThread().getName());
				
			}
		}).start();
		System.out.println("主线程中的代码:"+Thread.currentThread().getName());
		

Lambda表达式是一个匿名函数,可以理解为一段代码

new Thread(()->{
			System.out.println("新线程Lambda表达式。。。");
		}).start();

优点:简化了匿名内部类的使用,语法更加简洁;匿名内部类语法冗余;

Lambda语法规则:省去了面向对象的条条框框,Lambda标准格式组成:

(参数类型 参数名称 )->{

代码体;

}

格式说明:

  • (参数类型 参数名称 ):参数列表
  • {代码体;} :方法体
  • -> :箭头,分割参数列表和方法体

1.1 无参,无返回值lambda

定义接口

public interface UserService {
void show();
}

然后创建主方法使用

package com.newfeature;

import com.newfeature.service.UserService;

public class Demo02Lambda {

	public static void main(String[] args) {
		goShow(new UserService() {

			@Override
			public void show() {
				// TODO Auto-generated method stub
				System.out.println("执行show()方法");
			}
		});

		System.out.println("------------------");

		// 无参,无返回值lambda表达式
		goShow(() -> {
			System.out.println("Lamda show方法执行了。。");
		});

	}

	public static void goShow(UserService userService) {
		userService.show();
	}
}

输出

执行show()方法
------------------
Lamda show方法执行了。。

1.2 有参有返回值的lambda表达式

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
	private String name;
    private Integer age;
    private Integer height;
}

list集合保存多个person对象,然后对这些对象做根据age排序操作

package com.newfeature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.newfeature.domain.Person;

public class Demo03Lambda {

	public static void main(String[] args) {
		List<Person> list=new ArrayList<>();
		list.add(new Person("zhouzhou",33,175));
		list.add(new Person("zhouzhou2",23,155));
		list.add(new Person("zhouzhou3",53,125));
		list.add(new Person("zhouzhou4",63,165));
		Collections.sort(list, new Comparator<Person>() {
			@Override
			public int compare(Person o1, Person o2) {
				return o1.getAge()-o2.getAge();
			}
		});
		for (Person person : list) {
			System.out.println(person);
		}
	}

	
}

lambda表达式实现排序

package com.newfeature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.newfeature.domain.Person;

public class Demo03Lambda {

	public static void main(String[] args) {
		List<Person> list=new ArrayList<>();
		list.add(new Person("zhouzhou",33,175));
		list.add(new Person("zhouzhou2",23,155));
		list.add(new Person("zhouzhou3",53,125));
		list.add(new Person("zhouzhou4",63,165));
		

		Collections.sort(list,(Person o1,Person o2)->{
			return o1.getAge() - o2.getAge();
		});
		for (Person person : list) {
			System.out.println(person);
		}
	}

	
}

输出结果:

Person(name=zhouzhou2, age=23, height=155)
Person(name=zhouzhou, age=33, height=175)
Person(name=zhouzhou3, age=53, height=125)
Person(name=zhouzhou4, age=63, height=165)

1.3 @FunctionalInterface 注解

/**
 * @FunctionalInterface
 * 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
 */
@FunctionalInterface
public interface UserService {
void show();
}

1.4 lambda表达式的原理

  • 匿名内部类的本质是在编译时生产一个class文件,xxx$1.class;

  • lambda表达式原理:lambda表达式在程序运行的时候,会形成一个类。
    1.在类中新增一个方法,这个方法的方法体就是lambda表达式中的代码
    2.形成一个匿名内部类,实现接口,重写抽象方法

    3.在接口中重写方法会调用新生成的方法

  • jdk自带工具:javap对字节码进行反编译操作

    javap -c -p 文件名.class 
    
    -c:表示对 代码进行反汇编
    -p:显示所有的类和成员
    

1.5lambda表达式的简写写法

在lambda表达式的标准写法基础上,可以使用简写写法规则为:

  1. 小括号内的参数类型可以省掉
  2. 如果小括号内有且仅有一个参数,则小 括号可以省掉
  3. 如果大括号内有且仅有一个语句,可以省掉大括号、return关键字以及语句分号

定义接口:

public interface StudentService {
String show(String name,Integer age);
}


public interface OrderService {
    Integer show(String name);
}

lambda简写写法:

package com.newfeature;

import com.newfeature.service.OrderService;
import com.newfeature.service.StudentService;

public class Demo04lambda {
    public static void main(String[] args) {
     goStudent((String name,Integer age)->{
         return name+age+"***";
     });
     //简写写法
        goStudent((name,age)-> name+age+"0000");
        System.out.println("--------------");
        goOrder((String name)->{
            System.out.println("----->"+name);
            return 999;
        });

        //简写写法
        goOrder(name->{
            System.out.println("----->"+name);
            return 1999;
        });
    }
    public static  void goStudent(StudentService studentService){
        studentService.show("张三",22);
    }
    public static void goOrder(OrderService orderService){
        orderService.show("李四");
    }
}

1.6 lambda表达式的使用前提

  1. 方法的参数 或局部变量类型必须为接口才能使用
  2. 接口中有且仅有一个抽象方法@FunctionalInterface

1.7 lambda表达式与匿名内部类的对比

  • 所需类型不一样

    1. 匿名内部类的类型可以是类,抽象类,接口

    2.lambda表达式需要的类型必须是接口

  • 抽象方法数量不一样
    1匿名内部类所需的接口 中的抽象方法的数量是随意的
    2 lambda表达式所需的接口中只能有一个抽象方法

  • 实现原理不一样
    1 匿名内部类是 在编译后形成一个class
    2 lambda表达式是在程序运行的时候动态生成class

2. 接口中新增的方法

2.1 jdk8之前,接口定义

interface 接口名{
    静态常量;
    抽象方法;
}

jdk8之后,对接口做了增加,接口可以有默认方法和静态方法

interface 接口名{
    静态常量;
    抽象方法;
    默认方法;
    静态方法;
}

2.2 默认方法

package com.newfeature;

public class InterfaceDemo {
    public static void main(String[] args) {
A a=new B();
a.test3();
A c=new C();
c.test3();

    }
}


class  B implements A{

    @Override
    public void test1() {
        System.out.println("test1方法执行了....");
    }

    @Override
    public void test2() {
        System.out.println("test2中方法执行了...");
    }
    
/**
     * 重写默认方法
     * @return
     */
    
    @Override
    public String test3() {
        System.out.println("在B实现类中重写了默认方法test3");
        return "ok...";
    }
}

class C implements A{

    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }
}
interface A {
    void test1();
    void test2();

    /**
     * 定义接口中默认方法
     * @return
     */
    public default String test3(){
        System.out.println("接口中默认方法执行了....");
        return "hello";
    }
}

接口中默认方法的使用:

  1. 实现类直接调用接口中的默认方法
  2. 实现类重写接口的默认方法

2.3 静态方法

jdk8中为接口新增了静态方法,作用是为了接口的扩展

语法规则

interface 接口名{
    修饰符 static 返回值类型 方法ming{
        方法体;
    }
}
package com.newfeature;

public class InterfaceDemo {
    public static void main(String[] args) {
A a=new B();
a.test3();

A c=new C();
c.test3();

A.test4();

    }
}


class  B implements A{

    @Override
    public void test1() {
        System.out.println("test1方法执行了....");
    }

    @Override
    public void test2() {
        System.out.println("test2中方法执行了...");
    }

    @Override
    public String test3() {
        System.out.println("在B实现类中重写了默认方法test3");
        return "ok...";
    }
}

class C implements A{

    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }
}
interface A {
    void test1();
    void test2();

    /**
     * 定义接口中默认方法
     * @return
     */
    public default String test3(){
        System.out.println("接口中默认方法执行了....");
        return "hello";
    }

    /**
     * 定义接口中静态方法
     * @return
     */
    public static String test4(){
        System.out.println("接口中的静态方法执行了...");
        return "Hello2";
    }
}

接口中的静态方法使用:

接口中的静态方法在实现类中是不能被重写的,只能通过接口类型来实现:接口名.静态方法名称();

在这里插入图片描述

接口中默认方法与静态方法的区别:

1.默认方法通过实例调用,静态方法通过接口名调用

2.默认方法可以被继承,实现类可以直接调用默认方法,也可以重写接口默认方法

3.静态方法不能被继承,实现类不能重写接口的静态方法,只能用接口名调用

3. 函数式接口

3.1 函数式接口的由来

使用lambda表达式的前提是需要函数式接口,而lambda表达式使用时不关心接口的名称、抽象方法名,只关心抽象方法的参数列表和返回值类型。为了使用lambda表达式更方便,jkd提供了大量的函数式接口

package com.newfeature;

public class FuncInterface {
    public static void main(String[] args) {
        fun1((arr)->{
            int sum=0;
            for(int i:arr){
                sum+=i;
            }
            return sum;
        });
    }
    public static void fun1(Operator operator){
        int[] arr={1,2,3,4};
        int sum=operator.getSum(arr);
        System.out.println("sum="+sum);
    }
}

/**
 * 函数式接口
 */
@FunctionalInterface
interface Operator{
    int getSum(int[] arr);
}

3.2 函数式接口介绍

在jdk中提供的函数式接口,主要在java.util.function包中

1. Supplier: 无参有返回值接口,对于lambda表达式需要提供一个返回数据的类型; 用于生产数据
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用:

package com.newfeature;

import java.util.Arrays;
import java.util.function.Supplier;

/**
 * Supplier接口使用
 */
public class SupplierTest {
    private static void fun1(Supplier<Integer> supplier){
        Integer max=supplier.get();
        System.out.println("max="+max);
    }

    public static void main(String[] args) {
        fun1(()->{
            int arr[]={22,33,55,66,34,99};
            //计算数组中最大值
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }
}

2. Consumer :有参无返回值的接口,用于消费数据,使用时候需要指定一个泛型来定义参数类型
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

}

使用:将输入的数据统一转换为小写

package com.newfeature;

import java.util.Locale;
import java.util.function.Consumer;

public class ConsumerTest {
    public static void main(String[] args) {
        test(msg->{
            System.out.println(msg+"->转换为小写:"+msg.toLowerCase());
        });
    }
    public static void test(Consumer<String> consumer){
        consumer.accept("Hello World");
    }
}

默认方法andThen使用:如果一个方法的参数和返回值全部是consumer类型,就可以实现效果,消费一个数据的时候,首先做一个操作,然后 再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法->andThen方法。

 default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
具体操作:
package com.newfeature;

import java.util.function.Consumer;

public class ConsumerTest2 {
    public static void main(String[] args) {
        test2(msg1->{
            System.out.println(msg1+"->转换为小写:"+msg1.toLowerCase());
        },msg2->{
            System.out.println(msg2+"转换为大写:"+msg2.toUpperCase());
        });
    }
    public static void test(Consumer<String> consumer){
        consumer.accept("Hello World");
    }
    public  static void  test2(Consumer<String> c1,Consumer<String> c2){
        String str="Hello World";
       // c1.accept(str);//转小写
        //c2.accept(str);//转大写
        //c1.andThen(c2).accept(str);
        c2.andThen(c1).accept(str);
    }
}

3.Funciton:有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);


}

使用:传递进去一个字符串,返回一个integer

package com;

import java.util.function.Function;

public class FunctionTest {
    public static void main(String[] args) {
        test(msg->{
return Integer.parseInt(msg);
        });
    }
    public static void  test(Function<String,Integer> function){
        Integer apply=function.apply("666");
        System.out.println("apply="+apply);
    }

}

默认方法:andThen,也是进行组合操作

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

组合操作使用:

package com;

import java.util.function.Function;

public class FunctionTest {
    public static void main(String[] args) {
        test(msg -> {
            return Integer.parseInt(msg);
        }, msg2 -> {
            return msg2 * 2;
        });
    }

    public static void test(Function<String, Integer> f1, Function<Integer, Integer> f2) {
//        Integer apply=f1.apply("666");
//        Integer apply2=f2.apply(apply);
        Integer apply2 = f1.andThen(f2).apply("666");
        System.out.println("apply2=" + apply2);
    }

}

默认的compose方法作用顺序和andThen方法刚好相反;

静态方法identity则是,输入什么参数就返回什么参数

  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

static <T> Function<T, T> identity() {
        return t -> t;
    }
4. Predicate:有参且有返回值为boolean型的接口

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);


}

使用:

package com.newfeature;

import java.util.function.Predicate;

public class PredicateTest {
    private  static  void  test(Predicate<String> predicate,String msg){
        boolean b = predicate.test(msg);
        System.out.println("b="+b);
    }

    public static void main(String[] args) {
        test(msg->{
            return msg.length()>3;
        },"a");
    }
}

在Predicate中的默认方法提供了逻辑操作:

  • and
  • or
  • negate:取反
  • isEquals
package com.newfeature;

import java.util.function.Predicate;

public class PredicateTest {
    private  static  void  test(Predicate<String> p1,Predicate<String> p2){
       //p1包含H,p2包含w
        //p1包含H同时p2包含w
       boolean rs1= p1.and(p2).test("Hello");
       //p1包含H或者p2包含w
        boolean rs2=p1.or(p2).test("Hello");
        //p1不包含H
        boolean rs3=p1.negate().test("Hello");
        System.out.println("rs1:"+rs1);//false
        System.out.println("rs2:"+rs2);//true
        System.out.println("rs3:"+rs3);//false
    }

    public static void main(String[] args) {
        test(msg1->{
            return msg1.contains("H");
        },msg2->{
            return msg2.contains("W");
        });
    }
}

4.方法引用

4.1 使用方法引用的原因:

使用lambda表达式的时候,出现代码冗余的情况,eg:用lambda表达式求和

package com.newfeature;

import java.util.function.Consumer;

public class FunctionRefTest {
    private static void printMax(Consumer<int[]> consumer){
    int[] a={10,20,30,50,90};
    consumer.accept(a);
    }

    /**
     * 求数组中值的总和
      * @param a
     */    
public void getTotal(int a[]){
    int sum=0;
    for(int i:a){
        sum+=i;
    }
    System.out.println("数组之和:"+sum);
}
    public static void main(String[] args) {
        //lambda表达式中的代码和getTotal中代码出现冗余了
        printMax(a->{
            int sum=0;
            for(int i:a){
                sum+=i;
            }
            System.out.println("数组之和:"+sum);
        });
    }
}

冗余代码的解决方案:

​ 因为lambda表达式中要执行的代码和另外一个方法中的代码一样,就没必要重写一份代码,就可以使用:引用 重复代码

package com.newfeature;

import java.util.function.Consumer;

public class FunctionRefTest {
    private static void printMax(Consumer<int[]> consumer){
    int[] a={10,20,30,50,90};
    consumer.accept(a);
    }

    /**
     * 求数组中元素的总和
      * @param a
     */
public static void getTotal(int a[]){
    int sum=0;
    for(int i:a){
        sum+=i;
    }
    System.out.println("数组之和:"+sum);
}
    public static void main(String[] args) {
        // :: 方法引用,jdk8的新特性
        printMax(FunctionRefTest::getTotal);
    }
}

:: 方法引用,jdk8的新特性的语法

应用场景:如果Lambda表达式所要实现的方案,已经在其他方法存在相同的方案,那么则可以使用方法引用。

常见的引用方式:

  1. instanceName::methodName 对象::方法名
  2. ClassName::staticMethodName 类名::静态方法
  3. ClassName::methodName 类名::普通方法
  4. ClassName::new 类名::new 调用的构造器
  5. TypeName[]::new String[]::new 调用数组的构造器

4.2 对象名::方法名

package com.newfeature;

import java.util.Date;
import java.util.function.Supplier;

public class FunctionTest03 {
    public static void main(String[] args) {
        Date now = new Date();
        Supplier<Long> supplier = () -> {
            return now.getTime();
        };
        System.out.println(supplier.get());

        //通过引用的方法引用的方式处理
        Supplier<Long> supplier1 = now::getTime;
        System.out.println(supplier1.get());
    }
}

方法引用注意的事项:

  1. 被用户的方法,参数要和接口中的抽象方法的参数一样
  2. 当接口抽象方法有返回值时,被引用的方法必须有返回值

4.3 类名 ::静态方法名

package com.newfeature;

import java.util.function.Supplier;

public class FunctionTest04 {
    public static void main(String[] args) {
        Supplier<Long> supplier1=()->{
            return System.currentTimeMillis();
        };

        System.out.println(supplier1.get());

        //通过 方法引用来实现
        Supplier<Long> supplier2=System::currentTimeMillis;
        System.out.println(supplier2.get());
    }
}

4.4 类名::引用实例方法

java面向对象中,类名只能调用静态方法,类名引用实例方法是使用前提,实际上第一个参数作为方法的调用者

package com.newfeature;


import java.util.function.BiFunction;
import java.util.function.Function;

public class FunctionTest05 {
    public static void main(String[] args) {
        Function<String,Integer> function=(s)->{
            return s.length();
        };
        System.out.println(function.apply("hello"));

        //通过方法引用来实现
        Function<String,Integer> function1=String::length;
        System.out.println(function1.apply("hahah"));

        BiFunction<String,Integer,String> function2=String::substring;
        String msg = function2.apply("helloworld", 3);
        System.out.println(msg);
    }
}

4.5 类名::构造器

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.function.BiFunction;
import java.util.function.Supplier;

public class FunctionTest06 {
    public static void main(String[] args) {
        Supplier<Person> sup=()->{
            return new Person();
        };
        System.out.println(sup.get());
        //通过方法引用来实现
        Supplier<Person> sup1=Person::new;
        System.out.println(sup1.get());

        BiFunction<String,Integer,Person> function=Person::new;
        System.out.println(function.apply("张三",22));
    }
}

4.6 数组::构造器

package com.newfeature;


import java.util.function.Function;

public class FunctionTest07 {
    public static void main(String[] args) {
        Function<Integer, String[]> fun1 = (len) -> {
            return new String[len];
        };

        String[] a1 = fun1.apply(3);
        System.out.println("数值的长度是:" + a1.length);

        Function<Integer, String[]> fun2 = String[]::new;
        String[] a2 = fun2.apply(5);
        System.out.println("数组长度是:" + a2.length);
    }
}

方法引用是lambda表达式符合特定情况下的一种缩写方式,不过方法的引用只能引用已经存在的方法

5. stream API

5.1 集合处理数据的弊端

对集合中元素进行操作的时候,除了必须添加、删除、、获取外,典型的操作就是集合的遍历

package com.newfeature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StreamTest01 {
    public static void main(String[] args) {
        //定义一个集合
        List<String> list= Arrays.asList("张三","张麻子","李四","王五");
        //1.获取所有,姓张的信息
        List<String> list2=new ArrayList<>();
        for(String s:list){
           if(s.startsWith("张")){
               list2.add(s);
           }
        }

        //2.获取名称长度为3的用户
        List<String> list3=new ArrayList<>();
        for(String s:list2){
            if(s.length()==3){
                list3.add(s);
            }
        }

        //3.输出所有的用户信息
        for(String s:list3){
            System.out.println(s);
        }
    }
}

针对上面的需求,总是一次次的遍历循环,希望有高效的方式处理,通过jdk8中stream api优雅处理

package com.newfeature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StreamTest02 {
    public static void main(String[] args) {
        //定义一个集合
        List<String> list = Arrays.asList("张三", "张麻子", "李四", "王五");
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(System.out::println);
//                .forEach(s -> {System.out.println(s);});


    }
}

上面StreamAPI代码含义:获取流,过滤张,过滤长度,遍历打印;代码更简洁

5.2 stream 流式思想

  • Stream和IO流没有任何关系;
  • Stream流式思想类似工厂车间的生产流水线,stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,stream可以看做是流水线上的一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品
    在这里插入图片描述

在这里插入图片描述

Stream API能让我们快速完成复杂的操作,eg:筛选,切片,映射、查找,去除重复、统计、匹配和归约

5.3 stream流的获取方式

  1. 根据Collection获取
    首先,java.uitl.Collection接口中加入了默认的方法stream,也就是Collection接口下所有的实现都 可以通过stream方法来获取stream流

    在这里插入图片描述

package com.newfeature;


import java.util.*;

public class StreamTest03 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        list.stream();
        Set<String> set=new HashSet<>();
        set.stream();

        Vector<String> vector=new Vector<>();
         vector.stream();

    }
}

但是map接口没有实现Collection接口,怎么做呢?可以根据Map获取对应的key value的集合

package com.newfeature;


import java.util.*;
import java.util.stream.Stream;

public class StreamTest04 {
    public static void main(String[] args) {
        Map<String,Object> map=new HashMap<>();
        Stream<String> stream=map.keySet().stream();//key
        Stream<Object> ojb= map.values().stream();//value
        Stream<Map.Entry<String,Object>> stream2=map.entrySet().stream();//entry


    }
}
  1. 通过stream的of方法

    把数组转换成stream,采用of方法

在这里插入图片描述

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest05 {
    public static void main(String[] args) {
        Stream<String> a1= Stream.of("a1","a2","a3");
        String[]  arr1={"aa","bb","cc"};
        Stream<String> arr11=Stream.of(arr1);
        Integer[] arr2={1,2,3};
        Stream<Integer> arr21=Stream.of(arr2);
        arr21.forEach(System.out::println);
        
        //注意:基本的数据类型是不行的,要用包装类型
        int[] arr3={1,2,3};
        Stream.of(arr3).forEach(System.out::println);



    }
}

5.4 stream常用方法介绍

在这里插入图片描述

  • 终结方法:返回值类型不再是stream类型的方法,不再支持链式调用。eg: foreach() 、count()

  • 非终结方法:返回值类型仍然是stream类型的方法,支持链式调用.(除了终结方法,其余方法均为非终结方法)

注意:

  1. strem只能操作一次

  2. stream返回的是最新的流

  3. stream不调用终结方法,中间的操作的不会执行

    package com.newfeature;
    
    
    import java.util.stream.Stream;
    
    public class StreamTest06 {
        public static void main(String[] args) {
            Stream<String> a1 = Stream.of("a1", "a2", "a3");
            a1.filter(s -> {
                System.out.println("------------");
                return s.contains("a");
            }).forEach(System.out::println);
    
            System.out.println("------------->");
    
    
        }
    }
    

5.5 forEach:遍历数据

void forEach(Consumer<? super T> action);

该方法接受一个Consumer接口,会将每一个流元素交给函数处理

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest06 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3").forEach(System.out::println);
    }
}

5.6 count:统计stream流中元素个数的

long count();
package com.newfeature;


import java.util.stream.Stream;

public class StreamTest06 {
    public static void main(String[] args) {
      long count= Stream.of("a1", "a2", "a3").count();
        System.out.println(count);
    }
}

5.7 filter:用来过滤数据,返回符合条件的数据

在这里插入图片描述

可以通过filter方法将一个流转换为另一个子集流

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate函数式接口参数,作为筛选条件
package com.newfeature;


import java.util.stream.Stream;

public class StreamTest07 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
              .filter((s)->s.contains("a")).forEach(System.out::println);
    }
}

输出:

a1
a2
a3

5.8 limit:对流进行截取处理,只取前n个数据

在这里插入图片描述

Stream<T> limit(long maxSize);

参数是一个long类型数值,如果集合当前长度大于参数就进行截取,否则不操作

使用:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest08 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
                     .limit(3).forEach(System.out::println);
    }
}

输出:

a1
a2
a3

5.9 skip

在这里插入图片描述

如果想跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:

Stream<T> skip(long n);

操作:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest09 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
                     .skip(3).forEach(System.out::println);
    }
}

输出:

bb
dd

5.10 map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

在这里插入图片描述

该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一个R类型数据

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest10 {
    public static void main(String[] args) {
             Stream.of("1", "2", "3","4","5")
//                     .map(msg->Integer.parseInt(msg))
                     .map(Integer::parseInt)
                     .forEach(System.out::println);
    }
}

5.11 sorted:对数据进行排序

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

在使用时候,可以根据自然规则排序,也可以通过比较器指定对应的排序规则

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .forEach(System.out::println);
    }
}

5.12 distinct:去掉重复数据

Stream<T> distinct();

在这里插入图片描述

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1","3", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .distinct()  //去除重复记录
                     .forEach(System.out::println);
    }
}

Stream流中的distinct方法对于基本数据类型是可以直接去重的,但对自定义类型,需要重写hashcode & equals方法来移除重复元素

package com.newfeature.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Objects;

//@Data
//@AllArgsConstructor
//@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }


    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) && Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    // private Integer height;
}


package com.newfeature;


import com.newfeature.domain.Person;

import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1","3", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .distinct()  //去除重复记录
                     .forEach(System.out::println);
        System.out.println("------------------------");
        Stream.of(new Person("张三",18),
                new Person("李四",33),
                new Person("张三",18)
                ).distinct()
                .forEach(System.out::println);
    }
}

5.13 match

5.14 find

5.15 max&min

Optional<T> max(Comparator<? super T> comparator);

 Optional<T> min(Comparator<? super T> comparator);

max使用:

package com.newfeature;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Optional<Integer>  max = Stream.of(4, 5, 3, 9)
                        .max((o1,o2)->Integer.compare(o1,o2));
        System.out.println(max.get());

    }
}

min使用:

package com.newfeature;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Optional<Integer>  min = Stream.of(4, 5, 3, 9)
                        .min((o1,o2)->Integer.compare(o1,o2));
        System.out.println(min.get());

    }
}

5.16 reduce方法

在这里插入图片描述

如果需要将所有数据归纳得到一个数据,使用reduce方法

T reduce(T identity, BinaryOperator<T> accumulator);

使用:

package com.newfeature;

import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Integer sum = Stream.of(4, 5, 3, 9)
                .reduce(0, (x, y) -> {
                    //identity默认值
                    //第一次的时候会将默认值赋值给x
                    //之后再次会将上一次的操作结果赋值给x,y是每次从数据中获取的元素
                    System.out.println("x=" + x + ",y=" + y);
                    return x + y;
                });
        System.out.println(sum);
        //获取最大值、
        Integer max = Stream.of(4, 5, 3, 9)
                .reduce(0, (x, y) -> {
                    return x > y ? x : y;
                });
        System.out.println(max);

    }
}
  • map和reduce的组合:实际开发中经常会map与reduce一块使用

  • package com.newfeature;
    
    
    import com.newfeature.domain.Person;
    
    import java.util.stream.Stream;
    
    public class StreamTest13 {
        public static void main(String[] args) {
            //求年龄的总和
            Integer sum = Stream.of(new Person("张三", 18),
                            new Person("李四", 33),
                            new Person("张三", 38),
                            new Person("王五", 45),
                            new Person("张三", 19)
                    ).map(Person::getAge)
                    .reduce(0,Integer::sum);
            System.out.println(sum);
    
            //2.求年龄最大值
            Integer max = Stream.of(new Person("张三", 18),
                            new Person("李四", 33),
                            new Person("张三", 38),
                            new Person("王五", 45),
                            new Person("张三", 19)
                    ).map(Person::getAge)//实现数据类型的转换,符合reduce对数据的要求
                    .reduce(0,Math::max);//reduce实现数据的处理
            System.out.println(max);
    
    //3.统计字符串a出现的次数
           Integer count= Stream.of("a","b","c","d","a")
                    .map(ch->"a".equals(ch)?1:0)
                    .reduce(0,Integer::sum);
            System.out.println(count);
    
        }
    }
    

5.17 mapToInt

作用:将stream中的Integer类型转换为int类型,可使用mapToInt方法
在这里插入图片描述

使用:

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamTest14 {
    public static void main(String[] args) {
       //Integer占用的内存比int多很多,在stream流操作中会自动装箱和拆箱操作

        Integer arr[]={1,2,3,4,5,7,9};
        Stream.of(arr)
                .filter(i->i>0).forEach(System.out::println);
        System.out.println("--------------------");
        //为提高代码的执行效率,可以将stream中的integer数据先转换为int数据,然后再操作
        IntStream intStream = Stream.of(arr)
                .mapToInt(Integer::intValue);
        intStream.filter(i->i>3).forEach(System.out::println);
    }
}

5.18 concat:如果两个流,合并成为一个流,使用stream的concat方法

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);

    @SuppressWarnings("unchecked")
    Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
            (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
    Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
    return stream.onClose(Streams.composedClose(a, b));
}

使用:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest15 {
    public static void main(String[] args) {
        Stream<String> stream1=Stream.of("a","b","c");
        Stream<String> stream2=Stream.of("g","h","s");
        //通过concat将两个stream合并为一个新的stream
        Stream.concat(stream1,stream2).forEach(System.out::println);
    }
}

5.19 综合使用stream

在这里插入图片描述

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest16 {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("a1", "aa2", "a3", "aa4", "a5", "as6", "aa7");
        List<String> list2 = Arrays.asList("b1", "张b2", "b3", "张b4", "张bk5", "b6", "b7");
        Stream<String> stream1 = list1.stream()
                .filter(x -> x.length() == 3)
                .limit(3);
        Stream<String> stream2 = list2.stream().filter(x -> x.startsWith("张")).skip(2);
        Stream.concat(stream1, stream2)
                .map(Person::new)
                .forEach(System.out::println);
    }
}

输出结果:

Person{name='aa2', age=null}
Person{name='aa4', age=null}
Person{name='as6', age=null}
Person{name='张bk5', age=null}

1.结果收集到集合中

package com.newfeature;

import org.junit.Test;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Test01 {

    /**
     * Stream结果收集到集合中
     */
    @Test
    public void test01() {
        List<String> list = Stream.of("a", "b", "c", "c")
                .collect(Collectors.toList());
        System.out.println(list);

        //收集到set
        Set<String> set = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toSet());
        System.out.println(set);

        //获取的类型为具体的实现,eg:Arraylist,HashSet
        ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toCollection(ArrayList::new));
        //.collect(Collectors.toCollection(()->new ArrayList<>()));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);

    }
}

输出:


[a, b, c, c]
[a, b, c]
[a, b, c, a]
[a, b, c]

Process finished with exit code 0

2.结果收集到数组中

Stream中提供toArray方法将结果放到一个数组中,返回值类型是Object[],如果指定返回类型,则使用重载方法toArray(IntFunction f)方法

/**
 * 数据收集到数组中
 */
@Test
public void test02(){
    Object[] objects = Stream.of("a", "b", "c", "a").toArray();
    System.out.println(Arrays.toString(objects));

    //指定返回的数组中的数据类型
    String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
    System.out.println(Arrays.toString(strings));
}

5.20 对流中数据做聚合计算

使用stream流处理数据后,可以像数据库的聚合函数一样,对一个字段进行操作,eg:min,max,avg,sum,count.

package com.newfeature;

import com.newfeature.domain.Person;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Test01 {

    /**
     * Stream结果收集到集合中
     */
    @Test
    public void test01() {
        List<String> list = Stream.of("a", "b", "c", "c")
                .collect(Collectors.toList());
        System.out.println(list);

        //收集到set
        Set<String> set = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toSet());
        System.out.println(set);

        //获取的类型为具体的实现,eg:Arraylist,HashSet
        ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toCollection(ArrayList::new));
        //.collect(Collectors.toCollection(()->new ArrayList<>()));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);

    }

    /**
     * 数据收集到数组中
     */
    @Test
    public void test02() {
        Object[] objects = Stream.of("a", "b", "c", "a").toArray();
        System.out.println(Arrays.toString(objects));

        //指定返回的数组中的数据类型
        String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
        System.out.println(Arrays.toString(strings));
    }

    /**
     * stream中数据流聚合操作
     */
    @Test
    public void test03() {

        //获取年龄的最大值
        Optional<Person> maxAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最大年龄:" + maxAge.get());

        //获取年龄的最小值
        Optional<Person> minAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最小年龄:" + minAge.get());

        //求所有人年龄之和
        Integer sumAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                //.collect(Collectors.summingInt(s -> s.getAge()));
                .collect(Collectors.summingInt(Person::getAge));
        System.out.println("所有人年龄之和:" + sumAge);
        //求所有人年龄的平均值
        Double avgAge = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.averagingInt(Person::getAge));
        System.out.println("所有人年龄的平均值:" + avgAge);

        //统计人员数量
        Long count = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .filter(p -> p.getAge() > 18)
                .collect(Collectors.counting());
        System.out.println("统计人员数量:" + count);

    }
}

5.21 对流中数据做分组操作

/**
     * stream中数据流分组操作
     */
    @Test
    public void test04() {
        //分组
        Map<String, List<Person>> map = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.groupingBy(Person::getName));
        map.forEach((k,v)-> System.out.println("k="+k+",v="+v));

        System.out.println("--------------------------------");


//年龄大于等于18成年,否则未成年
        Map<String, List<Person>> m2 = Stream.of(
                        new Person("张三", 17),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
        m2.forEach((k,v)-> System.out.println("k="+k+",v="+v));


    }

多级分组实现:

/**
 * stream中数据流多级分组操作
 */
@Test
public void test05() {
    //根据name,age(成年与未成年)分组
    Map<String, Map<Object,List<Person>>> map=Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .collect(Collectors.groupingBy(Person::getName,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年")
    ));
    map.forEach((k,v)->{
        System.out.println("k="+k+",v="+v);
        v.forEach((k1,v1)->{
            System.out.println("\t"+k1+"="+v1);
        });
    } );


}

输出结果:

k=李四,v={成年=[Person{name='李四', age=33}]}
	成年=[Person{name='李四', age=33}]
k=张三,v={未成年=[Person{name='张三', age=17}], 成年=[Person{name='张三', age=18}]}
	未成年=[Person{name='张三', age=17}]
	成年=[Person{name='张三', age=18}]

5.22 对流中数据做分区操作

Collectors.partitioningBy根据值是否为true,把集合中的数据分割 为两个列表,一个true列表,一个false列表

在这里插入图片描述

@Test
public void test06() {
    Map<Boolean, List<Person>> m = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .collect(Collectors.partitioningBy(p -> p.getAge() > 18));
    m.forEach((k, v) -> {
        System.out.println(k + "\t" + v);
    });
}

输出结果:

false	[Person{name='张三', age=17}, Person{name='张三', age=18}]
true	[Person{name='李四', age=33}]

5.23 对流中数据做拼接

collectors.joining根据指定的连接符,将 所有的元素拼接成一个字符串

@Test
public void test07() {
    String s1 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining());
    System.out.println(s1);

    String s2 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining("-"));
    System.out.println(s2);

    String s3 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining("-","####","$$$$$"));
    System.out.println(s3);
}

5.24 并行的stream流

  • 串行的stream流:在一个线程上执行

    /**
     * 串行流
     */
    @Test
    public void test01() {
        long count = Stream.of(5, 6, 9, 0, 3)
                .filter(s -> {
                    System.out.println(Thread.currentThread() + " " + s);
                    return s > 3;
                }).count();
    }
    

输出结果:

Thread[main,5,main] 5
Thread[main,5,main] 6
Thread[main,5,main] 9
Thread[main,5,main] 0
Thread[main,5,main] 3
  • 并行流
    parallelStream是一个并行执行的流,通过默认的ForkJoinPool,可以提高多线程任务的速度。

    获取并行流的2种方式:

    1.通过list接口中的parallelStream()方法获取

    2.通过已有的串行流转换为并行流 parallel()

    /**
     * 获取并行流的两种方式
     */
    @Test
    public void test02() {
       List<Integer> list=new ArrayList<>();
    //通过list接口直接获取并行流
        Stream<Integer> integerStream=list.parallelStream();
        
        //将已有的串行流转换为并行流
        Stream<String> parallel = Stream.of("1,2,3").parallel();
    
    
    }
    
/**
 * 并行流的操作
 */
@Test
public void test03() {
    long count = Stream.of(1, 2, 3)
            .parallel()//将流转换为并发流,stream处理的时候会通过多线程处理
            .filter(s -> {
                System.out.println(Thread.currentThread() + " s=" + s);
                return s > 2;
            }).count();
    System.out.println(count);

}

输出:

Thread[ForkJoinPool.commonPool-worker-2,5,main] s=3
Thread[main,5,main] s=2
Thread[ForkJoinPool.commonPool-worker-1,5,main] s=1
1

5.25 并行流与串行流对比

通过for循环,串行stream流,并行流stream对八亿个数字求和,对比消耗时间

package com.newfeature;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.stream.LongStream;

public class Test03 {
    private  static long times=800000000;
    private long start;

    @Before
    public void before(){
start=System.currentTimeMillis();
    }

    @After
    public void end(){
        long end=System.currentTimeMillis();
        System.out.println("消耗时间:"+(end-start));

    }

    /**
     * 普通for循环
     */
    @Test
    public void test01(){
        System.out.println("普通for循环:");
        long res=0;
        for(int i=0;i<times;i++){
            res+=i;
        }
    }

    /**
     * 串行流处理
     */
    @Test
    public void test02(){
        System.out.println("串行流:serialStream");
        LongStream.rangeClosed(0,times)
                .reduce(0,Long::sum);
    }

    /**
     * 并行流处理
     */
    @Test
    public void test03(){
LongStream.rangeClosed(0,times)
        .parallel()
        .reduce(0,Long::sum);
    }

}

通过案例,可以看到parallelStream的效率最高

Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,表示每个任务都是一个线程操作

5.26 并行流的多线程安全问题

  • 多 线程处理数据的,线程安全问题

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test04 {
        //并行流多线程安全问题
        @Test
        public void test01(){
     List<Integer> list=new ArrayList<>();
     for(int i=0;i<1000;i++){
         list.add(i);
     }
            System.out.println(list.size());
    
     //使用并行流来向集合中添加数据
            List<Integer> listNew=new ArrayList<>();
            list.parallelStream()
                    .forEach(listNew::add);
            System.out.println(listNew.size());
        }
    }
    

    输出结果:

    1000
    950
    

    或者报异常,针对的解决方案:

    • 加同步锁
    • 使用线程安全的容器
    • 通过stream中的toArray或collect操作
     /**
         * 加同步锁
         */
        @Test
        public void test02() {
            List<Integer> list = new ArrayList<>();
    
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
            System.out.println(list.size());
    
            Object obj = new Object();
            //使用并行流来向集合中添加数据
            List<Integer> listNew = new ArrayList<>();
            list.parallelStream()
    //                .forEach(listNew::add);
                    .forEach(s -> {
                        synchronized (obj) {
                            listNew.add(s);
                        }
                    });
            System.out.println(listNew.size());
        }
    /**
     * 同步枷锁
     */
    @Test
    public void test03() {
        List<Integer> listNew = new ArrayList<>();
        Object obj = new Object();
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronized (obj) {
                        listNew.add(i);
                    }
                });
        System.out.println(listNew.size());
    }
    
     /**
         * 使用线程安全的容器
         */
        @Test
        public void test04() {
            Vector v = new Vector();
            IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .forEach(i -> {
    //                    synchronized (obj) {
                            v.add(i);
    //                    }
                    });
            System.out.println(v.size());
        }
    
    
        /**
         * 将线程不安全的容器转换为线程安全的容器
         */
        @Test
        public void test05() {
            List<Integer> listNew=new ArrayList<>();
            //将线程不安全的容器,包装成线程安全的容器
            List<Integer> synchronizedList= Collections.synchronizedList(listNew);
            IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .forEach(i -> {
                        synchronizedList.add(i);
    //                    }
                    });
            System.out.println(synchronizedList.size());
        }
    
    
        /**
         * 通过Stream中的toArray方法或collect方法来操作就是满足线程安全的
         */
        @Test
        public void test06() {
            List<Integer> list = IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .boxed()
                    .collect(Collectors.toList());
    
            System.out.println(list.size());
        }
    

5.27 Fork/Join框架

parallelStream使用的是Fork/Join框架,fork/join框架自jdk7引入,Fork/join框架可以将一个大任务拆分为很多小任务异步执行,fork/join框架主要包括三个模块:

  1. 线程池:ForkJoinPool

  2. 任务对象:ForkJoinTask

  3. 执行任务的线程: ForkJoinWorkerThread

    在这里插入图片描述

  • fork/join原理:采用分治法(Divide-and-Conquer Algorithm)来解决问题。eg:快速排序算法
    在这里插入图片描述

  • fork/join原理-工作窃取算法(work-stealing):
    利用现代硬件设备多核,在一个操作时候,会有空闲的cpu,利用空闲的cpu提高性能。工作窃取(work-stealing)算法是整个fork/join框架的核心理念,是指一个线程从其他队列里窃取任务来执行。

    在这里插入图片描述

  • fork/join使用

    需求:使用fork/join计算1~10000的和,当任务计算的数量大于3000,拆分任务,数量小于3000不拆分任务计算。

在这里插入图片描述

实现:

package com.newfeature;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class Test05 {
    public static void main(String[] args) {
       long start=System.currentTimeMillis();

        ForkJoinPool pool=new ForkJoinPool();
        SumRecursiveTask task=new SumRecursiveTask(1,10000L);
        Long result = pool.invoke(task);
        System.out.println("result="+result);
        long end=System.currentTimeMillis();
        System.out.println("总耗时:"+(end-start));
    }
}


class SumRecursiveTask extends RecursiveTask<Long> {
    //定义拆分的临界值
    private static  final long  THRESHOLD=3000L;

    private  final long start;
    private final  long end;

    public SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }


    @Override
    protected Long compute() {
        long length=end-start;
        if(length<=THRESHOLD){
            //任务不用拆分,可以计算
            long sum=0;
            for(long i=start;i<=end;i++){
                sum+=i;
            }

            System.out.println("计算:"+start+"-->"+end+",的结果:"+sum);
            return sum;
        }else{
           //数量大于预定的数量,说明任务还需要拆分
           long middle=(start+end)/2;
            System.out.println("拆分:左边->"+start+"-->"+middle+",右边-->"+(middle+1)+"-->"+end);
            SumRecursiveTask left=new SumRecursiveTask(start,middle);
            left.fork();
            SumRecursiveTask right=new SumRecursiveTask(middle+1,end);
            right.fork();
            return left.join()+right.join();
        }
    }
}

输出结果:

拆分:左边->1-->5000,右边-->5001-->10000
拆分:左边->1-->2500,右边-->2501-->5000
拆分:左边->5001-->7500,右边-->7501-->10000
计算:7501-->10000,的结果:21876250
计算:2501-->5000,的结果:9376250
计算:5001-->7500,的结果:15626250
计算:1-->2500,的结果:3126250
result=50005000
总耗时:18

6.optional类

Optional类主要用来解决空指针的问题

6.1 以前对null的处理

  @Test
    public void Test01(){
//        String username="张三";
        String username=null;
        if(username!=null){
            System.out.println("字符串的长度:"+username.length());
        }else{
            System.out.println("字符串为空");
        }

    }

6.2 jdk1.8对null的处理->optional类

optional类是一个没有子类的工具类,optional是一个可以为null的容器对象,主要作用:就是为了避免Null检查,防止NullPoiniterException

在这里插入图片描述

6.3 optional基本使用

optional对象的创建方式:

package com.newfeature;

import org.junit.Test;

import java.util.Optional;

public class Test06 {
    @Test
    public void Test01(){
//        String username="张三";
        String username=null;
        if(username!=null){
            System.out.println("字符串的长度:"+username.length());
        }else{
            System.out.println("字符串为空");
        }

    }

    /**
     * optional对象的创建方式
     */
    @Test
    public void test02(){
        //通过of方法,of方法不支持null的
        Optional<String> op1 = Optional.of("张三");
        //Optional<Object> op2 = Optional.of(null);

        //第二种通过ofNullable方法支持null
        Optional<String> op3 = Optional.ofNullable("lisi");
        Optional<Object> op4 = Optional.ofNullable(null);

        //第三种方式通过empty方法创建一个空的optional对象
        Optional<Object> op5 = Optional.empty();
    }

}

常用方法介绍:

/**
 * optional中常用方法介绍
 * get():如果optional有值就返回,否则抛出NoSuchElementException异常,
 *       get()通常和isPresent方法一块使用
 *  isPresent():判断元素是否存在,有返回true,无返回false
 *  orElse(T t):如果调用对象包含值,返回值,否则 返回t
 *  orElseGet(Supplier s):如果调用对象包含值,就返回值,否则返回Lambda表达式的返回值
 */
@Test
public void test03(){
    Optional<String> op1=Optional.of("张三");
    Optional<String> op2=Optional.empty();

  //获取optional中的值
    if(op1.isPresent()){
        String s1=op1.get();
        System.out.println("用于名:"+s1);
    }
   if(op2.isPresent()){
       System.out.println(op2.get());
   }else {
       System.out.println("op2是一个空Optional对象");
   }


   String s3=op1.orElse("李四");
    System.out.println(s3);
    System.out.println(op2.orElse("jackma"));

    String s5 = op2.orElseGet(()->{
        return "hello666";
    });
    System.out.println(s5);
}



  @Test
    public void test04() {
        Optional<String> op1 = Optional.of("张三");
        Optional<String> op2 = Optional.empty();
        //op1.ifPresent(s -> System.out.println("有值:"+s));
        op1.ifPresent(System.out::println);

        op2.ifPresentOrElse(System.out::println, () -> {
            System.out.println("op2 对象为null");
        });
    }

    /**
     * 自定义一个方法,将person对象中name转换为大写并返回
     */
    @Test
    public void test05() {
        Person p = new Person("zhangsan", 10);
        Optional<Person> op = Optional.of(p);
        String name = getNameForOptional(op);
        System.out.println("name:" + name);

    }

    public String getNameForOptional(Optional<Person> op) {
        if (op.isPresent()) {
            String name = op
                    //.map(p -> p.getName())
                    .map(Person::getName)
                    //.map(p -> p.toUpperCase())
                    .map(String::toUpperCase)
                    .orElse("空值");
            return name;

        }
        return null;
    }

    /**
     * 根据person对象,将name转换为大写并返回
     *
     * @param person
     * @return
     */
    public String getName(Person person) {
        if (person != null) {
            String name = person.getName();
            if (name != null) {
                return name.toUpperCase();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

7 .jdk1.8 日期&时间新API

  • 旧版的日期处理:1.设计不合理;2.存在线程不安全的问题->java.uitl.Date,所有的日期类都是可变的;3.时区处理麻烦,日期类不提供国际化,没有时区支持。

  • 新的日期API介绍:
    在这里插入图片描述

  • jdk新的日期api常用操作:

    package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    
    public class Test07 {
    
        /**
         * jdk1.8日期操作 LocalDate
         */
        @Test
        public  void test01(){
            //1.创建指定的日期
            LocalDate date1 = LocalDate.of(2021, 05, 02);
            System.out.println("date1="+date1);
    
            //2.得到当前的日期
            LocalDate now = LocalDate.now();
            System.out.println("now="+now);
            //3.根据localdate对象获取对应的日期信息
            System.out.println("Year:"+now.getYear());
            System.out.println("Month:"+now.getMonth());
            System.out.println("day:"+now.getDayOfMonth());
            System.out.println("week:"+now.getDayOfWeek().getValue());
        }
    
        /**
         * LocalTime 时间操作
         */
        @Test
        public  void test02() {
            //1.获取指定的时间
            LocalTime time=LocalTime.of(5,26,33,23145);
            System.out.println(time);
            //2.获取当前的时间
            LocalTime now=LocalTime.now();
            System.out.println(now);
    
            //3.获取时间信息
            System.out.println(now.getHour());
            System.out.println(now.getMinute());
            System.out.println(now.getSecond());
            System.out.println(now.getNano());
        }
    
        /**
         * 时期时间类型 LocalDateTime
         */
        @Test
        public void test03(){
            //1.获取指定的日期时间
            LocalDateTime dateTime = LocalDateTime.of(2020, 06, 01, 12, 12, 33, 213);
            System.out.println(dateTime);
            //2.获取指定的日期时间
            LocalDateTime now = LocalDateTime.now();
            System.out.println(now);
    
            //获取日期时间信息
            System.out.println(now.getYear());
            System.out.println(now.getMonth());
            System.out.println(now.getDayOfMonth());
            System.out.println(now.getDayOfWeek());
            System.out.println(now.getHour());
        }
    }
    
  • 日期时间的修改和比较

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    
    public class Test08 {
        /**
         * 日期时间的修改
         */
        @Test
        public  void test01(){
            LocalDateTime now=LocalDateTime.now();
            System.out.println("now:"+now);
            //修改日期时间,对日期时间的修改,对已存在的LocalDate对象,创建了模版,并不会修改原来的信息
            LocalDateTime localDateTime=now.withYear(1988);
            System.out.println("now:"+now);
            System.out.println("修改后的:"+localDateTime);
            System.out.println("月份:"+now.withMonth(04));
            System.out.println("天:"+now.withDayOfMonth(25));
            System.out.println("小时:"+now.withHour(8));
    
            //对当前日期时间的基础上,加上或减去指定的时间
            System.out.println("两天后:"+now.plusDays(2));
            System.out.println("10年后:"+now.plusYears(10));
            System.out.println("6个月后: " + now.plusMonths(6));
    
            System.out.println("10年前: " + now.minusYears(10));
            System.out.println("半年前:" + now.minusMonths(6));
            System.out.println("一周前:" + now.minusDays(7));
        }
    
        /**
         * 日期时间比较
         */
        @Test
        public void test02(){
            LocalDate now=LocalDate.now();
            LocalDate date=LocalDate.of(2020,1,22);
    
            //jdk8实现日期比较
            System.out.println("now.isAfter(date) = " + now.isAfter(date));
            System.out.println("now.isBefore(date) = " + now.isBefore(date));
            System.out.println("now.isEqual(date) = " + now.isEqual(date));
    
        }
    }
    

注意:在进行日期时间修改时候,原来的localdate对象是不会被修改的,每次操作是返回一个新的localdate对象,所以多线程场景下是数据安全的

  • 日期时间格式化
    在jdk8中通过java.time.format.DateTimeFormatter类进行日期的解析和格式化操作

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    public class Test8 {
        /**
         * 日期时间格式化
         */
        @Test
        public void test01(){
    
            LocalDateTime now=LocalDateTime.now();
            //指定格式,使用系统默认格式
            DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
            //将日期时间转换为字符串
            String format = now.format(isoLocalDateTime);
            System.out.println("format = " + format);
    
            //通过ofPattern方法来指定特定的格式
            DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String format1 = now.format(dateTimeFormatter1);
            System.out.println("format1 = " + format1);
    
            //将字符串解析为一个日期时间类型
            LocalDateTime parse = LocalDateTime.parse("1997-12-13 22:34:12", dateTimeFormatter1);
            System.out.println("parse = " + parse);
    
    
        }
    }
    
  • Instant类: jdk8中新增一个时间戳,内部保存了从1970年1月1日00:00:00以来的秒和纳秒

  • /**
     * Instant时间戳:可以用来统计时间消耗
     */
    @Test
    public void test02() throws InterruptedException {
        Instant now = Instant.now();
        System.out.println("now = " + now);
        //获取从1970年1月1日 00:00:00到现在的纳秒
        System.out.println(now.getNano());
    
        Thread.sleep(5);
       Instant now1= Instant.now();
        System.out.println("耗时:"+(now1.getNano()-now.getNano()));
    }
    
  • 计算日期时间差
    Jdk8提供了工具类:Duration/Period:计算日期时间差
    1.Duration:用来计算两个时间差(LocalTime)
    2.Period:用来计算两个日期差(LocalDate)

    package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.Duration;
    import java.time.LocalDate;
    import java.time.LocalTime;
    import java.time.Period;
    
    public class Test9 {
        /**
         * 计算日期时间差
         */
        @Test
        public  void test01(){
            //计算时间差
            LocalTime now = LocalTime.now();
            LocalTime time = LocalTime.of(22, 45, 59);
            System.out.println("now = " + now);
    
            //通过Duration计算 时间差
            Duration duration = Duration.between(now,time);
            System.out.println(duration.toDays());
            System.out.println(duration.toHours());
            System.out.println(duration.toMinutes());
            System.out.println(duration.toSeconds());
            System.out.println(duration.toMillis());
    
            //计算日期差
            LocalDate nowDate = LocalDate.now();
            LocalDate date = LocalDate.of(1997, 12, 5);
            Period period = Period.between(date, nowDate);
            System.out.println("period.getYears() = " + period.getYears());
            System.out.println("period.getMonths() = " + period.getMonths());
            System.out.println("period.getDays() = " + period.getDays());
    
        }
    }
    
  • 时间校正器:将日期调整到下个月第一天 等操作,通过时间矫正器操作
    1.TemporalAdjuster:时间 校正器
    2.TemporalAdjusters:通过该类静态方法提供的常用TemporalAdjuster的实现

  • /**
     * 时间校正器
     */
    @Test
    public  void test2(){
        LocalDateTime now = LocalDateTime.now();
        //将当前的日期调整到下个月的1号
        TemporalAdjuster adjuster=temporal -> {
            LocalDateTime dateTime= (LocalDateTime) temporal;
            LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
            System.out.println("nextMonth = " + nextMonth);
            return  nextMonth;
        };
    
        //通过TemporalAdjusters来实现
        //LocalDateTime nextMonth = now.with(adjuster);
        LocalDateTime nextMonth=now.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("nextMonth = " + nextMonth);
    
    }
    
  • 日期时间的时区
    jdk8中加入对时区的支持,LocalDate,LocalTime,LocalDateTime是不带时区的,带时区的日期时间类分别是:ZonedDate,ZonedTime,ZoneDateTime;其中每个时区都对应着ID,ID的格式为:区域/城市 eg:Asia/Shanghai等;zoneId:该类中包含的所有的时区信息

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.Clock;
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Test10 {
        /**
         * 时区操作
         */
        @Test
        public  void test01(){
            //1.获取所有的时区id
            //ZoneId.getAvailableZoneIds().forEach(System.out::println);
    
    
            //获取当前时间,使用的是东八区时区;比标准的时间早8个小时
            LocalDateTime now=LocalDateTime.now();
            System.out.println("now = " + now);
            //获取标准时间
            ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
            System.out.println("bz = " + bz);
    
            //使用计算机默认的时区,创建日期时间
            ZonedDateTime now1=ZonedDateTime.now();
            System.out.println("now1 = " + now1);
    
            //使用指定的时区创建日期时间
            ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
            System.out.println("now2 = " + now2);
        }
    }
    

jdk新的日期和时间API的优点:

1.新的日期时间API中,日期和时间对象是不可变,操作日期不会影响原来的值,而是生成一个新的实例

2.提供了两种不同的方式,区分人和机器的操作
3.TemporalAdjuster可以更精确的操作日期,还可以自定义日期调整期
4.线程安全

8 其他新特性

1.重复注解 :@Repeatable
使用步骤:
step1 定义重复注解容器:

package com.newfeature;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[]  value();

}

step2 定义一个可以重复的注解:

package com.newfeature;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

step3 配置多个注解:

package com.newfeature;

@MyAnnotation("test1")
@MyAnnotation("test12")
@MyAnnotation("test13")
public class AnnoTest01 {

    @MyAnnotation("fun1")
    @MyAnnotation("fun2")
    public void test01(){

}

  
}

step4解析得到指定的注解:

/**
 * 解析重复注解
 * @param args
 */
public static void main(String[] args) throws NoSuchMethodException {
   //获取类标注的重复注解
    MyAnnotation[] annotationsByType = AnnoTest01.class.getAnnotationsByType(MyAnnotation.class);
    for (MyAnnotation myAnnotation : annotationsByType) {
        String value = myAnnotation.value();
        System.out.println("value = " + value);
    }

    //获取方法上标注的重复注解
    MyAnnotation[] test01s = AnnoTest01.class.getMethod("test01").getAnnotationsByType(MyAnnotation.class);
    for (MyAnnotation test01 : test01s) {
        System.out.println("test01 = " + test01.value());
    }
}

2.类型注解
jdk8为@Target元注解新增两个类型:
TYPE_PARAMETER 表示该注解能写在类型参数的声明语句中。类型参数 声明如:
step1定义TYPE_PARAMETER :

package com.newfeature;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}

step2 使用 :

package com.newfeature;

public class TypeDemo01<@TypeParam T> {
 public <@TypeParam  K extends  Object> K test01(){
     return null;
 }
}

TYPE_USE 表示注解可以在任何用到的类型的地方使用.
step1定义

package com.newfeature;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

step2使用

package com.newfeature;

public class TypeUseDemo01 {
    public @NotNull  Integer age=10;

    public  Integer sum(@NotNull Integer a,@NotNull Integer b){
        return a+b;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/349283.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CI/CD

介绍一下CI/CD CI/CD的出现改变了开发人员和测试人员发布软件的方式,从最初的瀑布模型,到最后的敏捷开发(Agile Development),再到今天的DevOps,这是现代开发人员构建出色产品的技术路线 随着DevOps的兴起,出现了持续集成,持续交付和持续部署的新方法,传统的软件开发和交付方…

Maven(上):Maven介绍、安装配置及工程构建

1. Maven介绍 Maven 是一款为 Java 项目管理构建、依赖管理的工具&#xff08;软件&#xff09;&#xff0c;使用 Maven 可以自动化构建、测试、打包和发布项目&#xff0c;大大提高了开发效率和质量。 Maven就是一个软件&#xff0c;掌握安装、配置、以及基本功能 &#xff…

19 python快速上手

面向对象高级和应用 1. 继承【补充】1.1 mro和c3算法1.2 py2和py3区别&#xff08;了解&#xff09; 2. 内置函数补充3.异常处理3.1 异常细分3.2 自定义异常&抛出异常3.4 特殊的finally 4.反射4.1 一些皆对象4.2 import_module 反射 总结 各位小伙伴想要博客相关资料的话关…

vue3---inputRef.value.focus()报错Cannot read properties of null (reading ‘focus‘)

问题描述&#xff1a;点击编辑按钮&#xff0c;出现el-input框&#xff08;el-input显示隐藏通过v-if控制&#xff09; <el-input ref"inputRef" v-if"isEdit" v-model"modelName" blur"isEdit false" /> <el-button text …

python-自动化篇-办公-excel-实例应用(一维转二维)

文章目录 准备代码效果 准备 放根目录 代码 import openpyxl wbopenpyxl.load_workbook(业绩表.xlsx) if not 二维表 in wb.sheetnames:nwswb.create_sheet(二维表)wswb.worksheets[0]rngslist(ws.values)[1:]mmlist({m.value: for m in ws[b][1:]})namelist({m.value: for …

(2)(2.8) Holybro 900Mhz XBP9X无线电遥测设备

文章目录 前言 1 特点 2 规格 3 电源 4 引脚输出 5 下载 前言 Holybro XBP9X 无线电设备可使用 Digi 免费的 XCTU 软件或通过 Digi 简化的 AT 或 API 命令集轻松进行配置。无线电台采用 256 位 AES 加密技术&#xff0c;可在设备之间安全可靠地传输关键数据。无线电的射…

基于Kubernetes(K8s)构建企业容器云基础运行环境

cncfstack 新 文章上线&#xff1a; 书名&#xff1a;《云原生解决方案》 地址&#xff1a;https://zhaowenyu.com/cncf-solution 访问&#xff1a;文章底部“阅读原文”或访问域名 云原生计算是云计算发展新的里程碑阶段&#xff0c;是当今与未来很长一段时间中 IT 发展的技…

代码随想录 Leetcode111. 二叉树的最小深度

题目&#xff1a; 代码(首刷自解 2024年1月24日&#xff09;&#xff1a; class Solution { public:int minDepth(TreeNode* root) {if(root nullptr) return 0;queue<TreeNode*> que;TreeNode* cur root;que.push(cur);int size 0;int depth 0;while (!que.empty()…

Tomcat session复制及session共享技术

目录 1、环境 2、配置测试页面 3、配置session共享 前言&#xff1a; 为什么要做session复制或共享 实现Session复制或Session共享的目的是为了在多个Tomcat实例之间实现Session的无缝转移和共享&#xff0c;以提供更高的可伸缩性、负载均衡和容错性。以下是一些原因&#x…

C语言基本概念

目录 2.1 编写一个简单的C程序 2.1.1 编译和链接 2.1.2 集成开发环境 2.2 简单程序的一般形式 2.2.1 指令 2.2.2 函数 2.2.3 语句 2.3 注释 2.4 变量和赋值 2.4.1 类型 2.4.2 声明 2.4.3 赋值 2.4.4 显示变量的值 2.4.5 初始化 2.4.6 显示表达式的值 2.5 读入…

7款值得去尝试的前端动画特效(附效果图及在线演示)

分享7款好玩的前端动画特效 其中有CSS动画、canvas动画、js小游戏等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 橙汁粒子特效 一款基于canvas实现的粒子动画特效 点击杯子 杯子上方会有橙子掉落至杯中并变为橙…

如何做标准化?| 京东云技术团队

在现代信息化的市场环境和社会中&#xff0c;标准化已经成为了各种行业的一个重要的标志。标准化不仅可以提升生产效率&#xff0c;减轻质量问题&#xff0c;还可以增加产品的可靠性和互通性。在这篇文章中&#xff0c;我们将探讨如何做标准化&#xff0c;为您提供详细的指导和…

深入理解Java类和对象的关系

如果要说清楚对象和类的关系&#xff0c;不可避免的要提到C&#xff0c;因为Java从时间线上来说&#xff0c;是C和C之后的一门语言&#xff0c;很多Java Coder 也是因为厌烦了C的一些特性&#xff0c;进而从事于Java开发的&#xff0c;所以以下内容会利用C的一部分知识来对比&a…

Mac+Android Studio配置 Flutter环境

Fluttrer中文下载官网 Flutter下载官网 1、环境变量 .zshrc #Flutter export PUB_HOSTED_URL"https://pub.flutter-io.cn" export FLUTTER_STORAGE_BASE_URL"https://storage.flutter-io.cn" export FLUTTER_HOME/Users/leon/Flutter/flutter_3_10_4/f…

【C/C++】C/C++编程——第一个 C++ 程序:HelloWorld

第一个 C 程序&#xff1a;HelloWorld 大家好&#xff0c;我是 shopeeai&#xff0c;也可以叫我虾皮&#xff0c;中科大菜鸟研究生。昨天我们成功搭建好了 C 的开发环境&#xff0c;今天我们来介绍一下第一个 C 程序,打印一个"hello world"。首先我们先贴一下示例代…

Linux(linux版本 centos 7) 下安装 oracle 19c详细教程(新手小白易上手)

一、安装前准备 1、下载预安装包 wget http://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm预安装包下载成功 2、下载oracle安装包 下载地址如下 https://www.oracle.com/cn/database/technologies…

Python 正则分割字符串并提取匹配的记号和关键词

需求是&#xff1a;原字符串内含有特殊记号&#xff0c;比如"V_{CC} V_{DD}"&#xff0c;其中_{CC} 是latex 的下标语法&#xff0c;实际显示出来是下面这样&#xff1a; V C C V D D V_{CC} V_{DD} VCC​VDD​ 现在要把下标部分提取出来&#xff0c;并把原字符串…

Python之数据可视化基础

目录 一 JSON数据格式转换 二 pyecharts模块 三 Pyecharts入门 四 数据可视化之疫情折线图 一 JSON数据格式转换 什么是JSON? JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。它以易于阅读和编写的方式来表示结构化数据。JSO…

构建未来学堂:在线教育系统开发技术实践

在当今数字化时代&#xff0c;在线教育系统的开发越发显得至关重要。本文将带你深入了解在线教育系统的开发&#xff0c;涉及到关键的技术实践和代码示例。我们将采用现代化技术栈&#xff0c;为未来学堂的搭建提供实用的指南。 技术栈选择 在开始实际的开发之前&#xff0c…

在IDEA中创建SpringBoot项目

概述 SpringBoot是由Pivotal团队提供的全新的框架&#xff0c;其设计的目的是用来简化Spring应用的初始搭建以及开发过程。 传统方式构建Spring应用程序 导入依赖繁琐 依赖冲突 项目配置繁琐 SpringBoot特性 1、起步依赖 本质上就行一个Maven坐标&#xff0c;整合了完成一…