JavaSE 进阶 (8) Stream

Stream流-初体验

Stream 流:专门用来快速处理集合或数组的一个工具


体验Stream流

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以 “张” 开头的元素存储到一个新的集合
  • 把 “张” 开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合

传统方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args){
	//集合的批量添加
	ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子", "谢广坤"));
	
	//遍历list1,把以张开头的元素添加到1ist2中。
	ArrayList<String> list2 = new ArrayList<>();
	for (String s : list1){
		if(s.startswith("张")){
			list2.add(s);
		}
	}

	//遍历list2集合,把其中长度为3的元素,再添加到1ist3中。
	ArrayList<String> list3 = new ArrayList<>();
	for (String s : list2){
		if(s.length() == 3){
			list3.add(s);
		}
	}
	
	//遍历list3集合,输出打印
	for(String s : list3){
		System.out.println(s);
	}
}

Stream流的方式写

1
2
3
list1.stream().filter(s -> s.startswith("张"))
		.filter(s -> s.length() == 3)
		forEach(s -> System.out.println(s));

Stream流-思想特点

Stream流思想

假设有流水线如下

图1

Stream流的三种方法

图2
  1. 获取 Stream 流:创建一条流水线,并把数据放到流水线上准备进行操作
  2. 中间方法(流水线上的操作):一次操作完毕之后,还可以继续进行其他操作
  3. 终结方法(流水线上的最后一个操作):一个 Stream 流只能有一个终结方法

Stream流-获取方法

Stream流的获取方法

单列集合

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

双列集合

  • 间接的生成流
  • 可以先通过 keySet 或者 entrySet 获取一个 Set 集合,再获取 Stream 流

数组

  • Arrays 中的静态方法 stream 生成流

同种数据类型的多个数据

  • 1, 2, 3, 4, 5…..
  • “aaa”, ”bbb”, ”ccc”……
  • 使用Stream.of(T…values)生成流

单列集合

1
2
3
4
5
6
7
8
9
public static void main(String[] args){
	//单列集合
	ArrayList<String> list = new ArrayList<>();
	list.add("aaa");
	list.add("bbb");
	list.add("ccc");
	Stream<String> stream = list.stream();
	stream.forEach(s -> System.out.println(s));
}

双列集合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args){
	HashMap<String,Integer> hm = new HashMap<>();
	hm.put("zhangsan", 23);
	hm.put("lisi", 24);
	hm.put("wangwu", 25);
	hm.put("zhaoliu", 26);
	hm.put("qianqi", 27);

	//双列集合不能直接获取Stream流
	//keyset
	//先获取到所有的键
	//再把这个Set集合中所有的键放到stream流中
	hm.keyset().stream().forEach(s -> system.out.println(s)); //zhangsan,lisi,wangwu,zhaoliu,qianqi

	//entryset
	//先获取到所有的键值对对象
	//再把这个Set集合中所有的键值对对象放到Stream流中
	hm.entryset().stream().forEach(s -> System.out.println(s));//zhangsan=23,lisi=24......
}

hm.values().stream() //获取 map 集合中值的 stream 流


数组

1
2
3
4
5
6
7
8
9
public static void main(String[] args){
	int[] arr = {1, 2, 3, 4, 5};
	Arrays.stream(arr).forEach(new IntConsumer(){
		@Override
		public void accept (int value){
			System.out.println(value);
		}
	});
}

同种类型的多个数据

1
2
3
4
5
6
7
8
public static void main(String[] args){
	Stream.of (1, 2, 3, 4, 5, 6, 7, 8, 9).forEach (new Consumer<Integer>(){
		@Override
		public void accept(Integer integer){
			System.out.println(integer);
		}
	});
}

小结

Stream流的获取

  1. 单列集合:集合对象.stream();
  2. 双列集合:不能直接获取,需要间接获取
  • 集合对象.keySet().stream();
  • 集合对象.entrySet().stream();
  1. 数组:Arrays.stream(数组名);
  2. 同种数据类型的多个数据:Stream.of(数据1,数据2,数据3……);

中间方法-filter

Stream 流的常见中间操作方法

