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>() {
    @Override
    public 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);
        }

        // 将数组转换成ArrayList
        ArrayList<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")); // 输出 1
        System.out.println(map.get("two")); // 输出 2
        System.out.println(map.get("three")); // 输出 3

        // 使用size方法获取map中键值对的数量
        System.out.println(map.size()); // 输出 3

        // 使用containsKey方法判断map中是否包含指定的键
        System.out.println(map.containsKey("one")); // 输出 true
        System.out.println(map.containsKey("four")); // 输出 false

        // 使用containsValue方法判断map中是否包含指定的值
        System.out.println(map.containsValue(1)); // 输出 true
        System.out.println(map.containsValue(4)); // 输出 false

        // 使用 replace 方法将键 "two" 的值替换为 4
        map.replace("two", 4);
        System.out.println(map.get("two")); // 输出 4

        // 使用 replace 方法仅当键 "three" 的值为 3 时,将其替换为 5
        map.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)); // 输出 1
        System.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")); // 输出 1
        System.out.println(map.get("two")); // 输出 2
        System.out.println(map.get("three")); // 输出 3

        // 使用size方法获取map中键值对的数量
        System.out.println(map.size()); // 输出 3

        // 使用containsKey方法判断map中是否包含指定的键
        System.out.println(map.containsKey("one")); // 输出 true
        System.out.println(map.containsKey("four")); // 输出 false

        // 使用containsValue方法判断map中是否包含指定的值
        System.out.println(map.containsValue(1)); // 输出 true
        System.out.println(map.containsValue(4)); // 输出 false

        // 使用 replace 方法将键 "two" 的值替换为 4
        map.replace("two", 4);
        System.out.println(map.get("two")); // 输出 4

        // 使用 replace 方法仅当键 "three" 的值为 3 时,将其替换为 5
        map.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对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/11935.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python真的如此好吗?

作为一名合格的&#xff08;准&#xff09;程序员&#xff0c;必做的一件事是关注编程语言的热度&#xff0c;编程榜代表了编程语言的市场占比变化&#xff0c;它的变化更预示着未来的科技风向和机会&#xff01; Python霸占榜首 只因它真的很强 Python&#xff0c;年龄可能比…

这篇文章价值很大:股票历史分时成交数据怎么简单获取?【干货】

文章目录前言一、准备二、使用步骤1.引入库2&#xff0c;使用这个API查询历史分时数据&#xff1a;3.查询完整历史分时数据4.其他查询方法参数格式&#xff1a;[(市场代码, 股票代码), ...]参数&#xff1a;市场代码, 股票代码, 文件名, 起始位置, 数量参数&#xff1a;市场代码…

MySQL-binlog+dump备份还原

目录 &#x1f341;binlog日志恢复 &#x1f342;binlog介绍 &#x1f342;Binlog的用途 &#x1f342;开启binary log功能 &#x1f342;配置binlog &#x1f341;mysqldump &#x1f342;数据库的导出 &#x1f342;数据库的导入 &#x1f341;mysqldumpbinlog &#x1f990;…

【Python_Scrapy学习笔记(一)】Scrapy框架简介

Scrapy框架简介 前言 Scrapy 框架是一个用 python 实现的为了爬取网站数据、提取数据的应用框架&#xff0c;使用 Twisted 异步网络库来处理网络通讯&#xff0c;可以高效的完成数据爬取。本文主要介绍 Scrapy 框架的构成与工作原理。 正文 1、Scrapy安装 Windows安装&…

引导程序、BIOS中断、检测内存容量、实模式切换到保护模式

初始化引导程序 基本概念 BIOS会将磁盘的第0个扇区(大小为512字节)&#xff0c;加载到0x7c00处。 引导程序负责操作系统的加载&#xff0c;主要用于为操作系统运行提供初始化环境&#xff0c;并运行加载操作系统。 BIOS只加载磁盘的第0个扇区(512字节)到内存中&#xff0c;次程…

笔记本电脑开不了机?3种解决方法

案例&#xff1a;笔记本电脑开不了机怎么办&#xff1f; 【我的笔记本电脑一直用得好好的&#xff0c;今天突然开不了机&#xff0c;尝试按了开机键很多次也没有解决。有人遇到过同样的问题吗&#xff1f;有没有解决的方法&#xff01;】 在日常生活中&#xff0c;我们经常会…

【计算机网络——计算机网络的概念,组成,功能和分类以及相关的性能指标,分层结构和协议,TCP/IP参考模型】

文章目录计算机网络体系结构计算机网络的概念、组成、功能和分类标准化工作及相关组织速率相关的性能指标时延、时延带宽积、PTT和利用率分层结构、协议、接口和服务OSI参考模型TCP IP参考模型计算机网络体系结构 计算机网络的概念、组成、功能和分类 计算机网络的概念 计算…

游戏内嵌社区服务开放,助力开发者提升玩家互动与留存

