> 文章列表 > Java入坑之集合、流与序列化

Java入坑之集合、流与序列化

Java入坑之集合、流与序列化

一、集合

1.1集合定义

  • 集合概念:
    • 保存和盛装数据的容器,将许多元素组合成一个单一单元的容器对象
    • 集合,可用于存储/检索/操作/传输/聚合数据
  • 集合框架:
    • 表示和操作集合的体系,包括接口、实现类,集合框架的结构图。
    • 接口(Interfaces):表示集合的抽象数据类型。使用接口,允许集合独立于其表示的细节进行操作
    • 实现(Implementations):集合接口的具体实现,包含可重用的数据结构
    • 算法(Algorithms):对集合执行搜索/排序等操作,是可重用的功能

1.2集合框架

Java集合框架是Java编程语言提供的一组接口和类,用于处理和操作数据集合。Java集合框架包含以下主要接口和类:

4ede152b69a1475f945b23c974f34fbe.jpg

  • Iterable接口是所有可迭代对象的根接口,定义了一组基本的迭代操作方法,如获取迭代器、使用foreach循环遍历等。它的实现类都可以使用foreach循环进行遍历,使得操作更加简单和方便(Iterable接口不属于java集合框架)
  •  Collection接口:Collection接口是所有集合类的根接口,定义了一组基本的集合操作方法,如添加元素、删除元素、遍历集合等。
  • List接口:List接口是Collection接口的子接口,定义了一个有序集合,允许重复元素。List接口的常用实现类包括ArrayList、LinkedList和Vector。
  • Set接口:Set接口是Collection接口的子接口,定义了一个无序集合,不允许重复元素。Set接口的常用实现类包括HashSet和TreeSet。
  • Map接口:Map接口是一种映射关系,将键映射到值,每个键最多只能映射到一和TreeMap。
  • 除了上述接口和类,Java集合框架还包括一些其他的接口和类,如Queue接口、Deque接口、Iterator接口、Collections类等。Java集合框架提供了丰富的数据结构和算法,可以用于解决各种实际问题。

1.3Collection接口

Collection接口是Java集合框架中的一个重要接口,它定义了一组基本的集合操作方法,如添加元素、删除元素、遍历集合等。以下是Collection接口中的一些常见集合操作:

1.添加元素:可以使用add(E e)方法将元素添加到集合中,也可以使用addAll(Collection<? extends E> c)方法将另一个集合中的所有元素添加到当前集合中。

2.删除元素:可以使用remove(Object o)方法删除指定元素,也可以使用removeAll(Collection<?> c)方法删除当前集合中与另一个集合相同的元素,还可以使用clear()方法删除集合中的所有元素。

3.判断集合中是否包含指定元素:可以使用contains(Object o)方法判断集合中是否包含指定元素,也可以使用containsAll(Collection<?> c)方法判断集合中是否包含另一个集合中的所有元素。

4.遍历集合中的元素:可以使用迭代器(iterator()方法返回)或者增强型for循环(for (E element : collection))遍历集合中的元素。

5.获取集合中的元素个数:可以使用size()方法获取集合中元素的数量。

6.将集合转换为数组:可以使用toArray()方法将集合转换为数组。

7.判断集合是否为空:可以使用isEmpty()方法判断集合是否为空。

8.clear()方法,用于清空集合中的所有元素。它的作用是将集合中的所有元素删除,使集合变为空集合。

