Bootstrap

java基础案例第六章知识总结

第六章 集合

集合(Collection)是Java中的一种重要数据结构,用于存储一组对象。在Java中,集合主要分为两大类:实现Collection接口的集合和实现Map接口的集合。接下来,我们将详细讨论这两大类集合的概念、接口、实现类及相关操作。

6.1 集合概述

集合存储结构两大类

  1. Collection接口体系:包括ListSet等接口及其实现类,用于存储一组不唯一(List允许重复,Set不允许重复)的对象。
  2. Map接口体系:用于存储键值对(Key-Value),其中键(Key)是唯一的,值(Value)可以重复。

思维图表示关系

集合框架
├── Collection接口
│ ├── List接口
│ │ ├── ArrayList
│ │ ├── LinkedList
│ │ └── ...
│ └── Set接口
│ ├── HashSet
│ ├── TreeSet
│ └── ...
└── Map接口
├── HashMap
├── TreeMap
├── Properties
└── ...

6.2 Collection 接口

概念

Collection接口是Java集合框架中的根接口,提供了对集合对象进行基本操作的通用方法。

使用场景及条件
  • 当你需要存储一组对象,而不关心对象存储的顺序和是否唯一时,可以使用Collection接口及其实现类。
注意事项
  • Collection接口不支持存储重复元素(除List外),且不允许包含null元素(具体实现类可能有所不同)。

常用方法

方法声明功能描述
boolean add(E e)向集合中添加一个元素,若成功返回true
boolean remove(Object o)从集合中移除一个元素,若成功返回true
boolean contains(Object o)判断集合中是否包含某个元素
int size()返回集合中元素的个数
boolean isEmpty()判断集合是否为空
void clear()清空集合中的所有元素
Iterator<E> iterator()返回一个迭代器用于遍历集合
Object[] toArray()将集合转换为数组
<T> T[] toArray(T[] a)将集合转换为指定类型的数组
boolean containsAll(Collection<?> c)判断集合是否包含另一个集合的所有元素
boolean addAll(Collection<? extends E> c)向集合中添加另一个集合的所有元素
boolean removeAll(Collection<?> c)从集合中移除另一个集合的所有元素
boolean retainAll(Collection<?> c)保留集合中与另一个集合相同的元素

6.3 List接口

6.3.1 List接口简介

List接口是Collection接口的子接口,用于存储有序的集合,允许元素重复。

常用方法

方法声明功能描述
E get(int index)获取指定位置的元素
E set(int index, E element)将指定位置的元素替换为新的元素,并返回旧元素
void add(int index, E element)在指定位置插入元素
E remove(int index)移除指定位置的元素,并返回该元素
int indexOf(Object o)返回指定元素在集合中首次出现的位置
int lastIndexOf(Object o)返回指定元素在集合中最后一次出现的位置
List<E> subList(int fromIndex, int toIndex)返回集合中指定范围的子列表(视图)
boolean isEmpty()判断列表是否为空
使用场景及条件
  • 当你需要存储一组有序的对象,且允许对象重复时,可以使用List接口及其实现类。
注意事项
  • List接口的实现类如ArrayListLinkedList在底层实现上有所不同,ArrayList基于数组实现,LinkedList基于链表实现,因此它们在性能上各有优劣。

6.3.2 ArrayList集合

ArrayList是基于数组实现的List接口,支持动态扩容。

6.3.3 LinkedList集合

LinkedList是基于链表实现的List接口,支持高效的插入和删除操作。

LinkedList集合新增和删除元素过程
  • 新增元素:在链表尾部新增元素时,只需调整尾指针和新增节点的指针;在链表中间或头部新增元素时,需要遍历找到插入位置,并调整相关节点的指针。
  • 删除元素:在链表尾部删除元素时,只需调整尾指针和前一个节点的指针;在链表中间或头部删除元素时,需要遍历找到删除位置,并调整相关节点的指针。

6.3.4 Iterator接口

概念

Iterator接口用于遍历集合中的元素,是一种通用的迭代器设计模式。

使用场景及条件
  • 当你需要遍历集合中的元素,而不关心元素的存储顺序时,可以使用Iterator接口。
注意事项
  • 在使用Iterator遍历集合时,不能通过集合对象的方法修改集合(如添加、删除元素),否则会抛出ConcurrentModificationException异常。
