Java学习-Stream流和异常

1 Stream流

1.1 Stream流概述

什么是Stream流?
在Java 8中,得益于Lambda所带来的函数式编程,引入了一一个全新的Stream流概念。
目的:用于简化集合和数组操作的API。
 

体验Stream流的作用
需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合, 存储多个字符串元素

        List<String> list = new ArrayList<>();
        list.add("张麻子"); 
        list.add("张三丰");
        list.add("马邦德"); 
        list.add("张三"); 
        list.add("张德发");

  • 把集合中所有 以"张"开头的元素存储到一个新的集合
  • 把"张"开头的集合中 的长度为3的元素存储到-个新的集合
  • 遍历上一步得到的集合中 的元素输出。
package com.itzw.d1_stream;

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

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张麻子");
        list.add("张三丰");
        list.add("马邦德");
        list.add("张德发");
        list.add("张三");
        System.out.println(list);

        //传统方法
        List<String> zhangList = new ArrayList<>();
        for (String s : list) {
            if (s.startsWith("张")){
                zhangList.add(s);
            }
        }
        System.out.println(zhangList);

        List<String> threeList = new ArrayList<>();
        for (String s : zhangList) {
            if (s.length() == 3){
                threeList.add(s);
            }
        }
        System.out.println(threeList);

        System.out.println("============使用stream流=============");
        //一行搞定
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
    }
}

1.2 Stream流的获取

Stream流的三类方法

  • 获取Stream流

        创建一条流水线, 并把数据放到流水线上准备进行操作

  • 中间方法

        流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。

  • 终结方法

        一个Stream流只能有- -个终结方法,是流水线上的最后一个操作

Stream操作集合或者数组的第-步是先得到Stream流,然后才能使用流的功能。


集合获取Stream流的方式

  • 可以使用Collection接口中的默认方法stream()生成流

default Stream<E> stream()获取当前集合对象的Stream流

数组获取Stream流的方式

public static <T> Stream<T> stream(T[] array)获取当前数组的Stream流
public static<T> Stream<T> of(T... values)获取当前数组/可变数据的Stream流

演示:

        //Collection集合获取Stream流
        Collection<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream();

        //Map集合获取Stream流
        Map<String,Integer> map = new HashMap<>();
        //键流
        Stream<String> keyStream = map.keySet().stream();
        //值流
        Stream<Integer> valueStream = map.values().stream();
        //键值对流
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        //数组获取Stream流
        String[] arr = {"张麻子","马邦德","黄四郎"};
        Stream<String> arrayStream1 = Arrays.stream(arr);
        Stream<String> arrayStream2 = Stream.of(arr);

1.3 Stream流常用API

Stream流的常用API(中间操作方法)

Stream<T> filter(Predicate<? super T> predicate)                 用于对流中的数据进行过滤。
Stream<T> limit(long maxSize)                                                获取前几个元素
Stream<T> skip(long n)                                                                跳过前几个元素
Stream<T> distinct()                                       去除流中重复的元素。依赖(hashCode和equals方法)
static <T> Stream<T> concat(Stream a, Stream b)                        合并a和b两个流为一个流

 演示:

package com.itzw.d1_stream;

import javax.crypto.spec.PSource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Test02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张麻子");
        list.add("张三丰");
        list.add("马邦德");
        list.add("张德发");
        list.add("张三");
        System.out.println(list);
        list.stream().filter(s -> s.startsWith("张"));

        //limit 获取前几个元素
        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));

        System.out.println("============================");
        //skip 跳过前几个元素
        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);

        System.out.println("=============================");
        //contact 合并两个流
        Stream<String> zhangStream = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> stream = Stream.of("嗨嗨嗨", "Java");
        Stream.concat(zhangStream,stream).forEach(s -> System.out.println(s));

        //给集合元素前面都加上一个“鹅城的”
        list.stream().map(s -> "鹅城的" + s).forEach(s -> System.out.println(s));

        System.out.println("=====================================");
        //把所有的名称都加工成一个学生对象
        list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));

    }
}


class Student{
    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

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

注意:

  • 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
  • 在Stream流中无法直接修改集合、数组中的数据。
     

Stream流的常见终结操作方法
void forEach(Consumer action )对此流的每个元素执行遍历操作
long  count()返回此流中的元素数

注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
 

1.4 案例

需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。
分析:

  1. 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
  2. 开发一部有4个员工、开发二部有5名员工
  3. 分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
  4. 分别统计出2个部门的平均月收入,要求去掉最高和最低工资。
  5. 统计2个开发部门]整体的平均工资,去掉最低和最高工资的平均值。
     
