目录
(2)使用 forEach 方法和 Stream API(Java 8及以上)
Java的集合类主要由两个接口派生而出:Collection接口和Map接口。
核心集合接口封装了不同类型的集合,这些接口允许独立于集合表示的细节来操作集合。核心集合接口是Java集合框架的基础。// 注意,Map和Collection是相互独立的
1、Java Collection 接口详解
当涉及Java中的集合框架时,Collection接口是一个重要的核心接口,定义了对一组对象进行操作和管理的通用方法。下面是对Java Collection接口的详细解释:
1)概述:
- Collection接口是Java集合框架的根接口之一,位于java.util包中。
- Collection接口表示一组对象的集合,这些对象可以是任何类型,包括基本类型的包装类和用户自定义类。
- Collection接口是其他集合接口(如List和Set)的父接口,它提供了一组通用的方法,这些方法可以在不同类型的集合上进行操作。// 线性数据结构的通用接口(Map是键值对映射)
2)常用方法:// 对一个数据容器的增删改查操作
- int size():返回集合中的元素数量。
- boolean isEmpty():检查集合是否为空。
- boolean contains(Object obj):检查集合是否包含指定元素。
- boolean add(E element):向集合中添加一个元素。
- boolean remove(Object obj):从集合中移除指定元素。
- void clear():清空集合中的所有元素。
- Iterator<E> iterator():返回一个用于遍历集合的迭代器。
3)迭代器(Iterator):
- Collection接口提供了iterator()方法,用于获取一个实现了Iterator接口的迭代器对象。
- 迭代器可以用于按顺序遍历集合中的元素,通过调用hasNext()方法判断是否还有下一个元素,调用next()方法获取下一个元素。// 迭代器是一种设计模式,通过代码实现
- 迭代器还提供了remove()方法,用于从集合中移除当前元素。
4)数组与集合的转换:
Collection接口提供了两个方法,可以将集合转换为数组,或者将数组转换为集合:
- Object[] toArray():将集合转换为一个对象数组。
- <T> T[] toArray(T[] array):将集合转换为指定类型的数组。
// Collection接口本来就是线性数据结构的通用接口,转为数组也很正常
5)equals() 和 hashCode():
Collection接口继承了equals()和hashCode()方法,用于判断集合的相等性和计算集合的哈希码。
- equals()方法用于比较两个集合是否包含相同的元素,通常需要实现类重写该方法。
- hashCode()方法用于计算集合的哈希码,通常需要实现类重写该方法。
// 使用哈希码可以进行高效的查找和存储操作。需要注意的是,哈希码并不是唯一的,即不同的对象可能具有相同的哈希码(哈希冲突)
6)并发性:
- Collection接口不是线程安全的,如果在多线程环境下使用,需要进行额外的同步控制。
- Java提供了并发集合类,如ConcurrentHashMap和ConcurrentLinkedQueue,用于在多线程环境下进行安全的并发操作。
理解Java Collection接口,可以更好地使用和管理集合对象,并了解集合框架的常用操作和特性。此外,Java提供了许多实现Collection接口的类,包括ArrayList、LinkedList、HashSet、TreeSet等,它们提供了不同的数据结构和特性,可以根据实际需求选择合适的实现类来操作和管理集合。
(1)Java集合之List接口
List接口是Java集合框架中的一种有序、可重复的集合接口,位于java.util包中。它继承自Collection接口,并在其基础上提供了一系列操作有序元素的方法。下面是对List接口的详细解释:
1)概述://有序可重合
- List接口表示一个有序的集合,其中的元素可以按照插入顺序进行访问。
- List允许存储重复的元素,同一个元素可以出现多次。
- List接口继承了Collection接口,因此它包含了Collection接口中定义的大部分方法。
2)主要方法:
添加元素:
- boolean add(E element): 将元素添加到列表的末尾。
- void add(int index, E element): 在指定位置插入元素。
访问元素:
- E get(int index): 获取指定位置的元素。
- int indexOf(Object element): 返回指定元素第一次出现的位置。
修改元素:
- E set(int index, E element): 替换指定位置的元素。
删除元素:
- E remove(int index): 删除指定位置的元素。
- boolean remove(Object element): 删除第一次出现的指定元素。
其他方法:
- int size(): 返回列表的元素个数。
- boolean contains(Object element): 判断列表是否包含指定元素。
- boolean isEmpty(): 判断列表是否为空。
- void clear(): 清空列表中的所有元素。
- Iterator<E> iterator(): 返回一个迭代器,用于遍历列表中的元素。
3)常见实现类:
- ArrayList:基于动态数组实现,支持快速随机访问和插入/删除操作,但对于频繁的插入/删除操作性能较差。// 快速查询
- LinkedList:基于双向链表实现,支持快速插入/删除操作,但随机访问的性能较差。//快速修改
- Vector:与ArrayList类似,但是是线程安全的,通常在多线程环境中使用。
- Stack:继承自Vector,实现了后进先出(LIFO)的堆栈数据结构。
需要注意的是,List接口提供了根据索引访问和操作元素的方法,因此可以根据索引来操作列表中的元素顺序。另外,List接口还实现了Iterable接口,因此可以使用增强的for循环(foreach循环)来遍历列表中的元素。
(2)Java集合之Set接口
Set接口是Java集合框架中的一种集合类型,位于java.util包中。它表示一组不重复的元素,不保证元素的顺序。下面是对Set接口的详细解释:// 跟List对应(可重复、有序)
1)概述:
- Set接口表示一个无序的集合,其中的元素不重复。
- Set不保证元素的顺序,即不维护元素的插入顺序或访问顺序。
- Set接口继承自Collection接口,因此它包含了Collection接口中定义的大部分方法。
2)主要方法:
添加元素:
- boolean add(E element): 将元素添加到Set中。
- boolean addAll(Collection<? extends E> collection): 将一个集合中的元素添加到Set中。
访问元素:
- boolean contains(Object element): 判断Set中是否包含指定元素。
- boolean containsAll(Collection<?> collection): 判断Set是否包含另一个集合中的所有元素。
- Iterator<E> iterator(): 返回一个迭代器,用于遍历Set中的元素。
删除元素:
- boolean remove(Object element): 从Set中删除指定元素。
- boolean removeAll(Collection<?> collection): 删除Set中与另一个集合中相同的元素。
- boolean retainAll(Collection<?> collection): 保留Set中与另一个集合中相同的元素,删除其他元素。
其他方法:
- int size(): 返回Set中的元素个数。
- boolean isEmpty(): 判断Set是否为空。
- void clear(): 清空Set中的所有元素。
常见实现类:
- HashSet:基于哈希表实现,它通过哈希码来存储和访问元素,具有快速的插入、删除和查找操作。// 快速访问,底层是Hash表
- TreeSet:基于红黑树实现,它能够对元素进行排序,并提供了一些有序的集合操作。
- LinkedHashSet:基于哈希表和链表实现,它保留了元素的插入顺序,同时具有HashSet的快速访问特性。
Set接口不允许存储重复的元素。当向Set中添加元素时,如果元素已经存在,则添加操作将被忽略,不会导致Set中出现重复的元素。Set接口提供了高效的元素查找和判断功能,适用于需要检查元素是否存在或需要去重的场景。根据具体的需求,选择合适的Set实现类能够满足不同的性能和排序要求。
(2)Java集合之Queue接口
Queue接口是Java集合框架中的一种接口,位于java.util包中。它代表了一种队列(Queue)数据结构,用于存储和操作一组元素。Queue接口定义了一些常用的队列操作,例如添加元素、获取元素、删除元素等。下面是对Queue接口的详细解释:// 队列是有序的,可以用来保证排队顺序
1)概述:
- Queue接口代表了一种先进先出(FIFO)的数据结构,其中元素按照添加的顺序进行排列。
- Queue接口继承自Collection接口,因此它包含了Collection接口中定义的一些方法,以及新增的一些队列操作方法。
2)主要方法:
添加元素:
- boolean add(E element): 将指定元素添加到队列中。如果队列已满,抛出异常。
- boolean offer(E element): 将指定元素添加到队列中。如果队列已满,返回false。
获取元素:
- E element(): 获取队列的头部元素,但不删除该元素。如果队列为空,抛出异常。
- E peek(): 获取队列的头部元素,但不删除该元素。如果队列为空,返回null。
删除元素:
- E remove(): 获取并删除队列的头部元素。如果队列为空,抛出异常。
- E poll(): 获取并删除队列的头部元素。如果队列为空,返回null。
其他方法:
- int size(): 返回队列中的元素个数。
- boolean isEmpty(): 判断队列是否为空。
- void clear(): 清空队列中的所有元素。
常见实现类:// Queue有很多种实现形式,底层数据结构不一样,性能也不一样
- LinkedList:基于双向链表实现的队列。它既可以作为List集合使用,也可以作为Queue队列使用。
- ArrayDeque:基于数组实现的双端队列。它既可以作为Queue队列使用,也可以作为Deque双端队列使用。
- PriorityQueue:基于优先级堆实现的优先级队列。它按照元素的优先级进行排序,优先级高的元素先被取出。
Queue接口是一种有序的队列,它遵循先进先出(FIFO)的原则。元素添加到队列的末尾,从队列的头部获取和删除元素。Queue接口的实现类可以根据具体的需求进行选择,例如LinkedList常用于实现普通队列,ArrayDeque常用于实现双端队列,PriorityQueue常用于实现优先级队列等。
该部分的详细使用请参考这篇文章《Java 数据结构之队列(Queue)详解》
2、Java Map接口详解
Map接口是Java集合框架中的一种接口,位于java.util包中。它代表了一种映射(Mapping)关系,用于存储键-值对。Map接口提供了根据键来获取值、添加键值对、删除键值对等常用操作。下面是对Map接口的详细解释:
1)概述:
- Map接口表示了一种键值对的映射关系,其中的键和值都可以是任意的对象。
- Map中的键是唯一的,每个键只能关联一个值。
- Map接口继承自Collection接口中的Iterable和size方法,同时定义了一些额外的操作方法。
2)主要方法:
添加键值对:
- V put(K key, V value): 将指定的键和值添加到Map中。如果键已存在,将替换原有的值,并返回被替换的值。
- void putAll(Map<? extends K, ? extends V> map): 将另一个Map中的所有键值对添加到当前Map中。
获取值:
- V get(Object key): 根据键获取对应的值。如果键不存在,则返回null。
- boolean containsKey(Object key): 判断Map中是否包含指定的键。
- boolean containsValue(Object value): 判断Map中是否包含指定的值。
删除键值对:
- V remove(Object key): 根据键删除对应的键值对,并返回被删除的值。
- void clear(): 清空Map中的所有键值对。
其他方法:
- int size(): 返回Map中键值对的个数。
- boolean isEmpty(): 判断Map是否为空。
- Set<K> keySet(): 返回包含Map中所有键的Set集合。
- Collection<V> values(): 返回包含Map中所有值的Collection集合。
- Set<Map.Entry<K, V>> entrySet(): 返回包含Map中所有键值对的Set集合。
常见实现类:
- HashMap:基于哈希表实现的Map,它使用键的哈希码来存储和访问键值对,具有快速的查找和插入操作。
- TreeMap:基于红黑树实现的有序Map,它按照键的自然顺序或指定的比较器对键进行排序。
- LinkedHashMap:基于哈希表和双向链表实现的Map,它保留了键的插入顺序或访问顺序。
Map接口提供了丰富的操作方法,可以根据键来获取、添加、删除和修改对应的值。通过Map接口,可以实现键值对的存储和检索,非常适合需要根据键快速查找对应值的场景。
3、遍历集合的三种方式
在Java中,遍历集合可以使用多种方式,具体选择哪种方式取决于集合的类型和遍历的需求。下面是常用的遍历集合的方式:
(1)使用增强for循环(foreach循环)
增强for循环是一种简化遍历集合的语法。它可以直接遍历数组或实现了Iterable接口的集合类。示例:
List<String> list = new ArrayList<>();
// 添加元素到list中
for (String element : list) {
// 处理元素
}
(2)使用 forEach 方法和 Stream API(Java 8及以上)
针对Java 8及以上版本,集合类提供了forEach方法,可以传入一个Lambda表达式或方法引用来处理每个元素。示例:
List<String> list = new ArrayList<>();
// 添加元素到list中
list.forEach(element -> {
// 处理元素
});
Stream API提供了丰富的流操作,包括过滤、映射、排序等,也可以用于遍历集合。通过将集合转换为流,可以使用Stream API提供的各种操作来处理元素。示例:
List<String> list = new ArrayList<>();
// 添加元素到list中
list.stream().forEach(element -> {
// 处理元素
});
(3)使用迭代器(Iterator)
Iterator是一个对象,能够遍历集合,并根据需要有选择地从集合中删除元素。通过调用集合的Iterator方法,可以获得集合的Iterator。下面是Iterator接口。
package java.util;
import java.util.function.Consumer;
/**
* 迭代器允许调用者在迭代期间从集合中删除元素。
* Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
*/
public interface Iterator<E> {
// 如果迭代有更多的元素,返回 true
boolean hasNext();
// 返回迭代中的下一个元素。
E next();
// 从基础集合中移除此迭代器返回的最后一个元素(可选操作)。
default void remove() {
throw new UnsupportedOperationException("remove");
}
// 对剩余的每个元素执行给定的操作,直到所有元素都处理完毕或该操作引发异常。
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
如果迭代有更多的元素,则hasNext方法返回true,next方法返回迭代中的下一个元素。remove方法从基础集合中删除next返回的最后一个元素。每次调用next时,只能调用remove方法一次,如果违反此规则,则会抛出异常。
注意: Iterator.remove()
是在集合遍历时修改集合的唯一安全方法
以下情况,需要使用Iterator替换for-each构造遍历集合:
- 遍历集合时删除集合中的元素。for-each结构隐藏了迭代器,不能调用remove。
- 并行遍历多个集合。
for-each构造遍历时删除元素会导致被删除元素后的元素在集合中位置前移,导致结果与预期不同
下面的方法展示了如何使用Iterator来遍历集合,同时删除特定的元素。
static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); )
if (!cond(it.next()))
it.remove();
}
这段代码是多态的,这意味着无论实现如何,它都适用于任何集合。完整的例子:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> c = new ArrayList<>();
c.add("A");
c.add("B");
c.add("C");
for (Iterator<String> it = c.iterator(); it.hasNext(); ) {
if (cond(it.next())) {
it.remove();
}
}
// Iterator<String> it = c.iterator();
// while(it.hasNext()){ // 判断迭代中是否还有元素
// String next = it.next(); // 返回迭代中的下一个元素
// if (next.equals("B"))
// it.remove();
// }
c.forEach(System.out::println);
}
// 删除条件
public static Boolean cond(String str) {
if (str.equals("A")) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}
4、集合中的批量操作
批量操作是对整个集合执行操作。
containsAll
— 如果目标集合包含指定集合中的所有元素,则返回true。addAll
— 将指定集合中的所有元素添加到目标集合。(并集)removeAll
— 从目标集合中删除同样包含在指定集合中的所有元素。(差集)retainAll
— 从目标集合中移除指定集合中不包含的所有元素。也就是说,它只保留目标集合中同时包含在指定集合中的那些元素。(交集)clear
— 从集合中删除所有元素。
比如,从Collection c中删除指定元素e的所有实例。
c.removeAll(Collections.singleton(e));
或者希望从集合中删除所有空元素。
c.removeAll(Collections.singleton(null));
3、集合中的数组操作 Array Operations
提供toArray方法是为了集合能够和使用数组的旧api之间进行兼容。集合的数组操作允许将集合的内容转换为数组。
列如,将集合内容转储到一个新分配的数组中
Object[] a = c.toArray(); // 不指定类型
String[] a = c.toArray(new String[0]); // 指定类型
附:java集合具体实现的总结:
关于Java集合可以参考的文章:
集合各实现类的底层实现原理_一条很老的腊肉的博客-CSDN博客