算法学习-Java刷题过程中你一定需要熟练的容器Api、Stream流、Lambda表达式


笔者在用Java刷题的过程中,经常会使用到许多和容器、流、lambda表达式有关的操作,但是都不是特别熟练,如果在熟练的情况下,可以简化许多语句。因此,笔者写下这篇文章,对相关的操作进行归纳。

本文参考:

详解Java中的Lambda表达式
Java8中Function函数式接口详解及使用
菜鸟教程-Stream
Java Stream流(详解)
Java 8 stream的详细用法

1. Stream流

Stream Api是「集合操作」的一种简化表达形式。其特点是惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。

参考Java 8 stream的详细用法,其分类如下:
在这里插入图片描述

  • 无状态:指元素的处理不受之前元素的影响;

  • 有状态:指该操作只有拿到所有元素之后才能继续下去。

  • 非短路操作:指必须处理所有元素才能得到最终结果;

  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。`

创建方法:

  1. Collection(包括List、Set、Map键和值)下的 stream() 和 parallelStream() 方法获取流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
  1. 数组获取流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
还可以
Stream<String> stream = Stream.of(nums)
  1. 使用Stream中的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
 
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
 
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);

常用操作:

  1. Collection容器(list[Integer])转换成数组(int[]):将Collection<Integer>类型的容器转换为IntStream,然后再用IntStream.toArray()方法将IntStream转换为int[]数组。
int[]= list.stream().mapToInt(x->x).toArray();
int[]= stack.stream().mapToInt(x->x).toArray();
  1. 数组(int[])转换成Collection容器(list[Integer]):使用Arrays.stream将int[]转换成IntStream;使用IntStream中的boxed()装箱,将IntStream转换成Stream<Integer>;使用Stream的collect(),将Stream<T>转换成List<T>,因此正是List<Integer>
List<Integer> list1 = Arrays.stream(data).boxed().collect(Collectors.toList());

2. Lambda表达式

Lambda表达式是「创建匿名内部类对象」的一种简化方式。

Lambda表达式常见的用法就是将其创建的对象作为参数传递给方法。

函数式接口

Lambda表达式的目标类型必须是「函数式接口」,函数式接口是只包含一个抽象方法的接口,可以使用@FunctionalInterface 注解进行检查。函数式接口可以包含多个默认方法、类方法,但仅能声明一个抽象方法。感性理解来看,实现接口中的抽象方法,我们就能使用该被实现的接口创建匿名内部类对象,正是对这个过程进行简化。例如:

//自定义函数式接口
@FunctionalInterface
interface eat {
  void eatFood();
}


public static void main(String[] args) {	
	//override实现抽象方法,创建匿名内部类
    eat e1 = new eat(){
	  	@Override
	  	public void eatFood(){
	  		System.out.println("传统方法创建对象");
	  	}
  	};
  	e1.eatFood();
  
   //lambda表达式进行匿名内部类创建简化
   eat e2 = () -> System.out.println("lambda方式创建对象");
   e2.eatFood();
}

自定义函数式接口:

@FunctionalInterface //此注解用来表明是函数式接口
public interface MyInterface<T> {
	//函数式接口中只能有一个抽象方法
	void getValue(T t);
}

//自定义函数的Lambda表达式实现
MyInterface<String> myinter= (x)-> System.out.println(x);

JDK中常用的函数式接口:
在这里插入图片描述
在这里插入图片描述
常见的还有Comparator<T>
更全面的总结可以参考Java8中Function函数式接口详解及使用

Lambda书写语法

lambda表达式的语法格式如下:

(实现的这个接口中的抽象方法中的形参列表parameters) -> 抽象方法的处理expression;(实现的这个接口中的抽象方法中的形参列表parameters) ->{抽象方法的处理 statements; };
  • 可选参数类型声明:可以不声明参数类型。
  • 可选的参数圆括号:一个参数无需定义圆括号。
  • 可选的方法体大括号:expression只有一条语句,可以不用大括号。
  • 可选的return关键字:expression只有一个返回值,可以不写返回关键字。

无返回值的抽象方法:

//抽象方法无返回值
public interface MyInterface {
    public abstract void show(int a,int b);
}


public class MyTest1 {
    public static void main(String[] args) {
    	//传统的匿名内部类中重写方法
        MyInterface myInter = new MyInterface() {
            @Override
            public void show(int a, int b) {
                System.out.println(a + b);
            }
        };
        myInter.show(20, 30);//50
		
        //简写1:标准的lambda简化,抽象方法实现
        MyInterface myInter1 = (int a, int b) -> {
            System.out.println(a + b);
        };
        myInter1.show(20, 40);//60

        //简写2:省略形参列表中的形参类型
        MyInterface myInter2 = (a, b) -> {
            System.out.println(a + b);//70
        };
        myInter2.show(20, 50);

        //简写3:方法体中只有一行代码,进行简化
        MyInterface myInter3 = (a, b) -> System.out.println(a + b);
        myInter3.show(20, 60);//80
    }
}

有返回值的抽象方法:

//抽象方法有int返回值
public interface MyInterface {
    public abstract int test(int a,int b);
}

public class MyTest2 {
    public static void main(String[] args) {
    	//传统的匿名内部类写法
        MyInterface test1 = new MyInterface() {
            @Override
            public int test(int a, int b) {
                return a - b;
            }
        };
        System.out.println(test1.test(90, 8));//82

        //简写1:标准的lambda简化,抽象方法实现
        MyInterface test2 = (int a, int b) -> {
            return a - b;
        };
        System.out.println(test2.test(20, 10));//10

        //简写2:省略形参列表中的形参类型
        MyInterface test3 = (a, b) -> {return a - b;};
        System.out.println(test3.test(30, 10));//20

        //简写3:方法中只有一行代码,可以简化,同时去掉return关键字
        MyInterface test4 = (a, b) -> a - b;
        System.out.println(test4.test(40, 10));//30
    }
}

只有一个形参的抽象方法:

//抽象方法只有一个形参
public interface MyInterface {
    public abstract int show(int a);
}

public class MyTest3 {
    public static void main(String[] args) {
    	//传统的匿名内部类写法
        MyInterface myInter = new MyInterface(){
        	@Override
        	public int show(int a){
				return a-20;
			}
        }; 
        System.out.println(myInter.show(20););//0
		
		//简写1:标准的lambda简化,抽象方法实现
		MyInterface myInter1= (int a)->{
			return a-20;
		};
		System.out.println(myInter1.show(30););//10
		
		//简写2:省略形参列表中的形参类型
		MyInterface myInter2=(a)->{
			return a-20;
		};
		System.out.println(myInter2.show(40););//20

		 //简写3:方法中只有一行代码,可以简化,同时去掉return关键字
		 MyInterface myInter3=(a)->a-20;
		 System.out.println(myInter3.show(50););//30

		//简写4:一个形参可以不写圆括号
		MyInterface myInter4=a->a-20;
		System.out.println(myInter4.show(50););//30
    }
}

方法引用

当Lambda方法体中的操作,已经有别的对象或类实现了,就可以尝试引用已实现的方法。

传入方法的参数和方法内部调用方法的入参是一样的。

分类:

种类语法说明标准Lambda表达式
静态方法引用类名::静态方法函数式接口中「被实现的方法」的全部参数传给该方法作为参数(a,b,...) -> 类名.静态方法(a,b,...)
实例方法引用对象::实例方法函数式接口中「被实现的方法」的全部参数传给该方法作为参数(a,b,...) -> 对象.实例方法(a,b,...)
对象方法引用类名::对象方法函数式接口中「被实现的方法」的第一个参数作为调用者,后面的参数全部传给该方法作为参数(a,b,...)->a.对象方法(b,...)
构造方法引用类名::new函数式接口中「被实现的方法」的全部参数传给该构造器作为参数(a,b,...)->new 类名(a,b,...)

静态方法引用:

Consumer<String> c1 = r -> Integer.parseInt(r);
c1.accept("1");
Consumer<String> c2 =Integer::parseInt;
c1.accept("2");

实例方法引用:

 Consumer<String> ins1 = r -> System.out.print(r);
 c1.accept("1");
 Consumer<String> ins2 =System.out::print;
 c1.accept("2");

对象方法引用:

Comparator<String> comparator = (o1, o2)->o1.compareTo(o2);
System.out.println(comparator.compare("20", "12"));//1

Comparator<String> comparator1 = String::compareTo;
System.out.println(comparator1.compare("20", "12"));//1

构造方法引用:

Consumer<String> n1 = r ->new BigDecimal(r);
 c1.accept("1");
 Consumer<String> n2 =BigDecimal::new;
 c1.accept("2");

3. 各类Api

Array

//以a,b,c为元素生成List
List arr=Arrays.asList(a,b,c)

//使用提供的生成函数来计算每个元素,设置指定数组的所有元素
setAll(T[] array, IntFunction<? extends T> generator)
例子:
ArrayList<Integer>[] g=new ArrayList[k];
Arrays.setAll(g,e->new ArrayList<Integer>());

//将二维数组按照第二维升序排列
 Arrays.sort(points,(o1,o2)->o1[1]-o2[1]);
//但是减法可能会碰到负数溢出的问题,本来应该为负数的结果转变成正数,调用Interger.compare()
Arrays.sort(points,(o1,o2)->Integer.compare(o1[1],o2[1]));
//有可能原数据就需要用long表示,调用Long.compare()
Arrays.sort(points,(o1,o2)->Long.compare(o1[1],o2[1]));

ArrayList

//在对应位置处加入val
ArrayList<Integer> list = new ArrayList<>();
list.add(0,val);

//从别的Collection接口下的容器,拷贝生成ArrayList
//常在回溯记录路径中使用
LinkedList<Integer> path=new LinkedList<>();
ArrayList<Integer> list=new ArrayList<>(path);

// List转二维数组,转换到的每个元素需要为引用类型
 ArrayList<int[]> ans=new ArrayList<>();
 ans.toArray(new int[0][0])// 传入二维数组类型

// lambda实现int[]转List<Integer>
 List<Integer> ans= Arrays.stream(arr1).boxed().collect(Collectors.toList());

// List<Integer>转为int[]
int[]ans=list.stream().mapToInt(x->x).toArray();

LinkedList

LinkedList<Integer> list = new LinkedList<>();

//在对应位置处加入val
list.add(0,val);
//在队尾加入元素
list.add(val);
list.offer(val);
list.addLast(val);
list.offerLast(val);
//在队头加入元素
list.offerFirst(val);
list.addFirst(val);



// 得到idx索引上的元素
E=list.get(idx);
// 得到第一个元素
E=list.getFirst();
// 得到最后一个元素
E=list.getLast();

//删除第一个元素
E = list.remove();
E =list.removeFirst();
//删除最后一个元素
E =list.removeLast();

HashMap

//判断key是否存在
boolean flag=map.containsKey(key);
//判断value是否存在
boolean flag=map.containsValue(key);

//获取Map中的所有key
Set<Integer> map1 = map.keySet();
//获取Map中的所有value
Collection<String> map2 = map.values();
//获取Map中所有的key-value对象
Set<Map.Entry<Integer, String>> map3 = map.entrySet();


// 如果添加key对应value为空,添加新元素
map.putIfAbsent(key,value)

// 如果查找key对应value为空,则得到默认的元素
map.getOrDefault(key,new ArrayList<>(Integer));

// 删除对应的key-value,并返回value
value=map.remove(key)

Deque

//标准队列操作
Deque<TreeNode>  que= new ArrayDeque<>(); 
Deque<TreeNode>  que= new LinkedList<>(); 
que.offer(); 
que.poll(); 
que.peek();

//双端队列操作
//头部添加和尾部添加 
que.offerFirst(val);
que.offerLast(val);
//头部删除和尾部删除
que.pollFirst();
que.pollLast();
//​头部查询和尾部查询
que.peekFirst();
que.peekLast();

Stack

//标准栈操作
Stack<TreeNode> st= new Stack<>(); 
st.push(); 
st.pop(); 
st.peek()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网民工蒋大钊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值