Stream filter (Predicate predicate): 用于对流中的数据进行过滤

  • Predicate 接口中的方法
  • Boolean test(T t):对给定的参数进行判断,返回一个布尔值
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//filter方法获取流中的每一个数据
//而test方法中的s,就依次表示流中的每一个数据。
//我们只要在test方法中对s进行判断就可以了,
//如果判断的结果为true,则当前的数据留下
//如果判断的结果为false,则当前数据就不要。
list.stream().filter(
	new Predicate<string>(){
		@Override
		public boolean test(string s){
			boolean result = s.startsWith("张");
			return result;
		}
	}
)
.forEach(s -> System.out.println(s));

list.stream().filter(s -> s.startWith("张")).forEach(s -> System.out.println(s));

其他常用中间方法

Stream 流的常见中间操作方法

  • Stream limit (long maxSize):截取指定参数个数的数据,例如:输入3,代表截取前三个
  • Stream skip (long n):跳过指定参数个数的数据
  • static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
  • Stream distinct():去除流中重复的元素(依赖hashCode和equals方法)
  • public Stream map(Function mapper):把当前Stream流转换为其他类型的Stream流

concat

concat 是静态方法,可以用 类名.concat 来调用

1
2
3
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list2.stream();
Stream<String> stream3 = Stream.concat(stream1, stream2);

findFirst()

Optional optional = Stream()…….findFirst()

流 findFirst() 返回描述此流的第一个元素的 Optional (一个容器对象,可能包含或可能不包含,非 null 值);如果该流为空,则返回空的 Optional。


1
Optional<String> first = list.stream().findFirst();

findFirst() 会得到 Optional 类,然后 Optional 类可以调用 isPresent() 方法和 get() 方法

方法名 描述
isPresent() 确定此 Optional 实例中是否存在值
get() 用于获取此 Optional 实例的值

isPresent()

Java 中的 java.util.Optional 类的 isPresent() 方法用于确定此 Optional 实例中是否存在值。如果此 Optional 实例中不存在任何值,则此方法返回 false,否则返回 true。

  • 用法: public boolean isPresent()
  • 参数:此方法不接受任何参数。
  • 返回值:此方法返回一个布尔值,该布尔值说明此 Optional 实例中是否存在一个值。

get()

Java 中的 java.util.Optional 类的 get() 方法用于获取此 Optional 实例的值。如果此 Optional 实例中不存在任何值,则此方法将引发 NullPointerException。

  • 用法: public T get()
  • 参数:此方法不接受任何参数。
  • 返回值:此方法返回 Optional 类的实例的值。
  • 异常:如果此 Optional 实例中不存在任何值,则此方法将引发 NoSuchElementExcpetion。

Strean流的串行和并行

  • 并行流:List.parallelStream()
  • 串行流(顺序流):List.stream()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.*;  
import java.util.concurrent.atomic.AtomicInteger;  
import java.util.stream.Collectors;  
  
public class StreamExample {  
    public static void main(String[] args) {  
        List<Integer> ints = Arrays.asList(50, 10, 2, 40, 9, 8);  
  
        // 使用顺序流计算列表中所有数字的总和  
        long sumSequential = ints.stream()  
                .mapToLong(n -> n)  
                .sum();  
  
        System.out.println("Sum using sequential stream: " + sumSequential);  
  
        // 使用并行流计算列表中所有数字的总和  
        long sumParallel = ints.parallelStream()  
                .mapToLong(n -> n)  
                .sum();  
  
        System.out.println("Sum using parallel stream: " + sumParallel);  
    }  
}

mapToLong:将非长整型的流(如 IntStream/Stream) 中的每个元素,通过指定的映射函数(ToLongFunction)转换为 long 类型,最终返回一个基本类型的 LongStream(而非包装类型的 Stream)。

值得注意的是,不是所有的流操作都可以有效地并行化。有些操作(如 findFirst() 或 anyMatch())在并行流上的性能可能并不比顺序流好,因为它们通常需要在找到结果后立即停止处理。


map、flatMap区别

  1. map:对流中每一个元素进行处理
  2. flatMap:流扁平化,让你把一个流中的 “每个值” 都换成另一个流,然后把所有的流连接起来成为一个流

总结:map 是对一级元素进行操作,flatmap 是对二级元素操作。

本质区别:map 返回一个值;fltmap 返回一个流,多个值,

应用场景:map 对集合中每个元素加工,返回加工后结果;flatmap 对集合中每个元素加工后,做扁平化处理后(拆分层级,放到同一层)然后返回


对流中的数据进行排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//字母排序
List<String> string1 = Arrays.asList("abc", "bc", "bcf", "efg", "abcd", "bca", "jkl", "jkl");
List<String> sorted = string1.stream().sorted().collect(Collectors.toList());