import java.util.*;public class Main {public static void main(String[] args) {// 创建一个ArrayList集合Collection<String> collection = new ArrayList<String>();// 添加元素collection.add("Java");collection.add("Python");collection.add("C++");// 将另一个集合中的所有元素添加到当前集合中Collection<String> anotherCollection = new ArrayList<String>();anotherCollection.add("JavaScript");anotherCollection.add("Ruby");collection.addAll(anotherCollection);// 删除指定元素collection.remove("C++");// 删除与另一个集合中相同的元素Collection<String> commonCollection = new ArrayList<String>();commonCollection.add("Python");commonCollection.add("JavaScript");collection.removeAll(commonCollection);// 判断集合中是否包含指定元素boolean containsJava = collection.contains("Java");// 判断集合中是否包含另一个集合中的所有元素boolean containsAll = collection.containsAll(commonCollection);// 遍历集合中的元素Iterator<String> iterator = collection.iterator();while (iterator.hasNext()) {String element = iterator.next();System.out.println(element);}for (String element : collection) {System.out.println(element);}// 获取集合中的元素个数int size = collection.size();// 将集合转换为数组String[] array = collection.toArray(new String[0]);// 判断集合是否为空boolean isEmpty = collection.isEmpty();// 清空集合中的所有元素collection.clear();}
}

1.4泛型

1.4.1泛型概述

范式(Generics),也称为参数化类型,是Java 5引入的一个重要特性,用于提供更好的类型安全和代码重用。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

1.4.2泛型格式

在Java中,泛型的基本语法是在类名或方法名后添加一对尖括号(<>),在尖括号中定义类型参数。例如,下面是定义一个泛型类的语法:

class MyClass<T> {

    // 类中的方法和成员变量可以使用类型参数T

}

在使用泛型时,可以将具体的类型作为类型参数传递给类或方法

MyClass<Integer> myClass = new MyClass<Integer>();

1.4.3泛型类

泛型类是指使用泛型类型参数的类。在定义泛型类时,可以在类名后面添加尖括号,并在尖括号中声明一个或多个类型参数,这些类型参数可以在类的成员变量和方法中使用。

public class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public void setKey(K key) {this.key = key;}public V getValue() {return value;}public void setValue(V value) {this.value = value;}
}

实例

Pair<String, Integer> pair = new Pair<>("foo", 42);

1.4.4泛型接口

泛型接口是一个使用泛型类型参数的接口,它的类型参数可以在接口的方法参数、返回值类型和常量定义中使用。泛型接口的定义方式与泛型类类似,也是在接口名后面添加尖括号,并在尖括号中声明一个或多个类型参数。

public interface MyGenericInterface<T> {void doSomething(T t);
}

实例

MyGenericInterface<String> myInterface = new MyGenericInterface<String>() {@Overridepublic void doSomething(String s) {System.out.println("Doing something with " + s);}
};

1.4.5泛型方法

泛型方法是在方法定义中使用泛型类型参数的方法。泛型方法可以在参数类型、返回值类型或方法体中使用泛型类型参数,以提高方法的灵活性和重用性。

public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}
}

实例

String[] stringArray = {"foo", "bar", "baz"};
Integer[] intArray = {1, 2, 3, 4, 5};
printArray(stringArray);
printArray(intArray);

二、Iterator接口

java.lang.Iterable接口:实现了此接口类的对象,支持使用Iterable遍历元素及支持For Each循环语句;

Iterator接口:用来遍历集合中的元素,常用方法:

  • hasNext()//是否下一个元素;
  • next()//向后移动游标,同时返回游标指向的元素;
  • remove()//移除当前游标未知的元素