package com.itzw.d1_stream;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Test03 {
    public static double allMoney = 0;
    public static double allMoney2 = 0;

    public static void main(String[] args) {
        List<Employee> one = new ArrayList<>();
        one.add(new Employee("诸葛亮",'男',50000,100000,"木鱼哦"));
        one.add(new Employee("关羽",'男',5000,10000,"木鱼哦"));
        one.add(new Employee("张飞",'男',5000,10000,"木鱼哦"));
        one.add(new Employee("孙尚香",'女',40000,90000,"木鱼哦"));

        List<Employee> two = new ArrayList<>();
        two.add(new Employee("吴用",'男',40000,20000,"木鱼哦"));
        two.add(new Employee("武松",'男',20000,20000,"木鱼哦"));
        two.add(new Employee("鲁智深",'男',33000,20000,"木鱼哦"));
        two.add(new Employee("林冲",'男',34200,20000,"木鱼哦"));
        two.add(new Employee("孙二娘",'女',32000,20000,"木鱼哦"));

        //开发一部最高工资的员工
        /*Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(),
                e2.getBonus() + e2.getSalary())).get();
        System.out.println(e);*/

        //将一部门最高工资员工放入优秀员工中
        Topperformer oneTopper = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() +
                e1.getSalary(), e2.getBonus() + e2.getSalary())).
                map(e -> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();

        System.out.println(oneTopper);

        //将二部门最高工资员工放入优秀员工中
        Topperformer twoTopper = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() +
                e1.getSalary(), e2.getBonus() + e2.getSalary())).
                map(e2 -> new Topperformer(e2.getName(), e2.getBonus() + e2.getSalary())).get();
        System.out.println(twoTopper);


        //统计平均工资,去掉最高最低工资
        one.stream().sorted((e1,e2) -> Double.compare(e1.getBonus()+e1.getSalary(),
                e2.getBonus()+e2.getSalary())).skip(1).limit(one.size()-2).forEach(e -> {
                    allMoney += e.getBonus()+e.getSalary();
        });//这里相当于一个方法,无法用main方法中的变量,所以要创建静态变量

        System.out.println("开发一部平均工资:" + allMoney/(one.size()-2));

        //统计两个部门的平均工资,去掉最高最低工资
        Stream<Employee> s1 = one.stream();
        Stream<Employee> s2 = two.stream();
        Stream<Employee> s3 = Stream.concat(s1, s2);
        s3.sorted((e1,e2) -> Double.compare(e1.getBonus()+e1.getSalary(),
                e2.getBonus()+e2.getSalary())).skip(1).limit(one.size()+two.size()-2).forEach(e -> {
            allMoney2 += e.getBonus()+e.getSalary();
        });
        //使用
        BigDecimal b1 = BigDecimal.valueOf(allMoney2);
        BigDecimal b2 = BigDecimal.valueOf(one.size()+two.size()-2);
        System.out.println("两个部门平均工资:" + b1.divide(b2,2, RoundingMode.HALF_UP));
        //System.out.println("两个部门平均工资:" + allMoney2/(one.size()+two.size()-2));

    }
}



class Employee{
    private String name;
    private char sex;
    private double salary;
    private double bonus;
    private String punish;

    public Employee(){
    }

    public Employee(String name, char sex, double salary, double bonus, String punish) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
        this.bonus = bonus;
        this.punish = punish;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public String getPunish() {
        return punish;
    }

    public void setPunish(String punish) {
        this.punish = punish;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", punish='" + punish + '\'' +
                '}';
    }
}

class Topperformer{
    private String name;
    private double bonus;

    public Topperformer(){
    }

    public Topperformer(String name, double bonus) {
        this.name = name;
        this.bonus = bonus;
    }

    public String getName() {
        return name;
    }

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

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

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

Stream流的收集方法
R collect(Collector collector)开始收集Stream流,指定收集器

Collectors工具类提供了具体的收集方式
public static <T> Collector tolist()                        把元素收集到List集合中
public static <T> Collector toSet()                        把元素收集到Set集合中
public static Collector toMap(Function keyMapper ,Function valueMapper)把元素收集到Map集合中

        List<String> list = new ArrayList<>();
        list.add("张麻子");
        list.add("张三丰");
        list.add("马邦德");
        list.add("张德发");
        list.add("张三");
        System.out.println(list);
        //转成集合
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        List<String> list1 = s1.collect(Collectors.toList());
        System.out.println(list1);

        //转成数组
        Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
        Object[] array = s2.toArray();
        System.out.println(Arrays.toString(array));

2 异常

2.1 概述

什么是异常?