Iterator对象迭代元素的过程
  1. 调用集合的iterator()方法获取Iterator对象。
  2. 使用Iterator对象的hasNext()方法判断集合中是否还有元素。
  3. 使用Iterator对象的next()方法获取下一个元素。
  4. 可以使用Iterator对象的remove()方法删除迭代器上一次返回的元素(可选)。

6.3.5 foreach 循环

概念

foreach循环(也称为增强型for循环)是Java 5引入的一种简化数组和集合遍历的语法。

使用场景及条件
  • 当你需要遍历数组或集合中的元素时,可以使用foreach循环。
注意事项
  • foreach循环实际上是通过迭代器来实现的,因此它不支持在遍历过程中修改集合(如添加、删除元素)。
具体语法格式
for (类型 元素 : 数组或集合) {
// 循环体
}
foreach循环的局限性
  • 无法在遍历过程中修改集合(如添加、删除元素)。
  • 无法获取元素的索引(数组除外)。

6.4 Set接口

6.4.1 Set接口简介

Set接口是Collection接口的子接口,用于存储不重复的元素。

使用场景及条件
  • 当你需要存储一组不重复的对象时,可以使用Set接口及其实现类。
注意事项
  • Set接口的实现类如HashSetTreeSet在底层实现上有所不同,HashSet基于哈希表实现,TreeSet基于红黑树实现,因此它们在性能上各有优劣。

6.4.2 HashSet集合

概念

HashSet是基于哈希表实现的Set接口,不保证集合的迭代顺序。

使用场景及条件
  • 当你需要存储一组不重复的对象,且不关心对象的存储顺序时,可以使用HashSet
注意事项
  • HashSet允许null元素的存在,但最多只能有一个null元素。
  • HashSet不保证集合的迭代顺序,因此迭代顺序可能与添加顺序不同。
HashSet存储元素的流程
  1. 调用HashSetadd方法添加元素。
  2. 计算元素的哈希值,并定位到哈希表中的桶(Bucket)。
  3. 如果桶中为空,则直接将元素添加到桶中。
  4. 如果桶中已有元素,则比较元素的哈希值和equals方法,如果相同则不添加,否则链式存储到桶中。

6.4.3 TreeSet集合

概念

TreeSet是基于红黑树实现的Set接口,保证集合的元素处于排序状态。

使用场景及条件
  • 当你需要存储一组不重复的对象,且希望集合中的元素处于排序状态时,可以使用TreeSet
注意事项
  • TreeSet不允许null元素的存在。
  • TreeSet中的元素必须实现Comparable接口,或者构造TreeSet时传入一个Comparator对象,否则在添加元素时会抛出ClassCastException异常。
6.5 Map 接口及常用实现
6.5.1 Map 接口简介

概念
Map接口位于java.util包中,专门用于表示一组键值对映射。Map中的每个键与值一一对应,且键是唯一的。Map本身并不继承自Collection接口,但它提供了一些类似于集合的操作方法。

使用场景

  • 需要键值对存储时,如数据库查询结果、缓存等。
  • 需要快速根据键查找值时。

使用条件

  • 键必须是唯一的,不能重复。
  • 值可以重复。

为什么要用

  • 提供了灵活的键值对存储和查找机制。
  • 可以根据键快速获取值,性能高效。

注意事项

  • 选择合适的Map实现类,如HashMap、TreeMap、Properties等,根据具体需求选择。
  • 注意线程安全性和性能之间的平衡。

常用方法

方法声明功能描述
V put(K key, V value)将指定的键值对插入Map中,返回以前的值(如果有)或null。
V get(Object key)返回指定键所映射的值,如果不包含该键,则返回null。
V remove(Object key)移除键对应的键值对,返回被移除的值。
boolean containsKey(Object key)如果Map中包含指定的键,则返回true。
boolean containsValue(Object value)如果Map中存在一个或多个键映射到指定值,则返回true。
Set<K> keySet()返回Map中所有键的集合。
Collection<V> values()返回Map中所有值的集合。
Set<Map.Entry<K, V>> entrySet()返回Map中所有键值对的集合视图。
V replace(K key, V value)替换指定键的值。
boolean replace(K key, V oldValue, V newValue)如果当前映射包含指定键的指定值,则替换该值。
V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)尝试计算指定键及其当前映射值的重新映射函数。
V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)如果指定键尚未与某个值相关联(或关联为null),则将其与给定值关联,或者如果已关联一个值,则使用给定的重新映射函数计算新值。
6.5.2 HashMap 集合

