Java-Stream、File、方法递归、字符集

目录

1.Stream流

1.1 概括

 1.2 集合获取Stream流

 1.3 Stream流的常用方法

 1.4 Stream流的终结方法

 2.File、IO流

2.1 概述

2.2 File创建文件对象

2.3 File判断方法

 2.4 File的创建和删除

2.5 File的遍历

 3.方法递归

3.1 概述

3.2 递归算法

3.3 递归案例

3.3 文件搜索

4.字符集

4.1 常见字符集介绍

4.2 字符集的编码、解码操作

5.作业

5.1 打印文件目录

5.2 计算整个文件夹的大小


1.Stream流

1.1 概括

package com.itheima.d1_stream;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest1 {
    public static void main(String[] args) {
        // 目标:体验Stream流的使用
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        // 1、拿出姓张的放到新集合中去。
//        List<String> newList = new ArrayList<>();
//        for (String s : list) {
//            if(s.startsWith("张") && s.length() == 3) {
//                newList.add(s);
//            }
//        }
//        System.out.println(newList);
        
        // 2、体验Stream流
        List<String> newList = list.stream().filter(s -> s.startsWith("张") && s.length() == 3).collect(Collectors.toList());
        System.out.println(newList);
    }
}

 1.2 集合获取Stream流

package com.itheima.d1_stream;

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

/**
 *
 */
public class StreamTest2 {
    public static void main(String[] args) {
        // 目标:获取Stream流。
        // 1、获取集合的Stream流:  default Stream<E> stream()
        Collection<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若","赵敏","张强","张三丰");
        Stream<String> listS = list.stream();

        // 2、获取Map集合的Stream流
        Map<String,Integer> map = new HashMap<>();
        // a、获取键流
        Stream<String> keyS = map.keySet().stream();
        // b、获取值流
        Stream<Integer> valueS = map.values().stream();
        // c、获取键值对流.
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        // 3、获取数组的Stream
        String[] names = {"赵敏", "王菲", "小昭"};
        Stream<String> stream1 = Arrays.stream(names);
        Stream<String> stream2 = Stream.of(names);
    }
}

 1.3 Stream流的常用方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest3 {
    public static void main(String[] args) {
        // 目标:掌握Stream流的常见方法。
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若","赵敏","张强","张三丰");

        // 1、过滤方法
        list.stream().filter(s ->  s.startsWith("张") ).forEach(s -> System.out.println(s));

        // 2、准备一个集合,排序。
        List<Movie> movies = new ArrayList<>();
        movies.add(new Movie("摔跤吧,爸爸", 9.5, "阿米尔汗"));
        movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies.add(new Movie("阿甘正传", 7.5, "汤姆汉克斯"));

        // 集合中如果存储对象,方式一:对象类可以实现Comparable接口,指定比较规则
        // sorted方法就可以按照规则进行排序,否则报错!
        movies.stream().sorted().forEach(s -> System.out.println(s));
        System.out.println("-----------------------------------------------");
        movies.stream().sorted((m1, m2) -> Double.compare(m2.getScore(), m1.getScore()))
                .forEach(System.out::println);

        // 3、limit取前几个
        System.out.println("-----------------------------------------------");
        movies.stream().sorted((m1, m2) -> Double.compare(m2.getScore(), m1.getScore()))
                .limit(2).forEach(System.out::println);

        // 4、跳过前几个skip
        System.out.println("-----------------------------------------------");
        movies.stream().sorted((m1, m2) -> Double.compare(m2.getScore(), m1.getScore()))
                .skip(2).forEach(System.out::println);

        // 5、distinct去重复
        System.out.println("-----------------------------------------------");
        // 去重复:需要重写对象类的hashCode和equals方法
        movies.stream().sorted((m1, m2) -> Double.compare(m2.getScore(), m1.getScore()))
                .distinct().forEach(System.out::println);

        // 6、map加工方法:把流上的数据加工成新数据。
        System.out.println("-----------------------------------------------");
        movies.stream().map(m -> {
            m.setScore(m.getScore() + 2);
            return m;
        }).forEach(System.out::println);

        //接6
        System.out.println("============================");
        movies.stream().map(m -> m.getName() + "=>" + m.getScore()).forEach(System.out::println);
        // 7、合并流。
        // 把两个流接起来 。
        Stream<String> s1 = Stream.of("张三", "楚留香", "西门吹牛");
        Stream<String> s2 = Stream.of("李四", "石观音");
        //如果要合并的类型是猫和狗,那么合并流的类型就是Animal
        Stream<String> allStream = Stream.count(s1, s2);
        long count = allStream.count(); // count:统计个数
        System.out.println(count);
    }
}