  • 异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
  • 比如:数组索引 |越界、空指针异常、日期格式化异常, 等..

为什么要学习异常?

  • 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
  • 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全,健壮性。
     

2.2 常见异常

2.2.1 运行时异常

  • 直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。

运行时异常示例

  • 数组 索引越界异常: ArrayIndexOutOfBoundsException
  • 空指针异常: NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
  • 数学操作异常: ArithmeticException
  • 类型转换异常: ClassCastException
  • 数字转换异常 : NumberFormatException

运行时异常:一般是程序 员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,
 

        //1.数组索引越界异常:ArrayIndexOutOfBoundsException
        int[] a = {1,2,3};
        System.out.println(a[0]);
        System.out.println(a[1]);
        System.out.println(a[2]);
        //System.out.println(a[3]);

        //2.空指针异常NullPointerException
        String s = null;
        System.out.println(s);
        //System.out.println(s.length());

        //3.数学操作异常 ArithmeticException
        //int i = 10 / 0;

        //4.类型转换异常ClassCastException
        Object c = "哈哈哈";
        int d = (int)c;


        //5.数字转换异常NumberFormatException
        //String ss = "123a";
        //Integer sss = Integer.valueOf(ss);
        //System.out.println(sss + 1);

2.2.2 编译时异常

  • 不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
     

编译时异常的作用是什么:

  • 是担心程序员的技术不行,在编译阶段就爆出一一个错误,目的在于提醒不要出错!
     

2.3 异常处理

2.3.1 默认处理

  1. 默认会在出现异常的代码那里自动的创建一个异 常对象: ArithmeticException。
  2. 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虛拟机。
  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
  4. 直接从当前执行的异常点干掉当前程序。
  5. 后续代码没有机会执行了,因为程序已经死亡。
        System.out.println("程序开始");
        divide(10,0);
        System.out.println("程序结束");
    }

    public static void divide(int a,int b){
        System.out.println(a);
        System.out.println(b);
        int c = a / b;
        System.out.println(c);
    }

程序遇到错误就会立刻终止。

 

2.3.2 throws

  • throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
  • 这种方式并不好, 发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。

 

 2.3.3 try...catch...

  • 监视捕获异常, 用在方法内部,可以将方法内部出现的异常直接捕获处理。这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。

  • 程序错误不会抛给虚拟机,仍然继续运行

  • 如有多个异常建议放在一起try

package com.itzw.d2_exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test04 {
    public static void main(String[] args){
        System.out.println("程序开始");
        parseTime("2022-8-27 16:30:30");
        System.out.println("程序结束");
    }

    public static void parseTime(String time){
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
            Date parse = sdf.parse(time);
            System.out.println(parse);
            InputStream is = new FileInputStream("D:/haihai");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 2.3.4 前两者结合 

  • 方法直接将异通过throws抛出去给调用者
  • 调用者收到异常后直接捕获处理。

 

 

2.4 案例

异常处理使代码更稳健的案例
需求

  • 键盘录入一个合理 的价格为止(必须是数值,值必须大于0)。

分析

  • 定义一个死循环,让用户不断的输入价格。
package com.itzw.d2_exception;

import java.util.Scanner;

public class Test06 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (true){
            try {
                System.out.println("请输入一个价格:");
                String s = sc.nextLine();
                Double price = Double.valueOf(s);
                if (price > 0){
                    System.out.println("输入完成!");
                    break;
                }else {
                    System.out.println("输入有误,请输入正数!");
                }
            } catch (Exception e) {
                System.out.println("您输入的数字有毛病,请重新输入!!");
            }
        }
    }
}

2.5 自定义异常

自定义异常的必要?

  • Java无法为这个世界上全部的问题提供异常类。
  • 如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。

自定义异常的好处:

  • 可以使用异常的机制管理业务问题,如提醒程序员注意。
  • 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。

2.5.1 自定义编译时异常

  • 定义一个异 常类继承Exception.

  • 重写 构造器。

  • 在出现异常的地方用thrownew自定义对象抛出,

作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理! !

自定义编译时异常

public class ItZwAgeException extends Exception{
    public ItZwAgeException() {
    }

    public ItZwAgeException(String message) {
        super(message);
    }

}

自定义异常的使用

package com.itzw.d3_myselfexception;

public class ExceptionTest {
    public static void main(String[] args) {
        try {
            checkAge(-12);
        } catch (ItZwAgeException e) {
            e.printStackTrace();
        }
    }

    public static void checkAge(int age) throws ItZwAgeException {
        if (age < 0 || age > 200){
            throw new ItZwAgeException(age + " is xiagao");
        }else {
            System.out.println("年龄合法");
        }

    }
}

 