概念
HashMap是Java集合框架中的一个重要实现,用于存储键值对。它基于哈希表的原理,通过哈希函数将键映射到桶(bucket)中,从而实现快速的插入、删除和查找操作。

使用场景

  • 适用于大多数对性能要求较高且不需要线程安全的场合。
  • 需要快速插入、删除和查找键值对时。

使用条件

  • 允许使用null值和null键。
  • 不保证键值对的顺序。

为什么要用

  • 提供了高效的键值对存储和查找机制。
  • 性能优于其他Map实现类(如TreeMap)。

注意事项

  • 扩容机制:当元素数量超过容量乘以负载因子(默认0.75)时,HashMap会进行扩容,将容量扩大为原来的两倍,并重新哈希所有元素。
  • 链表和红黑树:当多个键的哈希值相同时,这些键会被存储在同一个桶中,形成一个链表。在Java 8及以后,当链表长度超过一定阈值(默认是8)时,链表会转换为红黑树,以提高在高碰撞情况下的性能。
6.5.3 TreeMap 集合

概念
TreeMap是一种基于红黑树(Red-Black tree)的NavigableMap的实现。它提供了一种可排序的键值对集合。

使用场景

  • 需要对键进行排序的场合。
  • 需要按照键的顺序进行遍历的场合。

使用条件

  • 默认情况下采取自然排序,也可以在构造函数中接受一个Comparator实例来定制排序规则。
  • 不允许使用null键。

为什么要用

  • 提供了有序的键值对存储和查找机制。
  • 可以通过键的顺序进行遍历和查找。

注意事项

  • TreeMap的时间复杂度为O(log n),适用于大规模数据量下的高效存储和检索。
  • 不允许使用null键,否则会抛出NullPointerException。
6.5.4 Properties集合

概念
Properties集合是一种用于处理键值对数据的集合,它继承自Hashtable类,提供了一种方便的方式来存储和读取配置信息。

使用场景

  • 用于读取和写入配置信息,如应用程序的配置文件、数据库连接信息等。
  • 需要将键值对数据持久化到文件中的场合。

使用条件

  • 键和值都是字符串类型。
  • 线程安全,可以在多线程环境中使用。

为什么要用

  • 提供了方便的配置信息存储和读取机制。
  • 支持持久化存储和读取键值对数据。

注意事项

  • 使用setProperty(key, value)方法添加键值对。
  • 使用getProperty(key)方法获取指定键对应的值。
  • 使用load(InputStream)方法从文件中加载Properties集合的数据。
  • 使用store(OutputStream, comments)方法将Properties集合的数据保存到文件。
6.6 泛型
6.6.1 泛型概述

概念
泛型是Java SE 5引入的一个语言特性,它提供了一种在编译时进行类型检查的机制,使得代码更加安全、灵活和易于维护。

使用场景

  • 需要编写可以处理多种类型数据的类或方法时。
  • 需要提高代码的可重用性和类型安全性时。

使用条件

  • 泛型类、泛型方法、泛型接口等都需要在声明时指定泛型类型参数。

为什么要用

  • 提高了代码的可重用性和类型安全性。
  • 减少了运行时类型转换错误的可能性。

注意事项

  • 泛型信息在运行时会被擦除,泛型类型参数会被替换为它们的限定类型(无限定类型时则为Object)。
  • 无法创建泛型数组。
  • 无法直接实例化泛型类型参数,需要通过反射或其他方式创建实例。
6.6.2 泛型类和泛型对象

概念
泛型类是指在类定义时使用了泛型类型参数的类。泛型对象是指使用泛型类创建的实例。

声明格式

public class GenericClass<T> {
private T field;
// 构造方法、方法等
}

创建语法格式

GenericClass<String> genericObject = new GenericClass<>();

使用场景

  • 需要编写可以处理多种类型数据的类时。
  • 需要提高代码的可重用性和类型安全性时。

使用条件

  • 在创建泛型对象时需要指定具体的泛型类型参数。

为什么要用

  • 提高了代码的可重用性和类型安全性。
  • 使得类可以处理多种类型的数据,而不需要编写多个类似的类。

注意事项

  • 泛型信息在运行时会被擦除,因此无法通过反射获取泛型类型参数的具体类型。
6.6.3 泛型方法

概念
泛型方法是指在方法定义时使用了泛型类型参数的方法。

定义格式