//数字排序
List<Integet> ints = Arrays.asList(50, 10, 2, 40, -9, 8);
List<Integet> collect = ints.stream().sorted().collect(Collectors.toList());

//汉字排序
List<String> strings2 = Arrays.asList("张三", "李四", "王五", "赵柳", "张哥", "李哥", "王哥");
List<String> collect1 = strings2.stream().sorted(Collections.reverseOrder(Collator.getInstance(Locale.CHINA))).collect(Collectors.toList());
//Collections.reverseOrder(Collator.getInstance(Locale.CHINA)) 的意思就是,通过中国地域设置的 Collator 实例来创建一个逆序比较器。这个比较器会按照中国地域设置的规则,对字符串进行逆序比较和排序。

//根据比较器指定的规则排序
List<Teacher> l = new ArrayList<>();
l.add(new Teacher(14,"zs"));
l.add(new Teacher(11,"ww"));
l.add(new Teacher(24,"ls"));
l.add(new Teacher(24,"ks"));
List<Teacher> collect = l.stream().sorted((a, b) -> {
    int i = a.getAge() - b.getAge();
    i = i == 0 ? a.getName().compareTo(b.getName()) : i;
    return i;
}).collect(Collectors.toList());

stream中有两个sorted方法:

  • Stream sorted():根据元素的自然顺序排序
  • Stream sorted(Comparator<? super T> comparator):根据比较器指定的规则排序

Stream流-终结方法

Stream流的常见终结操作方法

  1. void foreach (Consumer action):对此流的每个元素执行操作

Consumer接口中的方法,void accept(T t):对给定的参数执行此操作

  1. long count():返回此流中的元素数量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//在forEach方法的底层,会循环获取到流中的每一个数据。
//并循环调用accept方法,并把每一个数据传递给accept方法
//s就依次表示了流中的每一个数据
//所以,我们只要在accept方法中,写上处理的业务逻辑就可以了
list.stream().forEach(
	new Consumer<String>(){
		@Override
		public void accept(string s){
			System.out.println(s);
		}
	}
);

Stream流-不能直接修改数据源中的数据

Stream流的收集操作

需求:过滤元素并遍历集合

定义一个集合,并添加一些整数 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

将集合中的奇数删除,只保留偶数。

遍历集合得到 2, 4, 6, 8, 10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Mystream6{
	public static void main(String[] args){
		ArrayList<Integer> list = new ArrayList<>();
		for(int i = 1; i <= 10; i++){
			list.add(i);
		}
		
		list.stream().filter(number -> number%2 == 0).forEach(number -> System.out.println(number));
		//---------------------------------------
		for(Integer integer : list){
			System.out.println(integer);
		}
	}
}

结论:在Stream流中无法直接修改集合、数组等数据源中的数据

收集方法-toList和toSet

Stream流的收集操作

使用Stream流的方式操作完毕之后,我想把流中的数据存起来,该怎么办呢?


Stream流的收集方法

  • R collect (Collector collector)

工具类Collectors提供了具体的收集方式

  • public static Collector toList(): 把元素收集到 List 集合中
  • public static Collector toSet(): 把元素收集到 Set 集合中,会去除重复的元素
  • public static Collector toMap(Function keyMapper,Function valueMapper): 把元素收集到 Map 集合中
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Test{
	public static void main(String[] args){
		//filter负责过滤数据
		//collect负责收集数据
		//获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中,
		//Collectors.toList():在底层会创建一个List集合。并把所有的数据添加到List集合中
		List<Integer> list = list1.stream().filter(number -> number%2 == 0)
			.collect(Collectors.toList());
	}
}

Stream流-练习

现在有两个ArrayList集合,分别存储 6名男演员6名女演员,要求完成如下的操作

  1. 男演员只要名字为3个字的前两人
  2. 女演员只要姓杨的,并且不要第一个
  3. 把过滤后的男演员姓名和女演员姓名合并到一起
  4. 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历打印对象

演员类Actor,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

1
2
3
4
5
6
Stream<String> stream1 = manList.stream().filter(s -> s.length() == 3).limit(2);
Stream<String> stream2 = womanList.stream().filter(s -> s.startWith("杨")).skip(1);
Stream.concat(stream1, stream2).foreach(s -> 
		Actor a = new Actor(s);
		System.out.println(a);
	);

