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

news2024/9/25 19:23:10

一、集合

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对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Nav2】Ubuntu18+ROS2 Eloquent跑通Navigation2仿真示例

【背景】 打算通过Navigation2来了解ROS2的核心两大件——LifeCircle和BehaviorTree&#xff0c;结果根据官网的教程一顿鼓捣&#xff0c;这个Turtlbot3的仿真就是跑不起来&#xff0c;这怎么能忍&#xff1f;虽然在Ubuntu20上使用Foxy版本可以非常容易就跑通demo&#xff0c;…

3年外包终上岸,我只能说:但凡有点机会,千万别去外包...

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司的软件测试岗&#xff0c;一干就是3年。现在终于跳槽到了互联网公司了&#xff0c;我想说的是&#xff0c;但凡有点机会&am…

behaviac —— Win10下Vs2017编译“腾讯行为树“源码

简介 - 腾讯行为树 behaviac是游戏AI的开发框架组件,也是游戏原型的快速设计工具。支持全平台,适用于客户端和服务器,助力游戏快速迭代开发 。编辑器可以运行在PC上,操作方便直观可靠,支持实时和离线调试;编辑器可以导出xml,bson等多种格式,更可以导出C++、C#源码,提供…

免费1年服务器,部署个ChatGPT专属网页版

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 白皮袄个免费1年服务器&#xff0c;部署个ChatGPT专属网页版&#xff01; api.openai.com port 443: Connection timed out 你是…

Spring Security --- 基于内存模型创建用户角色

授权实现方式 基于内存模型实现授权基于默认数据库模型实现授权基于自定义数据库模型实现授权 基于内存模型创建用户角色 在Spring Security4.x版本中&#xff0c;登陆的用户有一个默认的ROLE_USER角色但是在Spring Security5.x版本中&#xff0c;把这个默认的角色给去掉了需要…

推荐一个3D建模工具集

3D建模工具集, 收录一下几个工具集&#xff1a;数字孪生编辑器 基于WebGL技术&#xff0c;依托丰富的模型资产库&#xff0c;通过拖拽式的操作&#xff0c;方便用户高效便捷的搭建三维数字孪生场景&#xff0c;配合twin服务平台&#xff0c;实现孪生设备姿态控制的虚实…

2023年第十四届蓝桥杯 C++ B组参赛经历和总结

2023年第十四届蓝桥杯 C B组参赛经历和总结 目录2023年第十四届蓝桥杯 C B组参赛经历和总结前言走上算法之路备考备战之路蓝桥杯比赛过程感受总结值不值得打备赛建议前言 写这篇博客的缘故&#xff0c;因为看到好几篇记录自己蓝桥杯经历的博客&#xff0c;于是乎我也想写一篇&…

【论文精读】Arxiv 2023 - Segment Anything

【论文精读】Arxiv 2023 - 分割一切 【论文原文】&#xff1a;Segment Anything 【作者信息】&#xff1a;Kirillov, Alexander and Mintun, Eric and Ravi, Nikhila and Mao, Hanzi and Rolland, Chloe and Gustafson, Laura and Xiao, Tete and Whitehead, Spencer and Ber…

如何设计帮助中心才能真正地帮助客户解决问题?

对于当今如此智能的时代&#xff0c;大多数人都习惯性地自己解决问题&#xff0c;所以在浏览某个网站或是使用某个产品遇到问题时&#xff0c;第一反应不再是找客服&#xff0c;而是到帮助中心去寻找解决问题的办法&#xff0c;因此&#xff0c;帮助中心变得越来越重要了。 那…

c# wpf log 调试 输出窗口

需求 刚好需要新手入门开发一个WPF界面&#xff0c;所以需要一些日志输出 其实我们只是简单的入门调试&#xff0c;只需要很简单的输出 真不需要log4net, expression等等比较长期地&#xff0c;跨度比较大的日志系统 而且这些日志系统接入也比较麻烦 有没办法做一个简单的…

多线程的使用与解释