在 Java 中,Stream.count()方法返回的是long类型,而不是int类型。这是因为流中的元素数量可能非常大,超出了int的表示范围(即Integer.MAX_VALUE,约为 21 亿)。

1.4 Stream流的终结方法

package com.itheima.d1_stream;

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

public class StreamTest4 {
    public static void main(String[] args) {
        // 目标:Stream流的常见终结方法。
        List<Movie> movies = new ArrayList<>();
        movies.add(new Movie("摔跤吧,爸爸", 9.5, "阿米尔汗"));
        movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies.add(new Movie("阿甘正传", 7.5, "汤姆汉克斯"));

        // 1、forEach
        movies.stream().forEach(System.out::println);
        // 2、count
        long count = movies.stream().skip(2).count();
        System.out.println(count);

        // 3、取最大值
        Movie movie = movies.stream().max((m1, m2) -> Double.compare(m1.getScore(), m2.getScore())).get();
        System.out.println(movie);

        Movie m = movies.stream().max((m1,m2) -> Double.compare(m1.getScore(),m2.getScore())).get();
        System.out.println(m);

        Movie movie2 = movies.stream().min((m1, m2) -> Double.compare(m1.getScore(), m2.getScore())).get();
        System.out.println(movie2);

        // 4、收集Stream流:把流中的数据恢复到集合或者数组中去。
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        // 收集到List集合
        Stream<String> stream = list.stream();//流只能用一次
        //也在后面用.toList() 但是JDK11才开始支持,在大部分公司还是需要collect(Collectors.toList())
        List<String> newList = stream.filter(s -> s.startsWith("张")).collect(Collectors.toList());
        System.out.println(newList);
        newList.add("张麻子");
        System.out.println(newList);

        System.out.println("---------------------------------------");
        // 收集到Set集合
        Stream<String> stream1 = list.stream();
        Set<String> set = stream1.collect(Collectors.toSet());
        System.out.println(set);

        // 收集到数组
        System.out.println("---------------------------------------");
        Object[] names3 = list.stream().filter(s -> s.length() == 3).toArray();
        System.out.println(Arrays.toString(names3));

        // 拓展
//        String[] names4 = list.stream().filter(s -> s.length() == 3).toArray(String[]::new);
//        System.out.println(Arrays.toString(names4));

        System.out.println("---------------------------------------");
        // 收到到Map集合。
        List<Movie> movies1 = new ArrayList<>();
        movies1.add(new Movie("摔跤吧,爸爸", 9.5, "阿米尔汗"));
        movies1.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies1.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
        movies1.add(new Movie("阿甘正传", 7.5, "汤姆汉克斯"));

        //取出流为Map时,如果有相同的键和值,要给出提示应该选择哪一个(v1,v2) -> v2
        Map<String, Double> map = movies1.stream().limit(3).collect(Collectors.toMap(m1 -> m1.getName()
                , m2 -> m2.getScore(), (v1,v2) -> v2));
        System.out.println(map);
    }
}

 2.File、IO流

2.1 概述

目前我们已经学过的存储数据的方案有哪些呢?

但是他们都有一个问题,他们都是内存中的数据容器,但是内存条所记住的数据,在断电或者程序终止时会丢失,不会长期存储。

 File:代表文本

IO流:读写数据

2.2 File创建文件对象

File file = new File("文件 / 文件夹 / 绝对路径 / 相对路径 / 不存在文件")

绝对路径是带盘符的

相对路径是不带盘符的,默认到当前工程下寻找文件

2.3 File判断方法

package com.itheima.d2_file;

import java.io.File;
import java.text.SimpleDateFormat;

/**
     目标:掌握File提供的判断文件类型、获取文件信息功能
 */