public <T> void genericMethod(T param) {
// 方法体
}

使用场景

  • 需要编写可以处理多种类型参数的方法时。
  • 需要提高方法的可重用性和类型安全性时。

使用条件

  • 在调用泛型方法时需要指定具体的泛型类型参数(有时可以通过类型推断自动确定)。

为什么要用

  • 提高了方法的可重用性和类型安全性。
  • 使得方法可以处理多种类型的数据,而不需要编写多个类似的方法。

注意事项

  • 泛型方法可以在普通类和泛型类中定义。
  • 泛型信息在运行时会被擦除,因此无法通过反射获取泛型方法的具体类型参数。

 

6.6.4 泛型接口

概念

泛型接口是Java泛型编程的一个重要特性,它允许在接口定义时引入类型参数,从而使得接口能够处理多种数据类型,提高了代码的复用性和灵活性。

声明泛型接口格式

声明泛型接口的格式与声明泛型类类似,在接口名称后添加尖括号,并在尖括号中指定类型参数。例如:

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

定义泛型接口的子类两种方式

  1. 在子类定义时指定具体类型

    在子类实现泛型接口时,可以直接在子类名称后指定具体的类型参数。例如:

    public class MyConcreteClass implements MyGenericInterface<String> {
    @Override
    public void doSomething(String param) {
    System.out.println("Doing something with: " + param);
    }
    }
  2. 在子类定义时声明泛型类型

    子类也可以在定义时声明自己的泛型类型,然后在实现接口时使用这些泛型类型。例如:

    public class MyGenericClass<T> implements MyGenericInterface<T> {
    @Override
    public void doSomething(T param) {
    System.out.println("Doing something with generic type: " + param);
    }
    }

使用场景与条件

泛型接口常用于需要处理多种数据类型的场景,如集合框架中的List、Set、Map等接口。使用泛型接口可以确保集合中的元素类型一致,避免类型转换错误。

为什么用

使用泛型接口可以提高代码的复用性和安全性。复用性体现在可以通过泛型接口处理多种数据类型,而不需要为每种数据类型编写专门的接口。安全性则体现在编译器可以在编译时检查类型匹配,减少运行时错误。

注意事项

  • 在定义泛型接口时,要确保接口中的方法参数和返回值类型都使用了泛型类型参数。
  • 在实现泛型接口时,如果子类没有指定具体的泛型类型,则需要在子类定义时声明自己的泛型类型。
6.6.5 类型通配符

概念

类型通配符(Type Wildcard)是Java泛型编程中的另一个重要概念,它允许在泛型类型声明时使用问号(?)作为类型参数的占位符,从而提高了代码的灵活性和通用性。

使用场景与条件

类型通配符常用于需要处理未知类型的泛型集合时。例如,当你有一个List集合,但你不知道集合中元素的具体类型时,可以使用List<?>来表示。

为什么用

使用类型通配符可以提高代码的灵活性,允许在不知道具体类型的情况下操作泛型集合。同时,类型通配符还可以结合边界(extends和super)来限制泛型类型的范围,从而提高代码的安全性。

注意事项

  • 当使用类型通配符时,不能在方法内部添加具体类型的元素到集合中,因为编译器无法确定集合的具体类型。
  • 可以使用通配符捕获(Wildcard Capture)和辅助方法来处理这种情况,即在方法内部将类型通配符的集合转换为具体类型的集合。
  • 在使用类型通配符时,要注意选择合适的边界(extends或super),以确保代码的正确性和安全性。
6.7 JDK8新特性——Lambda表达式

概念

Lambda表达式是Java 8引入的一项新特性,它允许以更简洁的方式表示匿名函数或代码块,主要用于函数式编程和简化代码。

六个Lambda表达式常用的语法格式

  1. 无参数,无返回值

    Runnable runnable = () -> System.out.println("Hello Lambda!");
  2. 一个参数,无返回值

    Consumer<String> consumer = x -> System.out.println(x);
  3. 有参数,有返回值(单条语句)

    Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
  4. 有参数,有返回值(多条语句)

    Comparator<Integer> comparatorWithStatements = (x, y) -> {
    System.out.println("Comparing...");
    return Integer.compare(x, y);
    };
  5. 省略参数类型(类型推断)

    Comparator<Integer> comparatorWithInference = (Integer x, Integer y) -> Integer.compare(x, y);
    // 可以简化为:
    Comparator<Integer> comparatorWithInferenceSimplified = (x, y) -> Integer.compare(x, y);
  6. 方法引用

    Consumer<String> consumerWithMethodReference = System.out::println;