File和IO的概述

思考: 以前是如何存储数据的

1
2
3
int a = 10;
int [] arr = {1,2,3,4,5};
ArrayList<String> list = new ArrayList<>();

通过变量、数组、集合等等方式进行数据存储,这样的方式存储有没有什么弊端呢?


弊端是什么?

不能永久化存储,只要代码运行结束,所有数据都会丢失。


思考:计算机中,有没有一个硬件可以永久化存储?

永久存储: 代码结束了,数据还在,甚至电脑重启之后,我的数据还在

硬盘


我们可以对硬盘进行哪些操作?

图3

对文件进行读写的前提条件?

我们得知道这个文件在哪

IO可以对硬盘中的文件进行读写

File表示要读写的文件在哪,也可以对文件进行创建,删除等操作


小结

IO流是什么?

  1. 可以将数据从本地文件中读取出来
  2. 可以将数据从内存保存到本地文件,实现数据的永久化存储

File类是什么?

  1. 在读写数据时告诉虚拟机要操作的(文件/文件夹)在哪
  2. 对(文件/文件夹)本身进行操作。包括创建,删除等

File-构造方法

File类概述和构造方法

File: 它是文件和目录路径名的抽象表示

  • 文件和目录可以通过File封装成对象
  • File封装的对象仅仅是一个路径名,它可以是存在的,也可以是不存在的
方法名 说明
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的File实例
File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的File实例

File(String pathname)

通过将给定的路径名字符串转换为抽象路径名来创建新的File实例

场景: 使用File类中的方法

1
2
String path = "C:\\it\\a.txt";
File file = new File(path);

问题:为什么要把字符串表示形式的路径变成File对象?

为了使用File类里面的方法

new File("…") 不会创建新文件,它将创建一个包含文件名的新对象(在内存中),然后,您可以对其执行 exists()、canRead() 和 isDirectory() 之类的操作,并目可以调用 createNewFile() 从中创建实际文件


File (String parent, String child)

从父路径名字符串和子路径名字符串创建新的File实例

场景: 路径的拼接

1
2
3
4
String path1 = "c:\\it";
String path2 = "a.txt";
File file = new File(path1, path2);//把两个路径拼接。
System.out.println(file); //"C:\it\a.txt"

File (File parent, String child)

从父抽象路径名和子路径名字符串创建新的File实例

场景: 路径的拼接

1
2
3
4
File file1 = new File("C:\\it");
String path = "a.txt";
File file = new File(file1, path);
System.out.println(file);  //"C:\it\a.txt"

File-绝对路径和相对路径

绝对路径和相对路径

绝对路径:永远不发生变化的路径,绝对路径一般是由盘符作为开始

1
File file1 = new File("C:\\it\\a.txt");

相对路径:相对当前项目下的路径 (根据参照物对比出来的路径,相对路径不是以盘符作为开始)

1
2
File file2 = new File("a.txt");
File file3 = new File("模块名\\a.txt");
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class FileDemo2{
	public static void main(String[] args){
		//这个路径固定不变了
		File file = new File("D:\\it\\a.txt");

		//当前项目下的 a.txt
		File file2 = new File("a.txt");

		//当前项目下---指定模块下的 a.txt
		File file3 = new File("filemodule\\a.txt");
	}
}

注意事项:

  1. Java项目中的相对路径参照物都是 “当前项目”
  2. Java项目任何书写路径的地方,都可以书写"绝对路径",也可以书写"相对路径".
  3. 相对路径和绝对路径的概念,仅限于当前Java项目 (在JavaWeb阶段,参照物会发生变化)

File-创建功能

file对象可以表示一个文件,也可以表示一个文件夹,表示的路径可以存在,也可以不存在,如果不存在,可以把它给创建出来


File类创建功能

方法名 说明
public boolean createNewFile() 创建一个新的空的文件
public boolean mkdir() 创建一个单级文件夹
public boolean mkdirs() 创建一个多级文件夹

以下三个方法均是由file对象调用的

  1. public boolean createNewFile(): 创建一个新的空的文件

注意点:

  • 如果文件存在,那么创建失败,返回false
  • 如果文件不存在,那么创建成功,返回true
  • createNewFile 方法不管调用者有没有后缀名,只能创建文件,不会创建文件夹,如果没有后缀名,就创建一个没有后缀名的文件

  1. public boolean mkdir(): 创建一个单级文件夹

