集合2(List和Set)
一.迭代器Iterator
是Collection,Set,Queue的父接口
Iterable
是一个接口,表示对象可以被迭代,也就是可以被遍历。
1.方法
修饰符 | 方法名 | 描述 |
---|---|---|
default``void | forEach(Consumer<? super T> action) | 对 Iterable 的每个元素执行给定的操作,直到处理完所有元素或操作引发异常。 |
Iterator<T> | iterator() | 返回类型为 T 的元素的迭代器。 |
default``Spliterator<T> | spliterator() | 在这个 Iterable 描述的元素上创建一个 Spliterator 。 |
2.与for循环的区别
首先我们集合中arraylist添加元素
ArrayList<String> list = new ArrayList();
list.add(null);
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("c");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if ("d".equals(s)){
list.remove(i);
}
System.out.print(s);
这里需要注意的一点是,因为i循环条件是size(),yc,随着删除d元素,size也会变小,
例如e原来索引是i=5,删除后:i=4,因此输出时会略过删除元素的后一个元素
输出结果:null a b c c
Iterator
// for (String str : list){
// if ("c".equals(str)){
// list.remove(str);
// }
// System.out.println(str);
// }
但是此处会报错:Exception in thread “main” java.util.ConcurrentModificationException
这里当删除的元素是索引倒数第二位的时候,会删除成功,因为删除成功以后迭代器会停止迭代,但是此时最后一位元素会因为删除的原因索引-1,因此很巧妙的完成了删除
for (String str : list){
if ("e".equals(str)){
list.remove(str);
}
System.out.println(list);
}
运行
是因为代码中底层对删除和增加迭代器中设置
int expectedModCount = modCount;
当修改量expectedModCount与md不同时,会抛出异常
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size())
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
为此Iterator提供了方法:
remove
-
方法签名:
javaCopy code void remove()
-
作用:
remove()
方法从集合中删除迭代器的当前位置所指向的元素。它会修改集合的结构,通常会导致集合的大小减小。 -
支持情况:
remove()
方法是一个可选操作,不是所有的迭代器都支持它。具体支持情况取决于集合类型和迭代器的实现。通常情况下,List
接口的迭代器(如ArrayList
或LinkedList
)支持remove()
方法,而Set
接口的迭代器(如HashSet
或TreeSet
)通常不支持。 -
使用限制:
remove()
方法通常要求在调用之前必须先调用next()
方法,以确保迭代器指向一个有效的元素。如果在调用remove()
之前没有调用next()
或者已经调用过remove()
,则会抛出IllegalStateException
异常。
示例
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
if ("c".equals(str)){
// list.remove(str);
iterator.remove();
continue;
}
System.out.println(str);
}
此处可以发现,arraylist中相比其他集合不同的特性
特性
- 动态数组:
ArrayList
是基于动态数组实现的,它可以自动增长和缩小以适应元素的添加和删除。这意味着它的大小可以根据需要动态调整。 - 有序集合:
ArrayList
是有序集合,它按照元素添加的顺序来维护元素。你可以通过索引访问元素,索引从0开始,以数组的方式进行访问。 - 允许重复元素:
ArrayList
允许存储重复的元素,同一个元素可以出现多次。 - 随机访问: 由于
ArrayList
使用数组来存储元素,所以可以通过索引进行随机访问,访问速度非常快。 - 不同步:
ArrayList
不是线程安全的,这意味着在多线程环境下使用时需要考虑同步问题。如果需要线程安全的集合,可以考虑使用Vector
或Collections.synchronizedList()
方法包装一个ArrayList
。 - 支持泛型: 从 Java 5 开始,
ArrayList
支持泛型,允许你指定集合中存储的元素类型,以提高类型安全性。 - 可变大小: 你可以随时向
ArrayList
中添加元素或删除元素,而不需要事先指定容量。 - 效率:
ArrayList
在访问元素时非常高效,但在插入或删除元素时,如果需要移动后续元素,可能会较慢。因此,对于频繁插入和删除操作,考虑使用LinkedList
注:arraylist允许存放null值
二.Arraylist
LinkedList 的直接父类是 AbstractSequentialList ,实现了 List 、 Deque
vector和arraylist区别
- 线程安全性:
Vector
是线程安全的,所有方法都是同步的,这意味着多个线程可以同时访问和修改Vector
实例,而不会导致数据不一致或冲突。ArrayList
不是线程安全的,它的方法没有同步,如果多个线程同时访问或修改一个ArrayList
实例,可能会导致数据不一致或抛出并发异常。
- 性能:
- 由于
Vector
的方法都是同步的,因此它的性能通常比ArrayList
差。在多线程环境中,Vector
可能会更安全,但也更慢。 ArrayList
没有同步开销,因此在单线程环境中通常比Vector
快。
- 由于
- 增长策略:
Vector
和ArrayList
都支持自动增长,但它们的增长策略不同。Vector
的增长策略是每次增加当前容量的一半,而ArrayList
增长策略是每次增加当前容量的一倍。这意味着ArrayList
可能会比Vector
更有效地处理动态大小的数组。
- 初始容量:
Vector
的默认初始容量是 10,但可以通过构造函数指定初始容量。ArrayList
的默认初始容量也是 10,但可以根据需要进行扩展。
三.LinkedList
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
1.方法
方法名 | 返回值 | 描述 |
---|---|---|
addFirst(E e) | void | 在该列表开头插入指定的元素 |
addLast(E e) | void | 将指定的元素追加到此列表的末尾(等同于 add() ) |
get(int index) | E | 返回此列表中指定位置的元素 |
getFirst() | E | 返回此列表中的第一个元素 |
getLast() | E | 返回此列表中的最后一个元素 |
push(E e) | void | 列表的前面插入元素(等同于 addFirst() ) |
removeFirst() | E | 从此列表中删除并返回第一个元素(等同于 poll() 、pollFirst() 、pop() ) |
ArrayList / LinkedList的区别:
-
底层实现:
-
ArrayList
:基于数组实现**,默认容量是 10** …。内部使用数组来存储元素,支持随机访问,通过索引访问元素的时间复杂度为 O(1)。 -
内存, ArrayList 使用的是连续空间
-
LinkedList
:基于双向链表实现。内部使用链表来存储元素,不支持随机访问,访问元素的时间复杂度取决于元素的位置,平均为 O(n/2)。
-
-
插入和删除操作:
ArrayList
:在尾部添加元素时性能良好,但在中间或头部插入/删除元素时性能较低,因为需要移动后面的元素。LinkedList
:在头部和尾部插入/删除元素时性能良好,而在中间插入/删除元素时性能良好,因为只需修改相邻元素的引用。
-
随机访问:
ArrayList
:支持随机访问,可以通过索引直接访问元素,因此在读取元素时性能较好。LinkedList
:不支持随机访问,必须从头部或尾部开始遍历链表来访问元素,因此在读取元素时性能较差。
-
内存占用:
ArrayList
:由于需要分配一块连续的内存空间来存储元素,可能会浪费一些内存空间。LinkedList
:每个元素都包含对前后元素的引用,因此可能会占用更多的内存。
-
迭代性能:
ArrayList
:迭代时性能良好,因为可以通过索引直接访问元素。LinkedList
:迭代时性能较差,因为必须从头部或尾部开始遍历链表。
-
应用场景:
ArrayList
:适用于需要频繁读取元素的场景,如遍历、搜索。LinkedList
:适用于需要频繁插入和删除元素的场景,如队列、栈。
四.Set
不包含重复元素的集合、不能保证存储的顺序、只允许有一个 null
public interface Set<E>
extends Collection<E>
Set 集合的实现类有很多,在此我们重点了解 HashSet 、 TreeSet 、 LinkedHashSet
1.TreeSet
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
1.特点:
- 非线程安全
- 值必须可比较(元素实现 Comparable 接口、传递 比较器 Comparator 对象)
- 不能存 null
- 判断是否是重复元素,是按照自然比较/比较器进行比较
就是说a.compareTo(b) == 0,如果是 true ,那么 add(a) 之后的 add(b) 将会返回 false ,
也就是添加失败
2.常用的构造方法
方法名 | 描述 |
---|---|
TreeSet() | 构造一个新的空 TreeSet 集合,根据其元素的自然顺序进行排序。 |
TreeSet(Comparator<? super E> comparator) | 构造一个新的空 TreeSet 集合,根据指定的比较器进行排序。 |
TreeSet(Collection<? extends E> c) | 构造一个新的 TreeSet 集合,该 TreeSet 集合包含指定集合中的元素,并根据其元素的自然顺序进行排序。 |
3.常用方法
( Collection 接口的方法不在此赘述)
方法名 | 返回值 | 描述 |
---|---|---|
ceiling(E e) | E | 返回此集合中大于或等于给定元素的最小元素,如果没有这样的元素,则返回 null 。 |
first() | E | 返回当前在此集合中的第一个(最低的)元素。 |
floor(E e) | E | 返回此集合中小于或等于给定元素的最大元素,如果没有这样的元素,则返回 null 。 |
headSet(E toElement) | SortedSet<E> | 返回此集合中元素严格小于 toElement 的部分的视图。 |
higher(e) | E | 返回此集合中严格大于给定元素的最小元素,如果没有这样的元素,则返回 null 。 |
last() | E | 返回当前在此集合中的最后一个(最高的)元素。 |
lower(e) | E | 返回此集合中严格小于给定元素的最大元素,如果没有这样的元素,则返回 null 。 |
pollFirst() | E | 检索并删除第一个(最低的)元素,如果此集合为空,则返回 null 。 |
pollLast() | E | 检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。 |
tailSet(E fromElement) | SortedSet<E> | 返回此集合中元素严格大于或等于 fromElement 的部分的视图。 |
示例:
TreeSet<String> set = new TreeSet(List.of("null", "a", "a", "b", "c", "e", "f",
"g"));
System.out.println(set); // [a, b, c, e, f, g, null]
// 返回此集合中大于或等于给定元素的最小元素,如果没有这样的元素,则返回null。
String ceiling = set.ceiling("d");
System.out.println(ceiling);// e
// 返回当前在此集合中的第一个(最低的)元素。
String first = set.first();
System.out.println(first); // a
// 返回此集合中小于或等于给定元素的最大元素,如果没有这样的元素,则返回null
String floor = set.floor("d");
System.out.println(floor); // c
// 返回此集合中元素严格小于toElement的部分的视图。
SortedSet<String> headSet = set.headSet("c");
System.out.println(headSet); // a, b
// 返回此集合中严格大于给定元素的最小元素,如果没有这样的元素,则返回null。
String higher = set.higher("c");
System.out.println(higher); // e
// 返回此集合中元素严格大于或等于fromElement的部分的视图。
SortedSet<String> tailSet = set.tailSet("c");
System.out.println(tailSet); // c, e, f, g, null
// 迭代
for (Object obj : set){
System.out.println(obj);
}
HashSet
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable
特点:
- 实现了 Set 接口,底层实现是 HashMap 。不保证迭代顺序,允许 null 元素
- 非线程安全的
- 如果 add 的值已存在( equals 方法返回 true ,基本数据类型自动装箱)返回 false
- 如果 HashSet 中存的是对象,需要重写此对象类中的 equals 和 hashCode() 方法
1.构造方法
方法名 | 描述 |
---|---|
HashSet() | 构造一个新的空集合,默认初始容量为 16,负载因子为 0.75。底层实现是 HashMap 。 |
HashSet(Collection<? extends E> c) | 构造一个新的集合,包含指定集合中的元素。 |
HashSet(int initialCapacity) | 构造一个新的空集合,指定初始容量。 |
HashSet(int initialCapacity, float loadFactor) | 构造一个新的空集合,指定初始容量和负载因子。(负载因子指到达容量多少进行扩容) |
示例:
import java.util.HashSet;
public class HashSetExample {
public static void main(String[] args) {
// 构造一个新的空集合,默认初始容量为 16,负载因子为 0.75
HashSet<String> set1 = new HashSet<>();
set1.add("apple");
set1.add("banana");
set1.add("cherry");
// 构造一个新的集合,包含指定集合中的元素
HashSet<String> set2 = new HashSet<>(set1);
set2.add("date");
// 构造一个新的空集合,指定初始容量
HashSet<Integer> set3 = new HashSet<>(20);
set3.add(1);
set3.add(2);
set3.add(3);
// 构造一个新的空集合,指定初始容量和负载因子
HashSet<Double> set4 = new HashSet<>(10, 0.5f);
set4.add(1.1);
set4.add(2.2);
// 输出各个集合的内容
System.out.println("set1: " + set1);
System.out.println("set2: " + set2);
System.out.println("set3: " + set3);
System.out.println("set4: " + set4);
// 使用方法示例
String floor = set1.floor("d");
System.out.println("floor(\"d\"): " + floor); // 输出:cherry
HashSet<String> headSet = new HashSet<>(set1.headSet("c"));
System.out.println("headSet(\"c\"): " + headSet); // 输出:[apple, banana]
String higher = set1.higher("c");
System.out.println("higher(\"c\"): " + higher); // 输出:null
HashSet<String> tailSet = new HashSet<>(set1.tailSet("c"));
System.out.println("tailSet(\"c\"): " + tailSet); // 输出:[cherry]
// 迭代示例
System.out.println("迭代 set1:");
for (String element : set1) {
System.out.println(element);
}
}
}
2.常用方法:
方法名 | 描述 |
---|---|
add(E e) | 将指定元素添加到集合中。如果元素已经存在,添加操作将被忽略,不会抛出异常。返回 true 表示添加成功,返回 false 表示添加失败。 |
remove(Object o) | 从集合中移除指定元素。如果元素存在且被成功移除,返回 true ,否则返回 false 。 |
contains(Object o) | 检查集合是否包含指定元素。如果集合包含该元素,返回 true ,否则返回 false 。 |
isEmpty() | 检查集合是否为空。如果集合中没有元素,返回 true ,否则返回 false 。 |
size() | 返回集合中的元素数量。 |
clear() | 清空集合,移除所有元素。 |
iterator() | 返回一个迭代器,用于遍历集合中的元素。 |
addAll(Collection<? extends E> c) | 将另一个集合中的所有元素添加到当前集合中。 |