public class FileTest2 {
    public static void main(String[] args) {
        // 1.创建文件对象,指代某个文件
        // File f1 = new File("E:/resource/meinv.jpg");
        //File f1 = new File("day08-stream-file-charset\\src\\dlei01.txt");
        File f1 = new File("E:/resource/meinv.jpg");

        // 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
        System.out.println(f1.exists());

        // 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之。
        System.out.println(f1.isFile());

        // 4、public boolean isDirectory()  : 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
        System.out.println(f1.isDirectory());

        // 5.public String getName():获取文件的名称(包含后缀)
        System.out.println(f1.getName()); // meinv.jpg

        // 6.public long length():获取文件的大小,返回字节个数
        System.out.println(f1.length());

        // 7.public long lastModified():获取文件的最后修改时间。
        long time = f1.lastModified();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");
        System.out.println(sdf.format(time));

        // 8.public String getPath():获取创建文件对象时,使用的路径
        System.out.println(f1.getPath());

        // 9.public String getAbsolutePath():获取绝对路径
        System.out.println(f1.getAbsolutePath());
        
        //找到真正的相对路径相对到哪里
        File file = new File("");
        System.out.println(file.getAbsoluteFile());
    }
}

 2.4 File的创建和删除

package com.itheima.d2_file;

import java.io.File;

/**
 * 目标:掌握File创建和删除文件相关的方法。
 */
public class FileTest3 {
    public static void main(String[] args) throws Exception {
        // 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
        File f = new File("day08-stream-file-charset/src/dlei02.txt");
        System.out.println(f.createNewFile());

        // 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
        File f2 = new File("E:/resource/aaa2");
        System.out.println(f2.mkdir()); //true

        // 3、public boolean mkdirs()(重点):用于创建文件夹,注意:可以创建多级文件夹
        File f3 = new File("E:/resource/aaa3/bb/cc/dd/ee/ff/gg");
        System.out.println(f3.mkdirs()); //true

        // 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
        System.out.println(f.delete()); // 删除文件,即便文件中有内容一样被删除。

        // File f4 = new File("E:/resource"); // 不能删除非空文件夹。
        // System.out.println(f4.delete());

        File f4 = new File("E:/resource/aaa2"); // 只能删除空文件夹。
        System.out.println(f4.delete());
    }
}

2.5 File的遍历

package com.itheima.d2_file;

import java.io.File;
import java.util.Arrays;

/**
 * 目标:掌握File提供的遍历文件夹的方法。
 */
public class FileTest4 {
    public static void main(String[] args) {
        File f = new File("E:\\磊哥面授\\Java入门至大牛课程");
        // 1、public String[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
        String[] names = f.list();
        for (String name : names) {
            System.out.println(name);
        }

        // 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }

    }
}

 3.方法递归

3.1 概述

什么是递归方法?

  • 递归是一种算法,在程序设计语言中广泛应用
  • 从形式上说:方法调用自身的形式称为方法递归

递归的形式

  • 直接递归:方法自己调自己
  • 简介递归:方法调用其他方法,其他方法又回调方法自己
package com.itheima.d3_recursion;

public class RecursionTest1 {
    public static void main(String[] args) {
        // 目标:认识递归的形式。
        test1();//直接递归
        test2();//间接递归
    }

    public static void test2(){
        System.out.println("==test2==");
        test3();
    }

    public static void test3() {
        System.out.println("==test3==");
        test2(); // 间接递归:
    }

    public static void test1(){
        System.out.println("==test1==");
        test1(); // 直接递归:自己调用自己
    }
}

 3.2 递归算法

package com.itheima.d3_recursion;

public class RecursionTest2 {
    public static void main(String[] args) {
        // 目标:递归的算法基础:解决求阶乘。
        System.out.println("1-5的阶乘:" + f(5));
    }

    public static int f(int n){
        if (n == 1) {
            return 1;
        }
        return f(n-1) * n;
    }

}

 环环相扣,一个结果确定之后当次调用方法就出栈

递归算法三要素:

  • 递归的公式:f (n) = f(n - 1) * n;
  • 递归的终结点:f(1)
  • 递归的方向必须走向终结点: f(5) = f(4) * 5

                                                        f(4) = f(3) * 2

                                                        f(3) = f(2) * 3

                                                        f(2) = f(1) * 2

                                                        f(1) = 1

方向一定要有终结点

3.3 递归案例

package com.itheima.d3_recursion;