注意点:

  • 只能创建单级文件夹,不能创建多级文件夹
  • 不管调用者有没有后缀名,只能创建单级文件夹

  1. public boolean mkdirs():创建一个多级文件夹

注意点:

  • 可以创建单级文件夹,也可以创建多级文件夹
  • 不管调用者有没有后缀名,只能创建文件夹

既然mkdirs能创建单级,也能创建多级。那么mkdir就没什么用了

我们看mkdirs()的源码 ,发现它里面也会调用makedir(),他会先用makedir()试试,看是不是的单极文件夹,如果创建成功,就返回了,创建不成功才会创建多级文件夹

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public boolean mkdirs() {
    if (exists()) {
        return false;
    }
    if (mkdir()) {
        return true;
    }
    File canonFile = null;
    try {
        canonFile = getCanonicalFile();
    } catch (IOException e) {
        return false;
    }
}

File-删除方法

File类删除功能

方法名 说明
public boolean delete() 删除由此抽象路径名表示的文件或目录

以下方法均是由file对象调用的

public boolean delete() //删除当前路径代表的文件或文件夹

注意点:

  1. 不走回收站的。
  2. 如果删除的是文件,那么直接删除,如果删除的是文件夹,那么能删除空文件夹
  3. 如果要删除一个有内容的文件夹,只能先进入到这个文件夹,把里面的内容全部删除完毕,才能再次删除这个文件夹

简单来说:只能删除文件和空文件夹


public void deleteOnExit() //请求在虚拟机终止时删除由此抽象路径名表示的文件或目录。

程序执行完才删除

当虚拟机终止时,删除此文件或文件夹

File-获取和判断方法

File类判断和获取功能

方法名 说明
public boolean isDirectory() 测试此抽象路径名表示的File是否为目录
public boolean isFile() 测试此抽象路径名表示的File是否为文件
public boolean exists() 测试此抽象路径名表示的File是否存在
public String getName() 返回由此抽象路径名表示的文件或目录的名称
public string getAbsolutePath() 获取绝对路径
public string getParent() 获取父级路径
public file getParentFile() 获取父级路径的file对象(也就是用父级路径创建的file对象)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static void method1(){
	File file = new File("C:\\it\\a.txt");
	boolean result1 = file.isFile();
	boolean result2 = file.isDirectory();
	System.out.println(result1);  //true
	System.out.println(result2);  //false
}

public static void method2(){
	File file = new File("C:\\it");
	boolean result1 = file.isFile();
	boolean result2 = file.isDirectory();
	System.out.println(result1);  //false
	System.out.println(result2);  //true
}

public String getName(): 返回由此抽象路径名表示的文件或目录的名称

注意点:

  1. 如果调用者是文件,那么获取的是文件名和后缀名
  2. 如果调用者是一个文件夹,那么获取的是文件夹的名字

File-listFile

File类高级获取功能

方法名 说明
public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组
public static File[] listRoots() 获取硬盘上的所有盘符

public File[] listFiles(): 返回此抽象路径名表示的目录中的文件和目录的File对象数组

  1. 进入文件夹,获取这个文件夹里面所有的文件和文件夹的File对象,并把这些File对象都放在一个数组中返回
  2. 包括隐藏文件和隐藏文件夹都可以获取

注意事项:

(1) 当调用者不存在时?

返回null

(2) 当调用者是一个文件时?

返回值为 null,listFiles() 有一个进入文件夹的动作,文件是不能进入的,文件只能打开写里面的内容,所以如果调用者是文件时,调用 list Files() 会返回一个 null

(3) 当调用者是一个空文件夹时?

返回一个长度为 0 的数组

(4) 当调用者是一个有内容的文件夹时?

进入文件夹,获取这个文件夹里所有的文件和文件夹的 File 对象,并把这些 File 对象都放在一个数组中返回

(5) 当调用者是一个有隐藏文件的文件夹时?

将里面所有文件和文件夹的路径放在Fil数组中返回,包含隐藏内容

(6) 当调用者是一个有权限才能进入的文件夹时 ?

java 是不允许进去的,返回值为 null

File-练习一