多线程 文章目录多线程什么是多线程线程特点线程的使用线程的创建线程ID获得线程终止线程等待进程分离什么是多线程 是资源调用的最小单位。一个进程内部的控制序列。线程是调度的基本单位。 线程有共享进程的数据&#xff0c;也有自己 的一部分数据 线程特点 1&#xff0c…

电脑回收站删除的文件还能找回吗 电脑回收站删除的文件怎么恢复

电脑回收站是保护电脑文件和数据的重要屏障&#xff0c;被删除的文件数据在这里会被保存很久&#xff0c;直到被用户永久删除。为了保证电脑系统的流畅运行&#xff0c;我们会对电脑回收站进行清理。在一系列的操作过后&#xff0c;我们可能会发现自己误删了重要文件。那么电脑…

Leetcode135. 分发糖果

Every day a leetcode 题目来源&#xff1a;135. 分发糖果 解法1&#xff1a;贪心 首先把所有孩子的糖果数初始化为1。 从左往右遍历一遍&#xff0c;如果右边孩子的评分比左边的高&#xff0c;则右边孩子的糖果数更新为左边孩子的糖果数加1。 再从右往左遍历一遍&#xf…

匈牙利算法学习记录

匈牙利算法主要用来解决两个问题&#xff1a;求二分图的最大匹配数和最小点覆盖数。 匈牙利算法事实上有两个算法&#xff0c;分别解决指派问题和二分图最大匹配求解问题&#xff0c;此处算法指求解指派问题的匈牙利算法。 处理流程 方法一 具体如何实现呢&#xff1f; 代码…

【数据结构】- 初识数据结构之时间复杂度(上)

文章目录前言一、什么是数据结构二、什么是算法三、算法效率3.1如何衡量一个算法的好坏3.2算法复杂度四、时间复杂度4.1时间复杂度的概念4.2大O的渐进表示法4.3常见时间复杂度计算举例总结前言 努力不是为了和别人一较高下 而是为了让生活多一种可能 别让世俗淹没生活的浪漫和…

MySQL:基本常识介绍、操作数据库、操作数据库中的表、操作表中的数据(增删改查)、MySQL 函数

文章目录Day 02&#xff1a;一、常见的 SQL 语句二、基本常识1. 数据库的列类型2. 数据库的字段属性三、操作数据库1. 操作数据库2. 操作数据库中的表&#xff08;1&#xff09;创建表&#xff1a;CREAT&#xff08;2&#xff09;修改表&#xff1a;ALTER&#xff08;3&#xf…

肖 sir_就业课__014python讲解

python讲解 一、python梳理 1、python 数据类型有哪些&#xff1f; 字符、列表、元组、字典、集合 2、列表、元组、字典、集合的区别&#xff1f; 3、python中函数&#xff1f; &#xff08;1&#xff09;自定义函数 def 函数名&#xff08;&#xff09; &#xff08;2&#…

聊聊架构方案选择

大家好&#xff0c;我是易安&#xff01; 在完成备选方案设计后&#xff0c;如何挑选最终的方案是一个很大的挑战&#xff0c;因为每个备选方案都是可行的。但是&#xff0c;没有哪个备选方案是完美的&#xff0c;因为每个方案都存在一些缺点或风险。此外&#xff0c;评价备选方…

薅!无魔法无限量GPT-4安卓App安装包;Notion AI从入门到精通;最全大模型进展汇总;雇AI给我打零工 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『大模型进展汇总 (持续更新至4月17日)』应该是最全总结了吧 ShowMeAI资料编号 No.T001 &#xff08;进入社群获取高清PDF文件&#x…

AI已经解锁自动化能力 | 颠覆商业模式和劳动力市场

AI已经解锁自动化能力&#xff0c;将颠覆商业模式和劳动力市场。目前AutoGPT的开源项目&#xff1a; BabyAGI、Auto-GPT、AgentGPT、TeenagerAGI、Jarvis。 AutoGPT原理&#xff1a; 3个GPT4协同合作&#xff0c;一个GPT4负责分解目标创建任务&#xff0c;另一个GPT4负责分配…