public class RecursionTest4 {
    public static void main(String[] args) {
        // 目标:猴子吃桃问题、
        //终结点:f(10) = 1
        //f(n + 1) = f(n) - f(n) / 2 -1
        //变形:2f(n + 1) = 2f(n) - f(n) - 2
        //2f(n + 1) = f(n) - 2
        //f(n) = 2f(n+ 1) + 2
        //递归的方向要朝着终结点走
        //递归的方向 :f(1) = ?
        System.out.println(f(1));
        System.out.println(f(2));
        System.out.println(f(3));
    }

    public static int f(int n){
        if (n == 10){
            return 1;
        }
        return 2 * f(n + 1) + 2;
    }
}

但并不是所有的递归都会有公式,比如我们下面要学习的文件搜索,要根据用户的线性思维来写代码

3.3 文件搜索

package com.itheima.d3_recursion;

import java.io.File;

public class FileSearchTest5 {
    public static void main(String[] args) throws Exception {
        // 目标:文件搜索。
        File dir = new File("D:/");
        searchFile(dir, "QQ.exe");
    }

    /**
     * 文件搜索
     * @param dir  传需要查询的目录
     * @param fileName  传需要查询的文件名
     * 用户思维
     */
    public static void searchFile(File dir, String fileName) throws Exception {
        // 1、极端情况判断。
        if(dir == null || fileName == null || !dir.exists() || dir.isFile()) {
            return; // 不搜索的情况
        }

        // 2、拿当前目录下的全部一级文件对象。
        File[] files = dir.listFiles();
        // 判断是否可以获取一级文件对象,且一级文件对象的个数是的大于0,存在才可以搜索
        if( files != null && files.length > 0) {
             // 3、遍历当前全部一级文件对象
            for (File f : files) {
                // 4、判断f对应的文件对象是文件,还是文件夹
                if(f.isFile()) {
                    //是文件
                    //5.看这个文件是不是我们要找的
                    if(f.getName().contains(fileName)) {
                        System.out.println(f.getAbsolutePath());

                        // 找到并启动!
                        Runtime r = Runtime.getRuntime();
                        r.exec(f.getAbsolutePath());
                    }
                }else {
                    // 6、是文件夹,递归进入继续寻找
                    searchFile(f, fileName);
                }
            }
        }
    }
}

学到这里,我们会发现File只能操作文件本身,并没有办法读取文件内容,这就需要用到 IO 流l来读取文件内容数据,但是在学习 IO 流之前,我们先学一点前置知识:字符集,提前学会数据在计算机内是怎么存储的。

4.字符集

4.1 常见字符集介绍

标准ASCII字符集:

  • ASCII:美国信息交换标准代码,包括了英文、符号等
  • 标准ASCII使用1个字节存储一个字符,首位是0,总共可表示123个字符,对美国老来说完全够用

'0' -> 48

'A' -> 65

'a' -> 97

中国编码(GBK编码集(汉字内码扩展规范,国标)):

 Unicode字符集(统一码):基本不用,有些专业领域才用

 UTF - 8(被全世界所采用):技术人员在开发时都应该使用UTF-8编码

4.2 字符集的编码、解码操作

package com.itheima.d4_cjarset;

import java.util.Arrays;

public class CharSetDemo1 {
    public static void main(String[] args) throws Exception {
        //目标:掌握字符的编码和解码
        String info = "abc我在黑马听磊哥吹nb!";

        //1.编码成字节
        byte[] bytes = info.getBytes();//默认用平台编码UTF-8编码
        System.out.println(Arrays.toString(bytes));

        byte[] bytes2 = info.getBytes("GBK");//默认用平台编码UTF-8编码
        System.out.println(Arrays.toString(bytes2));

        //2.解码成字符
        String rs1 = new String(bytes);
        System.out.println(rs1);

        String rs2 = new String(bytes2,"GBK");
        System.out.println(rs2);
    }
}

5.作业

5.1 打印文件目录

windows资源管理器中会展示目录中的内容结构,但是java并没有提供展示目录结构的操作,本案例完成按照指定格式打印文件夹的目录结构(包含子文件夹和文件),效果如下图:

package work.Test2;

import java.io.File;

public class test2 {
    public static void main(String[] args) {
        //创建要遍历的文件夹对象
        File file = new File("...");
        printDir(file,0);
    }