import java.util.*;public class Main {public static void main(String[] args) {// 创建一个ArrayList集合Collection<String> collection = new ArrayList<String>();// 添加元素collection.add("Java");collection.add("Python");collection.add("C++");// 获取集合的迭代器Iterator<String> iterator = collection.iterator();// 遍历集合中的元素while (iterator.hasNext()) {// 向后移动游标,同时返回游标指向的元素String element = iterator.next();System.out.println(element);// 移除当前游标指向的元素if (element.equals("Java")) {iterator.remove();}}// 输出移除后的集合中的元素for (String element : collection) {System.out.println(element);}}
}

三、List集合

List是Java中的一个接口,它是一个有序的集合,允许我们按顺序存储和访问元素。List接口扩展了集合接口

除了从Collection继承的方法外,提供基于位置索引的操作方法:

  1. add(),将指定位置元素后移,添加
  2. set(),替换
  3. get(int index),获取
  4. remove(int index),移除

数组列表(ArrayList类)、链表(LinkedList类)、向量(Vector类)和堆栈(Stack类)

3.1ArrayList类

Java中的ArrayList是一种可变长度的数组实现,允许在运行时添加、删除和修改元素。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;public class Main {public static void main(String[] args) {// 创建一个ArrayList对象ArrayList<String> list = new ArrayList<String>();// 添加元素到列表list.add("Java");list.add("Python");list.add("C++");// 使用add(int index, E element)方法将元素插入到指定的位置list.add(1, "JavaScript");// 使用set(int index, E element)方法替换指定位置上的元素list.set(2, "PHP");// 使用get(int index)方法获取指定位置上的元素String element = list.get(0);System.out.println("Element at index 0: " + element);// 使用remove(int index)方法移除指定位置上的元素list.remove(3);// 使用Collection接口继承的方法进行操作System.out.println("List size: " + list.size());System.out.println("Is list empty? " + list.isEmpty());// 使用Collections类的sort()方法对列表进行排序Collections.sort(list);// 使用foreach循环遍历列表System.out.println("Elements in list:");for (String str : list) {System.out.println(str);}// 将ArrayList转换成数组String[] array = list.toArray(new String[list.size()]);System.out.println("Array length: " + array.length);System.out.println("Elements in array:");for (String str : array) {System.out.println(str);}// 将数组转换成ArrayListArrayList<String> newList = new ArrayList<String>(Arrays.asList(array));System.out.println("New list size: " + newList.size());}
}

其他 ArrayList 方法

 3.2LinkedList类

LinkedList是Java中一个双向链表的实现,它实现了List接口,同时也实现了Deque接口,可以作为队列或栈来使用。与ArrayList不同,LinkedList的插入和删除操作效率更高,但是随机访问效率比较低,多数情况下使用ArrayList或不可变集合(后期讨论)即可

import java.util.Collections;
import java.util.LinkedList;public class Main {public static void main(String[] args) {// 创建一个空的LinkedList对象LinkedList<String> linkedList = new LinkedList<String>();// 添加元素到列表linkedList.add("Java");linkedList.add("Python");linkedList.add("C++");// 在列表开头添加元素linkedList.addFirst("JavaScript");// 在列表末尾添加元素linkedList.addLast("PHP");// 获取列表的第一个和最后一个元素String firstElement = linkedList.getFirst();String lastElement = linkedList.getLast();System.out.println("First element: " + firstElement);System.out.println("Last element: " + lastElement);// 移除列表的第一个和最后一个元素linkedList.removeFirst();linkedList.removeLast();// 使用foreach循环遍历列表System.out.println("Elements in list:");for (String str : linkedList) {System.out.println(str);}// 查找元素的位置int index = linkedList.indexOf("Python");System.out.println("Index of 'Python': " + index);// 对列表进行排序Collections.sort(linkedList);// 使用foreach循环遍历排序后的列表System.out.println("Elements in sorted list:");for (String str : linkedList) {System.out.println(str);}}
}

3.3Vector类

Vector 和 ArrayList 都是基于数组实现的,它们都可以用来表示一组数量可变的对象引用的集合,并且可以随机地访问其中的元素。但是,它们之间有一些区别:

同步性:Vector 的方法都是同步的,是线程安全的,而 ArrayList 的方法不是。由于线程同步会影响性能,因此 ArrayList 的性能比 Vector 好。
数据增长:当需要增加存储容量时,默认情况下,Vector 会将容量增加一倍,而 ArrayList 只会增加50%。
如果不需要线程安全的实现,建议使用 ArrayList 代替 Vector

import java.util.Vector;
import java.util.Collections;public class Main {public static void main(String[] args) {// 创建一个空的Vector对象Vector<String> vector = new Vector<String>();// 添加元素到向量vector.add("Java");vector.add("Python");vector.add("C++");// 在向量开头添加元素vector.add(0, "JavaScript");// 在向量末尾添加元素vector.add("PHP");// 获取向量的第一个和最后一个元素String firstElement = vector.firstElement();String lastElement = vector.lastElement();System.out.println("First element: " + firstElement);System.out.println("Last element: " + lastElement);// 移除向量的第一个和最后一个元素vector.removeElementAt(0);vector.removeElementAt(vector.size() - 1);// 使用for循环遍历向量System.out.println("Elements in vector:");for (int i = 0; i < vector.size(); i++) {System.out.println(vector.get(i));}// 查找元素String elementToFind = "Java";int index = vector.indexOf(elementToFind);if (index != -1) {System.out.println("Found " + elementToFind + " at index " + index);} else {System.out.println(elementToFind + " not found");}// 替换元素String elementToReplace = "C++";int replaceIndex = vector.indexOf(elementToReplace);if (replaceIndex != -1) {vector.set(replaceIndex, "Go");System.out.println("Replaced " + elementToReplace + " with Go");} else {System.out.println(elementToReplace + " not found");}// 对向量进行排序Collections.sort(vector);System.out.println("Sorted elements in vector:");for (int i = 0; i < vector.size(); i++) {System.out.println(vector.get(i));}}
}

3.4JAVA缺陷

 四、Map Interface

 Map是Java中的一个接口,它表示一组键值对的映射。Map接口提供了对映射中的元素进行添加、删除、修改和查询的方法。Map中的键和值都可以是任何Java对象。Map不是集合

基本实现类 java.util.HashMap<K, V>,查询效率与内存占用最平衡,非线程安全 java.util.TreeMap <K, V>/HashTable<K, V>

java.util.Map接口:用于存放键值对(key-value),通过Key值获取相应的value值

  1. Map中key必须是唯一的,且每个key只能对应一个value;但不同key,可以对应同一个value;添加key-value时,如果key已经存在,则后一个覆盖前一个
  2. 支持以任何类型(引用类型)对象为key/value

4.1hashmap

import java.util.HashMap;
import java.util.Map;public class Main {public static void main(String[] args) {// 创建一个HashMap实例Map<String, Integer> map = new HashMap<>();// 使用put方法向map中添加键值对map.put("one", 1);map.put("two", 2);map.put("three", 3);// 使用get方法根据键来获取对应的值System.out.println(map.get("one")); // 输出 1System.out.println(map.get("two")); // 输出 2System.out.println(map.get("three")); // 输出 3// 使用size方法获取map中键值对的数量System.out.println(map.size()); // 输出 3// 使用containsKey方法判断map中是否包含指定的键System.out.println(map.containsKey("one")); // 输出 trueSystem.out.println(map.containsKey("four")); // 输出 false// 使用containsValue方法判断map中是否包含指定的值System.out.println(map.containsValue(1)); // 输出 trueSystem.out.println(map.containsValue(4)); // 输出 false// 使用 replace 方法将键 "two" 的值替换为 4map.replace("two", 4);System.out.println(map.get("two")); // 输出 4// 使用 replace 方法仅当键 "three" 的值为 3 时,将其替换为 5map.replace("three", 3, 5);System.out.println(map.get("three")); // 输出 5// 使用remove方法删除map中指定的键值对map.remove("one");System.out.println(map.containsKey("one")); // 输出 false// 使用clear方法清空map中所有的键值对map.clear();System.out.println(map.size()); // 输出 0}
}

Map本身是不具备排序功能的,因为它是一种映射关系的数据结构,它的元素是按照键来存储和访问的,而不是按照值的大小或者键的插入顺序来排序的。但是可以通过将Map中的元素转换成List集合,然后使用Java的排序工具来对List进行排序,从而实现对Map的排序。

getOrDefault 方法是 Map 接口中的一个实用方法,它允许你根据指定的键来获取对应的值。如果 Map 中不存在该键,则返回一个默认值

这个方法的语法如下:

V getOrDefault(Object key, V defaultValue)
import java.util.HashMap;
import java.util.Map;public class MapExample {public static void main(String[] args) {// 创建一个HashMap实例Map<String, Integer> map = new HashMap<>();// 使用put方法向map中添加键值对map.put("one", 1);map.put("two", 2);map.put("three", 3);// 使用getOrDefault方法根据键来获取对应的值System.out.println(map.getOrDefault("one", 0)); // 输出 1System.out.println(map.getOrDefault("four", 0)); // 输出 0}
}

4.2Treemap

import java.util.Map;
import java.util.TreeMap;public class Main {public static void main(String[] args) {// 创建一个TreeMap实例Map<String, Integer> map = new TreeMap<>();// 使用put方法向map中添加键值对map.put("one", 1);map.put("two", 2);map.put("three", 3);// 使用get方法根据键来获取对应的值System.out.println(map.get("one")); // 输出 1System.out.println(map.get("two")); // 输出 2System.out.println(map.get("three")); // 输出 3// 使用size方法获取map中键值对的数量System.out.println(map.size()); // 输出 3// 使用containsKey方法判断map中是否包含指定的键System.out.println(map.containsKey("one")); // 输出 trueSystem.out.println(map.containsKey("four")); // 输出 false// 使用containsValue方法判断map中是否包含指定的值System.out.println(map.containsValue(1)); // 输出 trueSystem.out.println(map.containsValue(4)); // 输出 false// 使用 replace 方法将键 "two" 的值替换为 4map.replace("two", 4);System.out.println(map.get("two")); // 输出 4// 使用 replace 方法仅当键 "three" 的值为 3 时,将其替换为 5map.replace("three", 3, 5);System.out.println(map.get("three")); // 输出 5// 使用remove方法删除map中指定的键值对map.remove("one");System.out.println(map.containsKey("one")); // 输出 false// 使用clear方法清空map中所有的键值对map.clear();System.out.println(map.size()); // 输出 0}
}

TreeMap 和 HashMap 都是 Map 接口的实现类,它们都可以用来存储键值对。但是,它们之间也有一些重要的区别。

  • 排序TreeMap 是一个有序的 Map 实现,它按照键的自然顺序或者根据创建时提供的 Comparator 进行排序。而 HashMap 是一个无序的 Map 实现,它不保证键值对的顺序。

  • 性能:由于 TreeMap 是基于红黑树实现的,所以它的插入、删除和查找操作的时间复杂度都是 O(log n)。而 HashMap 是基于哈希表实现的,所以它的插入、删除和查找操作的时间复杂度都是 O(1)(在最坏情况下为 O(n))。因此,在大多数情况下,HashMap 的性能要优于 TreeMap

  • 线程安全TreeMap 和 HashMap 都不是线程安全的。如果需要在线程安全的环境中使用它们,可以使用 Collections.synchronizedSortedMap() 方法来获取一个线程安全的 SortedMap,或者使用 ConcurrentHashMap 类来代替 HashMap

总之,如果你需要对键值对进行排序,那么可以使用 TreeMap;否则,如果你更关心性能,那么可以使用 HashMap

五、Set

Set 是 Java 集合框架中的一个接口,它继承自 Collection 接口。Set 接口的实例表示一个不包含重复元素的集合。也就是说,Set 中的每个元素都是唯一的。

Set接口:只包含继承自Collection方法,并添加禁止重复元素的限制,无基于索引的操作方法;

  1. 基本实现类:

  2. java.util.HashSet<>,元素无序(底层基于HashMap确定元素是否重复)

  3. java.util.LinkedHashSet<>,元素有序

  4. java.util.TreeSet <>,元素有序

均无基于索引的操作方法

5.1Hashset

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class Main {public static void main(String[] args) {// 创建一个HashSet实例Set<String> set = new HashSet<>();// 使用add方法向set中添加元素set.add("one");set.add("two");set.add("three");// 使用addAll方法将另一个集合中的所有元素添加到set中Set<String> anotherSet = new HashSet<>();anotherSet.add("four");anotherSet.add("five");set.addAll(anotherSet);// 使用iterator方法获取一个迭代器Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}// 使用remove方法从set中移除指定的元素set.remove("one");// 使用removeAll方法从set中移除另一个集合中的所有元素set.removeAll(anotherSet);// 使用retainAll方法保留set中与另一个集合中相同的所有元素Set<String> yetAnotherSet = new HashSet<>();yetAnotherSet.add("two");yetAnotherSet.add("three");set.retainAll(yetAnotherSet);// 使用clear方法清空set中的所有元素set.clear();// 使用size方法获取set中元素的数量System.out.println(set.size());// 使用toArray方法将set转换为数组String[] array = set.toArray(new String[0]);System.out.println(Arrays.toString(array));// 使用contains方法判断set中是否包含指定的元素System.out.println(set.contains("one"));// 使用containsAll方法判断set中是否包含另一个集合中的所有元素System.out.println(set.containsAll(yetAnotherSet));// 使用hashCode方法获取set的哈希码值System.out.println(set.hashCode());}
}

其他方法

5.2TreeSet

import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;public class Main {public static void main(String[] args) {// 创建一个TreeSet实例Set<String> set = new TreeSet<>();// 使用add方法向set中添加元素set.add("one");set.add("two");set.add("three");// 使用addAll方法将另一个集合中的所有元素添加到set中Set<String> anotherSet = new TreeSet<>();anotherSet.add("four");anotherSet.add("five");set.addAll(anotherSet);// 使用iterator方法获取一个迭代器Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}// 使用remove方法从set中移除指定的元素set.remove("one");// 使用removeAll方法从set中移除另一个集合中的所有元素set.removeAll(anotherSet);// 使用retainAll方法保留set中与另一个集合中相同的所有元素Set<String> yetAnotherSet = new TreeSet<>();yetAnotherSet.add("two");yetAnotherSet.add("three");set.retainAll(yetAnotherSet);// 使用clear方法清空set中的所有元素set.clear();// 使用size方法获取set中元素的数量System.out.println(set.size());// 使用toArray方法将set转换为数组String[] array = set.toArray(new String[0]);System.out.println(Arrays.toString(array));// 使用contains方法判断set中是否包含指定的元素System.out.println(set.contains("one"));// 使用containsAll方法判断set中是否包含另一个集合中的所有元素System.out.println(set.containsAll(yetAnotherSet));// 使用hashCode方法获取set的哈希码值System.out.println(set.hashCode());}
}

六、不可变集合

不可变集合,就是不可被修改的集合,定义完成后不可以修改,或者添加、删除。集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。

在Java 9中,可以使用静态工厂方法List.of、Set.of与Map.of来创建不可变列表、集合或映射

​List.of、Set.of和Map.of是Java 9中引入的静态工厂方法,用于简单地创建不可变的少量元素的集合。例如,可以使用以下代码创建一个不可变的Set集合:

Set<String> str1 = Set.of("a", "b", "c");

同样,可以使用以下代码创建一个不可变的Map集合:

Map<String,Integer> str2 = Map.of("a", 1, "b", 2);

以及使用以下代码创建一个不可变的List集合:

List<String> str3 = List.of("a", "b");

需要注意的是,这些方法只存在于Map、List、Set接口中,它们的实现类(如HashMap、ArrayList等)并没有这些方法。此外,返回的集合是不可变的,不能再次添加或删除元素。

 不可变集合的注意

 七、集合

7.1概述

Stream(流)是Java 8中引入的一个新的抽象概念,它可以让你以一种声明的方式处理数据。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。它使用一种类似于SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象

Stream操作的是集合中的元素,而非集合本身。因此,将创建新集合聚合Stream操作的结果,而不影响源集合结构

7.2Stream基本操作

使用Stream的基本步骤包括:

  1. 创建Stream:通过stream()方法,取得集合对象的数据集。
  2. Intermediate:通过一系列中间(Intermediate)方法,对数据集进行过滤、检索等数据集的再次处理。例如,使用filter()方法来对数据集进行过滤。
  3. Terminal:通过最终(terminal)方法完成对数据集中元素的处理。例如,使用forEach()完成对过滤后元素的打印
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class StreamExample {public static void main(String[] args) {List<String> list = Arrays.asList("a1", "a2", "b1", "c2", "c1");list.stream() // 创建一个流.filter(s -> s.startsWith("c")) // 过滤以"c"开头的元素.map(String::toUpperCase) // 将每个元素映射为大写.flatMap(s -> Stream.of(s.split(""))) // 将每个元素转换为一个流,然后连接成一个流.distinct() // 去除重复元素.sorted() // 对元素进行排序.peek(System.out::println) // 对每个元素执行操作并返回一个新的流,用于调试.limit(2) // 截取前两个元素.skip(1) // 跳过第一个元素.collect(Collectors.toList()) // 将流中的元素收集到列表中.forEach(System.out::println); // 迭代列表中的每个元素}
}

groupingBy()是Java 8中Collectors类的一个方法,它可以将流中的元素按照指定的条件分组。它返回一个Map<K, List<T>>,其中K是分组条件的类型,List<T>是每个分组中元素的列表。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class Main {public static void main(String[] args) {List<String> list = Arrays.asList("a1", "a2", "b1", "c2", "c1");Map<String, List<String>> result = list.stream() // 创建一个流.collect(Collectors.groupingBy(s -> s.substring(0, 1))); // 按照字符串的第一个字符进行分组System.out.println(result); // 输出分组结果}
}

7.3Steam的几点注意

7.3.1filter的优化 

7.3.2Comparator类。

比较器。控制顺序 comparing(),基于指定值排序  reversed(),倒序

7.3.3Stream的其他方法 

 八、Optional

Optional是Java 8中引入的一个新的类,它用于解决空指针异常(NullPointerException)的问题。它是一个容器类,可以包含一个非空的值,也可以不包含任何值。

常见方法

创建Optional对象的方法,如of()ofNullable()of()方法用于创建一个包含非空值的Optional对象,如果传入的值为null,则会抛出NullPointerException异常。而ofNullable()方法则用于创建一个可能为空值的Optional对象。

执行操作的方法,如ifPresent()ifPresentOrElse()。这些方法可以基于容器是否为空来执行操作,且无返回值。

中间操作方法,如filter()map()or()。这些方法可以将操作结果置于新的Optional容器中以执行后续操作。如果结果为空,则会返回相同类型的空容器。

获取操作和判断方法,如orElse()orElseGet()get()isEmpty()isPresent()。这些方法可以用来获取容器中的对象或判断当前容器是否为空。

8.1创建Optional对象的方法

of()ofNullable()都是Java中Optional类的静态方法,用于创建Optional对象。

of()方法用于创建一个包含非空值的Optional对象。如果传入的值为null,则会抛出NullPointerException异常。

ofNullable()方法则用于创建一个可能为空值的Optional对象。如果传入的值为null,则返回一个空的Optional对象。

	private static void create(){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1); // 相当于:// Optional<USB> usb1 = Optional.of(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3); // 不可以用// Optional<USB> usb3 = Optional.of(u3); 因为现在的u3是空指针。}

8.2执行操作的方法

ifPresent()ifPresentOrElse()都是Java中Optional类的实例方法,用于在Optional对象中的值存在时执行操作。

ifPresent()方法接受一个Consumer函数式接口作为参数,如果Optional对象中的值存在,则使用该值调用Consumer函数。

private static void create(){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3);usb1.ifPresent(usb -> {System.out.println(usb.getName());});// 当Optional容器不空的时候执行的函数;}

ifPresentOrElse()方法则接受两个参数:一个Consumer函数式接口和一个Runnable函数式接口。如果Optional对象中的值存在,则使用该值调用Consumer函数;否则,调用Runnable函数。

Java9才有。
private static void create(){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3);usb1.ifPresentOrElse(usb -> {System.out.println(usb.getName());}, () -> System.out.println("空"));// 当Optional容器不空的时候执行第一个函数, 否则之心发第二个函数。}

8.3中间操作方法

filter()map()or()都是Java中Optional类的实例方法,用于对Optional对象中的值进行中间操作。

filter()方法过滤容器中元素。容器为空,返回空容器;容器不为空,执行过滤;符合条件,将对象置于新容器,不符合条件,返回空容器即,无论是否匹配,均返回一个容器,用于后续操作

public static void main(String[] args){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3);usb1.filter(usb -> "键盘".equals(usb.getName())).ifPresent(usb -> System.out.println(usb.getName()));}

Optional map()

基于容器中对象,映射。容器为空,返回相同类型的空容器;容器不为空,执行映射函数,将映射结果封装在新容器中
即,无论是否匹配,均返回一个容器,用于后续操作

public static void main(String[] args){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3);usb1.map(USB::getName).ifPresent(System.out::println);}

or()方法接受一个Supplier函数式接口作为参数,如果Optional对象中的值存在,则返回该Optional对象;否则,返回Supplier函数返回的Optional对象

Optional<String> optional = Optional.ofNullable(null);
Optional<String> orOptional = optional.or(() -> Optional.of("World"));

8.4获取操作和判断方法

orElse()orElseGet()get()isEmpty()isPresent()都是Java中Optional类的实例方法,用于获取Optional对象中的值或判断Optional对象是否为空。

orElse()方法接受一个默认值作为参数,如果Optional对象中的值存在,则返回该值;否则,返回默认值。

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElse("World");

orElseGet()方法与orElse()方法类似,但是它接受一个Supplier函数式接口作为参数,用于生成默认值。如果Optional对象中的值存在,则返回该值;否则,返回Supplier函数返回的值。

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElseGet(() -> "World");

get()方法用于获取Optional对象中的值。如果Optional对象中的值存在,则返回该值;否则,抛出NoSuchElementException异常。例如:

public static void main(String[] args){USB u1 = new USB("键盘");USB u2 = new USB("鼠标");USB u3 = null;Optional<USB> usb1 = Optional.ofNullable(u1);Optional<USB> usb2 = Optional.ofNullable(u2);Optional<USB> usb3 = Optional.ofNullable(u3);System.out.println(usb1.get().getName());
}

isEmpty()isPresent()方法用于判断Optional对象是否为空。isEmpty()方法在Optional对象为空时返回true,否则返回false;而isPresent()方法在Optional对象不为空时返回true,否则返回false。

Optional<String> optional = Optional.ofNullable(null);
boolean isEmpty = optional.isEmpty();
boolean isPresent = optional.isPresent();

九、Java 序列化

9.1概念

Java序列化是指将一个对象转换为字节序列的过程,这些字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。这样,将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象

  • 序列化: 将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

9.2序列化的使用场景

  • 永久性保存对象,保存对的字节序列到本地文件或者数据库中;
  • 通过序列化以字节流的形式对象在网络中进行传递和接收;
  • 通过序列化在进程间传递对象

9.3序列化优点

  • 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
  • 利用序列化实现远程通信,即在网络上传送对象的字节序列。

9.4序列化过程(Serializable 接口的使用)

一个类的对象要想序列化成功,必须满足两个条件:

1.该类必须实现 java.io.Serializable 接口。

2.该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的

在Java中,可序列化指的是一个类实现了Serializable接口,这样它的对象就可以被序列化。序列化是指将一个对象转换为字节序列的过程

public class Employee implements java.io.Serializable
{public String name;public String address;public transient int SSN;public int number;public void mailCheck(){System.out.println("Mailing a check to " + name+ " " + address);}
}

实例化

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class SerializeDemo {public static void main(String[] args) {Employee e = new Employee();e.name = "John Doe";e.address = "123 Main St";e.SSN = 11122333;e.number = 101;try {// 创建FileOutputStream对象,用于写入序列化后的对象FileOutputStream fileOut =new FileOutputStream("employee.ser");// 创建ObjectOutputStream对象,用于序列化对象ObjectOutputStream out = new ObjectOutputStream(fileOut);// 使用writeObject()方法将Employee对象e序列化out.writeObject(e);// 关闭输出流out.close();fileOut.close();System.out.printf("Serialized data is saved in employee.ser");} catch (IOException i) {i.printStackTrace();}}
}

上述代码通过使用Java的ObjectOutputStream类来实现序列化。ObjectOutputStream类提供了一个writeObject()方法,该方法可以将一个实现了Serializable接口的对象序列化为一个字节流。

在上述代码中,首先创建了一个Employee对象,并将其各个字段设置为特定值。然后,使用FileOutputStream类创建了一个名为"employee.ser"的文件输出流。接下来,使用该文件输出流创建了一个ObjectOutputStream对象。

接下来,调用ObjectOutputStream对象的writeObject()方法,并将Employee对象作为参数传递。这会将Employee对象序列化为一个字节流,并将其写入到指定的文件中。最后,关闭ObjectOutputStream和FileOutputStream对象。

运行此代码后,将在当前目录中创建一个名为"employee.ser"的文件,其中包含序列化的Employee对象。

9.5反序列化

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;public class DeserializeDemo {public static void main(String[] args) {Employee e = null;try {// 创建FileInputStream对象,用于读取序列化后的对象FileInputStream fileIn = new FileInputStream("employee.ser");// 创建ObjectInputStream对象,用于反序列化对象ObjectInputStream in = new ObjectInputStream(fileIn);// 使用readObject()方法从输入流中读取序列化的Employee对象e = (Employee) in.readObject();// 关闭输入流in.close();fileIn.close();} catch (IOException i) {i.printStackTrace();return;} catch (ClassNotFoundException c) {System.out.println("Employee class not found");c.printStackTrace();return;}System.out.println("Deserialized Employee...");System.out.println("Name: " + e.name);System.out.println("Address: " + e.address);System.out.println("SSN: " + e.SSN);System.out.println("Number: " + e.number);}
}

在上述代码中,首先声明了一个Employee类型的变量e,并将其初始化为null。然后,使用FileInputStream类创建了一个名为"employee.ser"的文件输入流。接下来,使用该文件输入流创建了一个ObjectInputStream对象。

接下来,调用ObjectInputStream对象的readObject()方法,并将返回的对象强制转换为Employee类型。这会从指定的文件中读取序列化的Employee对象,并将其反序列化为一个Java对象。最后,关闭ObjectInputStream和FileInputStream对象。

运行此代码后,将从名为"employee.ser"的文件中读取序列化的Employee对象,并将其反序列化为一个Java对象。