华为 HMS Core 游戏内嵌社区服务提供快速访问华为游戏中心论坛能力&#xff0c;支持玩家直接在游戏内浏览帖子和交流互动&#xff0c;助力开发者扩展内容生产和触达的场景。 一、为什么要游戏内嵌社区&#xff1f; 二、游戏内嵌社区的典型使用场景 1、游戏内打开论坛 您可以在…

【从零开始学Skynet】实战篇《球球大作战》(十三):场景代码设计(下)

1、主循环 《球球大作战》是一款服务端运算的游戏&#xff0c;一般会使用主循环程序结构&#xff0c;让服务端处理战斗逻辑。如下图所示&#xff0c;图中的balls和foods代表服务端的状态&#xff0c;在循环中执行“食物生成”“位置更新”和“碰撞检 测”等功能&#xff0c;从而…

商城系统开发方案分析

互联网的不断发展&#xff0c;电商行业已经成为了当前最重要的商业形式之一。商城系统的开发也因此而备受关注。商城系统的开发是针对B2C、B2B2C等多种商业模式&#xff0c;如用户熟知的SHOP、商派等一系列商城系统&#xff0c;将商品和服务进行在线销售的一个综合性平台。那么…

【C语言进阶:动态内存管理】常见的动态内存错误

本节重点内容&#xff1a; 对NULL指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同一块动态内存多次释放动态开辟内存忘记释放&#xff08;内存泄漏&#xff09;经典的笔试题⚡对NULL指针的解引用操作 ⚡对动态…

Linux基础命令-seq打印数字序列

Linux基础命令-sed流编辑器 前言 seq命令通常是用来打印一串有规律的数字&#xff0c;常与其他命令搭配使用&#xff0c;一起来看下它的用法。 一. 命令介绍 在doc文档中查看seq命令的含义 NAMEseq - print a sequence of numbers DESCRIPTIONPrint numbers from FIRST to…

李宏毅教程系列——增强学习

目录 0. 强化学习wiki 1. 介绍 2. Exploration vs Exploitation 探索与开发 3. 各类最优化方法 3.1 Brute force猛兽蛮力法&#xff08;暴力搜索&#xff09; 3.2 Value function estimation&#xff08;价值函数估计&#xff09; 3.2.1 Monte Carlo methods 蒙特卡洛方…

3年经验,面试测试岗只会功能测试开口要求18K,令我陷入沉思。

由于朋友临时有事&#xff0c; 所以今天我代替朋友进行一次面试&#xff0c;公司需要招聘一位自动化测试工程师&#xff0c;我以很认真负责的态度完成这个过程&#xff0c; 大概近30分钟。 主要是技术面试&#xff0c; 在近30分钟内&#xff0c; 我与被面试者是以交流学习的方式…

【Linux】通过网络版计算器来认识协议

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;再谈协议&…

腾讯云4核8G12M轻量服务器配置性能评测

腾讯云轻量4核8G12M服务器&#xff0c;之前是4核8G10M配置&#xff0c;现在公网带宽和月流量包整体升级&#xff0c;12M公网带宽下载速度可达1536KB/秒&#xff0c;系统盘为180GB SSD盘&#xff0c;每月2000GB免费流量&#xff0c;腾讯云百科来详细说下4核8G12M轻量应用服务器配…

AJAX | 拦截器、文件上传和下载

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; AJAX Ajax即Asynchronous Javascript And XML&#xff08;异步JavaScript和XML&#xff09;&#xff1b;Ajax技术网页应用能够快速地将增量更新呈现在用户界面上&…

锁子甲 bulid+sim

链接: youtube 分析&#xff1a;洒一堆点——copy 模型——点和模型符合一定规律 点和点的距离符合上述图中的关系 &#xff08;横纵&#xff09; 横向 但是我们要横向10个点够了&#xff1a; 用modulo 除余 纵向 这里用除法向上取整 /10 eg &#xff1a; 0-9 得0 10-19 得1…

redis哨兵模式配置(配置文件等)

Redis-Sentinel机制主要用三个功能&#xff1a; (1)监控&#xff1a;不停监控Redis主从节点是否安装预期运行 (2)提醒&#xff1a;如果Redis运行出现问题可以 按照配置文件中的配置项 通知客户端或者集群管理员 (3)自动故障转移&#xff1a;当主节点下线之后&#xff0c;哨兵…

【版本控制】Github同步Gitee镜像仓库自动化脚本

文章目录Github同步Gitee镜像仓库自动化脚本前言什么是Hub Mirror Action&#xff1f;1.介绍2.用法配置步骤1.生成密钥对2.GitHub私钥配置3.Gitee公钥配置4.Gitee生成私人令牌5.Github绑定Gitee令牌6.编写CI脚本7.多仓库同步推送8.定时运行脚本总结Github同步Gitee镜像仓库自动…