    //定义printFileName方法,负责格式化输出文件或文件夹名称,使用制表符进行缩进,形成层级结构
    public static void printFileName(int num, File file) {
        //打印num个制表符,用于缩进
        for (int i = 0; i < num; i++) {
            //错误:已经使用了制表符"\t",所以这里不能再使用println换行,要用print
            System.out.print("\t");
        }
        //打印文件或文件夹的名称,格式为 |-名称
        System.out.println("|-" + file.getName());
    }

    //定义printDir方法逐层递进打印文件夹
    public static void printDir(File dir,int num) {
        //打印当前文件夹的名称
        printFileName(num,dir);
        num++;

        //获取当前文件夹中的所有文件和文件夹
        //(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回
        File[] files = dir.listFiles();

        //如果文件夹为空或无法访问,直接返回
        //files == null代表 1.文件指向一个非文件夹 2.无权限访问 3.路径不存在
        //files.length == 0代表 文件夹存在但为空
        if (files == null || files.length == 0) {
            return;
        }

        //遍历所有的文件和子文件夹
        for (File file : files) {
            if (file.isFile()){
                //如果是文件,直接调用printFile()方法打印文件名
                printFileName(num,file);
            } else if (file.isDirectory()) {
                //如果是文件夹,递归调用printDir方法
                printDir(file,num);
            }
        }
    }
}

 在这个目录结构打印程序中,递归的出口(终止条件)是通过一下语句实现的:

//如果文件夹为空或无法访问,直接返回
        //files == null代表 1.文件指向一个非文件夹 2.无权限访问 3.路径不存在
        //files.length == 0代表 文件夹存在但为空
        if (files == null || files.length == 0) {
            return;
        }

这两个条件是必要的,如果没有这些出口条件,当遇到空文件夹或者无效路径时,程序还会继续调用自身,导致栈溢出错误(StackOverflowError),作为整个程序的终止条件防止无限递归。

在这个案例中,只有一个递归终结条件,而遍历所有文件和子文件夹的循环结束后没有return语句,这是因为此方法会自然结束,当文件夹是非空并且可访问时,遍历完所有的子项后,方法自然结束(无显式return),另外一个原因就是因为此方法是void类型,无需返回结果,可以与下一个案例进行对比,实际上此处也可以添加return,但是没有必要,不会影响程序行为。

5.2 计算整个文件夹的大小

在windows操作系统中可以查看文件夹的大小,直接右键文件夹/属性,如下图: 但是在java中没有提供直接查看文件夹大小的操作,请使用File类和IO流的相关知识,计算文件夹的大小。 要求通过键盘录入文件夹路径(必须保证该文件夹路径是存在的)

package work.Test3;

import java.io.File;

public class test3 {
    public static void main(String[] args) {
        File dir = new File("D:\\【黑马】2024年11月AI版Java全栈开发V15课程\\配套资料\\02阶段:java基础进阶\\java基础进阶\\day08-Stream、File、方法递归");
        long length = getSize(dir);
        System.out.println("大小:" + length + "字节");
    }

    //定义获取文件夹大小的方法
    public static long getSize(File dir) {
        //1.初始化size变量用于累加文件大小
        long size = 0;

        //2.获取当前文件夹下的所有文件和子文件夹
        File[] files = dir.listFiles();

        //3.处理边界情况/定义递归终结条件:文件夹不存在、不可访问或为空
        if (files == null || files.length == 0) {
            return size;
        }

        //4.遍历当前文件夹下的所有项
        for (File file : files) {
            if (file.isFile()) {
                //5.如果是文件,直接累加其大小到size中
                size += file.length();
            } else if (file.isDirectory()) {
                //6.如果是文件夹,递归计算其大小并累加
                size += getSize(file);
            }
        }

        //7.返回累加的总大小
        return size;
    }

}

 这个案例的递归终结条件是:

//3.处理边界情况/定义递归终结条件:文件夹不存在、不可访问或为空
        if (files == null || files.length == 0) {
            return size;
        }

 这两个条件共同确保递归在遇到无法继续处理的情况时安全终止,并正确返回结果size,避免无限递归和错误计算。

计算文件夹大小为什么会有多次return?

这是因为方法返回值类型为long,必须通过return size返回计算结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值