2.5.2 自定义运行时异常

  • 定义一个异 常类继承RuntimeException.
  • 重写构造器。
  • 在出现异常 的地方用throw new自定义对象抛出!

作用:提醒不强烈,编译阶段不报错!N运行时才可能出现! !
 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 8的stream)操作是一种新的API,提供了一种简单而有效的方法来处理集合和数组等数据类型。操作可以让我们以一种更加声明式的方式来处理数据,从而提高代码的可读性和可维护性。 Java 8中的操作具有以下特点: 1. 集成了过滤、映射、排序、聚合等操作; 2. 支持并行处理,提高了运行效率; 3. 可以避免空指针异常等常见问题; 4. 支持延迟计算,节省了资源。 下面是使用Java 8的操作的一些示例: 1. 过滤操作: ``` List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> result = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); ``` 上面的代码使用了stream()方法将List转换成,使用filter()方法过滤出偶数,最后使用collect()方法将结果转换成List。 2. 映射操作: ``` List<String> words = Arrays.asList("hello", "world"); List<Integer> result = words.stream() .map(String::length) .collect(Collectors.toList()); ``` 上面的代码使用了map()方法将List中的字符串转换成它们的长度,最后使用collect()方法将结果转换成List。 3. 排序操作: ``` List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2); List<Integer> result = numbers.stream() .sorted() .collect(Collectors.toList()); ``` 上面的代码使用了sorted()方法将List中的数字按升序排序,最后使用collect()方法将结果转换成List。 4. 聚合操作: ``` List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> result = numbers.stream() .reduce((a, b) -> a + b); ``` 上面的代码使用了reduce()方法将List中的数字相加,最后返回一个Optional对象,其值为15。 在实际开发中,Java 8的操作可以大大简化代码,提高开发效率。但是需要注意的是,在处理大量数据时,操作可能会对性能产生影响,因此需要根据实际情况选择合适的方法。 ### 回答2: Java 8引入了stream)操作,它是一种用于处理集合数据的新方式。 操作主要通过对集合进行一系列的处理操作来实现数据的筛选、转换、聚合等功能。与传统的集合操作相比,操作具有更加简洁、灵活和高效的特点。下面是一些常用的操作: 1. 过滤(filter):根据指定条件从中筛选出符合条件的元素。 2. 映射(map):通过对中的每个元素应用指定的函数来生成一个新的,可以对元素进行转换或提取。 3. 排序(sorted):对中的元素进行排序,可以根据自然顺序或自定义的排序规则进行排序。 4. 限制(limit):截断,获取指定数量的元素。 5. 跳过(skip):跳过指定数量的元素。 6. 匹配(matching):用于检查中的元素是否满足指定的条件。 7. 查找(finding):在中查找符合指定条件的元素。 8. 聚合(reducing):将中的元素使用指定的操作进行聚合,可以得到最大值、最小值、求和等结果。 9. 收集(collecting):将中的元素收集到一个集合中,可以使用预定义的收集器或自定义的收集器。 使用操作可以大大简化代码,并且使得操作更为直观和易于理解。它提供了丰富的功能,可以处理各种不同类型的集合数据。同时,操作的使用还可以减少中间变量的使用,提高代码的性能。 综上所述,Java 8的操作是一种强大的工具,可以帮助我们对集合数据进行高效的处理,提升代码的简洁性和可读性。它是现代化Java编程中不可或缺的一部分。 ### 回答3: Java 8 引入了stream)操作,它是一种新的处理数据集合的方式。操作可以让我们以更简洁、清晰和灵活的方式处理数据。 首先,是一组有序的元素,它可以来自各种数据源,比如数组、集合或者I/O通道。操作分为两种类型:中间操作和终端操作。中间操作可以对进行转换或者过滤,但并不产生最终结果。而终端操作会触发的处理,并产生一个结果或副作用。 通过操作,我们可以进行各种常见的数据处理操作。例如,我们可以使用 filter 方法来过滤中的元素,只保留符合条件的元素。可以使用 map 方法来对中的元素进行映射,生成一个新的。可以使用 reduce 方法将中的所有元素聚合起来并生成一个结果。 此外,操作还支持并行处理。通过将转换为并行,我们可以同时对多个元素进行处理,提高处理效率。但是需要注意的是,并不是所有的操作都适合并行处理,有些操作在并行处理时可能会导致结果不确定或出错。 总之,Java 8 的操作可以让我们以更简洁、灵活的方式处理数据集合。它提供了丰富的中间操作和终端操作,支持串行和并行处理。通过学习和掌握操作,我们可以更高效地处理数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值