使用场景与条件

Lambda表达式常用于需要传递行为(即函数式接口的实现)的场景,如集合操作(forEach、filter、map等)、排序和比较(Comparator接口)、并行和多线程(并行流)、事件处理等。

为什么用

使用Lambda表达式可以使代码更简洁、更紧凑,同时提高代码的可读性和可维护性。此外,Lambda表达式还支持并行流和多线程编程,可以更容易地编写并发代码。

注意事项

  • Lambda表达式需要函数式接口的支持,即接口中只能有一个抽象方法。
  • 在使用Lambda表达式时,要确保方法参数和返回值类型与函数式接口中的抽象方法一致。
  • Lambda表达式中的局部变量必须是final的(或在Java 8中,实际上是“有效地final”的),即不能在Lambda表达式内部修改这些变量的值。
  • 在使用Lambda表达式进行集合操作时,要注意避免类型转换错误和空指针异常。
6.8 本章小结

 1. 集合概述
集合框架是Java中用于存储和操作一组对象的统一架构。它提供了一种标准的方式来处理对象集合,包括各种数据结构和算法。集合框架的主要目的是通过提供一种统一的操作集合对象的方式,来简化对象的管理和操作。

2. Collection 接口
Collection 是所有单列集合的根接口,它定义了集合的基本操作,如添加、删除和遍历。所有具体的集合类(如 `List`、`Set` 和 `Map`)都继承自 `Collection` 接口或其子接口。

3. List接口
List`是一个有序的集合,可以包含重复的元素。它继承自 `Collection` 接口。

3.1 List接口简介
List提供了对元素的有序访问,并且可以精确控制每个元素的插入位置。

 3.2 ArrayList集合
ArrayList 是基于动态数组实现的 `List` 接口的实现类,它允许快速随机访问,但在插入和删除操作时可能需要数组复制,影响性能。

3.3 LinkedList集合
LinkedList是基于双向链表实现的 `List` 接口的实现类,它在进行插入和删除操作时性能较好,但随机访问性能较差。

 3.4 Iterator接口
Iterator 提供了一种遍历集合的方法,它定义了 `hasNext()`、`next()` 和 `remove()` 等方法,用于遍历集合中的元素。

 3.5 foreach 循环
Java 提供了增强的 `foreach` 循环,使得遍历集合更加简洁和直观。

 4. Set接口
Set 是一个不允许重复的集合。

 4.1 Set接口简介
Set 接口继承自 Collection,它的实现类必须保证元素的唯一性。

 4.2 HashSet集合
HashSet 是基于哈希表实现的 `Set` 接口的实现类,它提供了快速的插入、删除和查找操作。

4.3 TreeSet集合
TreeSet 是基于红黑树实现的 `Set` 接口的实现类,它可以按照自然顺序或自定义顺序对元素进行排序。

5. Map接口
Map 提供了键值对的存储,它继承自 `Collection` 接口。

 5.1 Map 接口简介
Map接口存储键值对,它不允许键重复,但允许值重复。

5.2 HashMap 集合
HashMap是基于哈希表实现的 `Map` 接口的实现类,它提供了快速的键值对插入、删除和查找操作。

 5.3 TreeMap 集合
TreeMap是基于红黑树实现的 `Map` 接口的实现类,它可以按照自然顺序或自定义顺序对键进行排序。

5.4 Properties集合
Properties 是 `Hashtable` 的子类,它继承自 `Map` 接口,用于处理属性文件。

 6. 泛型
泛型提供了一种类型安全的机制,允许在编译时检查类型错误。

6.1 泛型概述
泛型允许在类、接口和方法中使用类型参数,从而提高代码的复用性和类型安全性。

 6.2 泛型类和泛型对象
可以创建泛型类和对象,使用类型参数来指定具体的类型。

6.3 泛型方法
可以创建泛型方法,它们在方法级别使用类型参数。

 6.4 泛型接口
可以创建泛型接口,它们在接口级别使用类型参数。

 6.5 类型通配符
类型通配符提供了一种方式来表示不同类型的集合,如 `List<?>` 表示任何类型的 `List`。

7. JDK8新特性——Lambda表达式
Lambda表达式是JDK 8引入的新特性,它允许你以简洁的方式表示单方法接口的实现。
 

;