练习一: 在当前模块下的aaa文件夹中创建一个a.txt文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static void main(String[] args){
	File file = new File("filemodule\\aaa\\a.txt");
	file.createNewFile();  //报错,原因:aaa文件夹不存在
	
	//注意点:文件所在的文件夹必须要存在
	File file1 = new File("filemodule\\aaa");
	if(!file1.exists()){
		//如果文件夹不存在,就创建出来
		file1.mkdirs();
	}
	File newFile = new File(file1, "a.txt"); //将 "filemodule\\aaa" 和 "a.txt" 拼接起来
	newFile.createNewFile();
}

File-练习二

练习二: 删除一个多级文件夹

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void deleteDir(File src){
	//先删掉这个文件夹里面所有的内容,
	//递归:方法在方法体中自己调用自己。
	//注意:可以解决所有文件夹和递归相结合的题目
	//1.进入---得到src文件夹里面所有内容的File对象。
	File[] files = src.listFiles();
	
	//2.遍历---因为我想得到src文件夹里面每一个文件和文件夹的File对象
	for(File file : files){
		if(file.isFile()){
			//3.判断---如果遍历到的File对象是一个文件,那么直接删除
			file.delete();
		}else{
			//4.判断
			//递归
			deleteDir(file);  //参数一定要是src文件夹里面的文件夹File对象
		}
	}

	//最后再删除这个文件夹
	src.delete();
}

File-练习三

练习三: 统计一个文件夹中每种文件的个数并打印。

打印格式如下:

1
2
3
4
txt: 3个
doc: 4个
jpg: 6个
……
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static void main(String[] args){
	//统计一个文件夹中,每种文件出现的次数。
	//统计---定义一个变量用来统计。---弊端:同时只能统计一种文件
	//利用map集合进行数据统计,键---文件后缀名  值----次数
	File file = new File("filemodule");  //相对路径,当前模块
	HashMap<String, Integer> hm = new HashMap<>();
	getcount(hm, file);
	System.out.println(hm);
}

private static void getCount(HashMap<String, Integer> hm,File file){
	File[] files = file.listFiles();
	for(File f : files){
		if(f.isFile()){
			String fileName = f.getName();
			String[] fileNameArr = fileName.split("\\.");  //只统计常规的,例如:aaa.txt,不考虑a.b.txt这样的
			if(fileNameArr.length == 2){
				String fileEndName = fileNameArr[1];
				if(hm.containskey(fileEndName)){ //判断集合中是否存在这个键
					//已经存在
					//将已经出现的次数获取出来
					Integer count = hm.get(fileEndName);
					//这种文件又出现了一次
					count++;
					//把己经出现的次数给覆盖掉
					hm.put(fileEndName,count);
				}else{
					//不存在
					//表示当前文件是第一次出现
					hm.put(fileEndName, 1);
				}
			}
		}else{
			getCount(hm, f);
		}
	}
}

File.createNewFile | File.createTempFile

1
2
3
4
5
6
/**
 * fileName: 临时文件的名字, 生成后的文件名字将会是【fileName + 随机数】
 * suffix: 文件后缀,例如.txt, .tmp
 * parentFile: 临时文件目录,如果不指定,则默认把临时文件存储于系统临时文件目录上       
*/
public static File createTempFile(String fileName, String suffix, File parentFile)

File.createNewFile

createNewFile();返回值为 boolean;

1
2
3
File file = new File("D:\\test\\1.txt");
boolean res = file.createNewFile();
if(!res)System.out.println("创建失败!");

如果 D:/test 目录下没有 1.txt 文件,则创建该文件;

如果没有 test 目录,直接抛出异常;

如果 1.txt 已经存在,那么文件创建失败。

可以得知,createNewFile() 方法,根据抽象路径创建一个新的空文件,当抽象路径指定的文件存在时,创建失败。

File.createTempFile

File.createTempFile(String prefix, String suffix);

根据指定的前缀和后缀在默认的文件夹里创建文件

方法默认的保存路径为:C:\Documents and Settings\Administrator\Local Settings\Temp


File.createTempFile(String prefix, String suffix, File directory);

在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。

createTempFile() 方法,在指定的目录下创建一个 temp 文件,directory 类型为 File ,如果路径不存在,则创建失败。

删除方法

1
2
3
4
5
//立即删除文件
file.delete();

//在JVM退出时删除文件
file.deleteOnExit();

总结:临时文件能够使用默认路径,可以避免存在创建文件是因为路径错误导致创建文件失败的问题。

如果需求中需要创建一个临时文件,这个临时文件可能作为存储使用,但在程序运行结束后需要删除文件,可以使用 deleteOnExit 方法