1. 创建空/单元素集合
import java.util.Collections;
//创建空集合
Map<String, String> emptyMap = Collections.emptyMap();
List<String> emptyList = Collections.emptyList();
Set<String> emptySet = Collections.emptySet();
//创建单元素集合
Map<String, String> singletonMap = Collections.singletonMap("key", "value");
List<String> singletonList = Collections.singletonList("123");
Set<String> singletonSet = Collections.singleton("123");
2. 数组
2.1. 数组的创建
创建普通数组
//方法一:指定元素的数组
int[] arr1 = new int[]{1,2,3,4,5};
System.out.println(arr1.length + " " + arr1[2]); //5 3
//Arrays.toString(Object[]) 将数组的元素生成字符串,数组的各个元素使用方括号括着
System.out.println(Arrays.toString(arr1)); //[1, 2, 3, 4, 5]
//方法二:声明、分配空间并赋值
int[] arr2 = {1,2,3,4};
System.out.println(arr2.length + " " + arr2[2]); //4 3
//方法三:声明数组时指定元素个数,然后赋值。此时所有的元素值均为0
int[] arr3 = new int[4];
System.out.println(arr3.length + " " + arr3[3]); //4 0
//方法四:声明数组名、开辟空间、赋值
int[] arr4;
arr4 = new int[]{0,1,2,3};
System.out.println(arr4.length + " " + arr4[2]); //4 2
创建多维数组
int[][] doubleArr1 = {{1,2,3},{4,5,6},{7,8,9}};
int m = 3, n = 4, q = 5;
int[][] doubleArr2 = new int[m][n]; //其中n可以省略
int[][][] tArr = new int[m][n][q]; //其中n、q可以省略
常用数组定义
// 定义一个整型数组
int[] myIntArray = new int[10];
// 定义一个字符串数组
String[] myStringArray = new String[5];
// 定义一个混合类型数组
Object[] myMixedArray = new Object[3];
// 定义时直接初始化
int[] myIntArrayInit = {1, 2, 3, 4, 5};
// 定义二维数组
int[][] my2DArray = new int[3][2];
// 二维数组初始化
int[][] my2DArrayInit = {
{1, 2},
{3, 4},
{5, 6}
};
3. Map
3.1. Map 的五种遍历方法
提前准备测试代码:
Map<String, String> map = new HashMap<>(4);
map.put("aaa", "one");
map.put("bbb", "two");
map.put("ccc", "three");
3.1.1. entrySet 遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " : " + value);
}
3.1.2. Lambda 遍历
map.forEach((key,value)-> System.out.println(key + " : " + value));
3.1.3. 迭代器遍历
迭代器遍历的一个优势是可以在遍历过程中进行元素删除操作。
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " : " + value);
//删除操作
//iterator.remove();
}
3.1.4. 单独遍历 key
for (String key : map.keySet()) {
System.out.println("key = " + key);
}
遍历出 key 之后,可以根据 key 获取对应的 value:
for (String key : map.keySet()) {
String value = map.get(key);
System.out.println(key + " : " + value);
}
获取 key 的 List
Set<String> set = map.keySet();
ArrayList<String> list = new ArrayList<>(set);
不能使用强制类型转换,会报异常。
Set<String> set = map.keySet();
List<String> list = (List<String>) set;//错误
异常信息:
Exception in thread "main" java.lang.ClassCastException: class java.util.HashMap$KeySet cannot be cast to class java.util.List (java.util.HashMap$KeySet and java.util.List are in module java.base of loader 'bootstrap')
3.1.5. 单独遍历 value
for (String value : map.values()) {
System.out.println("value = " + value);
}
获取 value 的 List
Collection<String> collection = map.values();
List<String> list = new ArrayList<>(collection);
不能使用强制类型转换,会报异常。
Collection<String> collection = map.values();
List<String> list = (List<String>) collection;//错误
异常信息:
Exception in thread "main" java.lang.ClassCastException: class java.util.HashMap$Values cannot be cast to class java.util.List (java.util.HashMap$Values and java.util.List are in module java.base of loader 'bootstrap')
3.2. HashMap 容量初始化
/**
* 获取HashMap的初始化容量
*
* @param expectedSize 预期元素个数
* @return 初始化容量,2的次幂
*/
public static int capacity(int expectedSize) {
//避免扩容的最小容量,向上取整
int num = (int) Math.ceil((float) expectedSize / 0.75F);
//获取最近的2的次幂的数
if (num <= 0) {
return 1;
}
num--;
num |= num >>> 1;
num |= num >>> 2;
num |= num >>> 4;
num |= num >>> 8;
num |= num >>> 16;
return num + 1;
}
3.3. Map 的常用方法
//TODO 待补充
clear
clone
compute
computeIfAbsent
computeIfPresent
containsKey
containsValue
entrySet
forEach
get
getOrDefault
isEmpty
keySet
merge
put
putAll
putIfAbsent
remove
remove
replace
replace
replaceAll
4. List
4.1. List 的常用方法
4.1.1. 添加元素方法
向列表末尾添加元素。
boolean add(E e);
在指定位置插入元素。
void add(int index, E element);
添加指定集合中包含的所有元素到列表的尾部。
boolean addAll(Collection<? extends E> c);
添加指定集合中包含的所有元素到列表的指定位置,列表中当前位于该位置及其后的元素将相应地后移。
boolean addAll(int index, Collection<? extends E> c);
4.1.2. 替换元素方法
替换列表中指定位置的元素,并返回被替换的元素
E set(int index, E element);
4.1.3. 查询元素方法
获取指定位置的元素。
E get(int index);
查找指定元素在列表中首次出现的位置索引。
int indexOf(Object o);
查找指定元素在列表中最后一次出现的位置索引。
int lastIndexOf(Object o);
4.1.4. 删除元素方法
删除指定位置的元素。
E remove(int index);
删除指定的元素。
boolean remove(Object o);
删除指定集合中包含的所有元素。
boolean removeAll(Collection<?> c);
4.1.5. 检查元素方法
检查列表中是否包含指定的元素。
boolean contains(Object o);
检查列表中是否包含指定集合中的所有元素。
boolean containsAll(Collection<?> c);
检查列表是否为空。
boolean isEmpty();
比较当前列表与指定对象是否相等。如果指定的对象也是列表,并且两个列表具有相同的大小、相同的元素顺序(根据元素的equals方法判断),则认为这两个列表相等。
boolean equals(Object o);
4.1.6. 列表迭代器方法
这个方法返回一个 Iterator 对象,该对象用于遍历列表中的元素。Iterator 接口提供了 hasNext(), next(), 和 remove() 方法来遍历和(可选地)移除元素。
Iterator<E> iterator();
这个方法返回一个 ListIterator 对象,它是 Iterator 的一个子接口,提供了额外的功能来双向遍历列表,即可以向前也可以向后遍历。ListIterator 接口添加了 hasPrevious(), previous(), nextIndex(), previousIndex(), set(E e), 和 add(E e) 方法。
ListIterator<E> listIterator();
这个方法返回一个 ListIterator 对象,该对象从列表中的指定位置(索引)开始遍历。这允许你从列表的任意点开始遍历,而不是总是从开头开始。
stIterator<E> listIterator(int index);
示例:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
System.out.println(lit.next());
}
while (lit.hasPrevious()) {
System.out.println(lit.previous());
}
ListIterator<String> litFromIndex = list.listIterator(1); // 从索引 1 开始
while (litFromIndex.hasNext()) {
System.out.println(litFromIndex.next());
}
4.1.7. 列表工具方法
返回列表中元素的个数
int size();
返回列表的哈希码
int hashCode();
清除列表中的所有元素,也就是size等于0
void clear();
返回列表中指定的fromIndex(包括)和toIndex(不包括)之间的元素。这个方法不会修改原列表,而是返回原列表的一个子列表。
List<E> subList(int fromIndex, int toIndex);
这个方法的作用是保留列表中那些也存在于指定集合 c 中的元素(取交集),而移除列表中所有未包含在集合 c 中的元素。换句话说,它会对列表进行“交集”操作,只保留与给定集合共有的元素。
boolean retainAll(Collection<?> c);
参数 c 是一个集合,它包含了要保留在列表中的元素。这个集合可以是任何实现了 Collection 接口的类型,比如 List、Set 等。
方法的返回值是一个布尔值,如果列表由于调用此方法而发生了改变(即至少有一个元素被移除了),则返回 true;如果列表没有发生改变(即所有元素都已经被保留下来了),则返回 false。
List<String> list = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
List<String> retainList = Arrays.asList("Banana", "Cherry", "Fig");
// 调用 retainAll 方法
boolean changed = list.retainAll(retainList);
// 打印结果
System.out.println("List after retainAll: " + list);//List after retainAll: [Banana, Cherry]
System.out.println("List was changed: " + changed);//List was changed: true
需要注意的是,retainAll 方法会修改调用它的列表,并且如果参数集合 c 包含 null 元素,那么列表中所有 null 元素都会被保留下来(前提是列表中也包含 null)。此外,如果参数集合 c 本身为空,那么调用 retainAll 方法后,列表将变为空,因为没有任何元素可以保留下来。
4.1.8. 列表转换数组方法
将列表中的元素转换为一个 Object 类型的数组。
Object[] toArray();
这个方法是一个泛型方法,它允许你指定返回数组的类型。你需要传递一个与列表元素类型相同的数组作为参数。如果传递的数组足够大,能够容纳列表中的所有元素,那么列表中的元素将被复制到这个数组中,并返回这个数组(不推荐)。如果传递的数组不够大,那么将创建一个新的、具有相同运行时类型的数组,并将列表中的元素复制到这个新数组中。
<T> T[] toArray(T[] a);
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
Object[] array = list.toArray();
for (Object obj : array) {
System.out.println((String) obj); // 需要进行类型转换
}
// 创建一个足够大的数组(不推荐)
String[] array = list.toArray(new String[list.size()]);
for (String str : array) {
System.out.println(str); // 不需要进行类型转换
}
// 传递一个空数组也可以(推荐)
String[] anotherArray = list.toArray(new String[0]); // 传递一个空数组也可以
for (String str : anotherArray) {
System.out.println(str); // 同样不需要进行类型转换
}
有两种样式将集合转换为数组:使用预大小的数组(如c.toArray(new String[c.t osize()]))或使用空数组(如c.toArray(new String[0]))。
在较早的 Java 版本中,建议使用预大小的数组,因为创建适当大小的数组所必需的反射调用非常慢。然而,由于 OpenJDK6 的更新,这个调用被强化了,使得空数组版本的性能与预大小版本相同,有时甚至更好。此外,传递预大小的数组对于并发或同步集合是危险的,因为 size 和 toArray 调用之间可能存在数据竞争,如果在操作期间并发地收缩集合,则可能导致数组末尾出现额外的 null。
这种检查允许遵循统一的样式:要么使用空数组(这在现代 Java 中是推荐的),要么使用预大小的数组(这在旧 Java 版本或非基于 hotspot 的 jvm 中可能更快)。
4.1.9. 列表中的默认方法
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
Java 8 引入了默认方法,允许在接口中定义具有实现的方法,这样即使接口被现有的类实现,也不会破坏它们的兼容性。下面三个方法是 Java 8 中 List 接口的默认方法实现。
replaceAll(UnaryOperator<E> operator)
这个方法接受一个 UnaryOperator 函数式接口作为参数,该接口表示对类型为 E 的对象进行操作并返回同类型的结果。replaceAll 方法会遍历列表中的每个元素,并将 operator 应用于这些元素,然后用返回的结果替换原元素。
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator); // 检查 operator 是否为 null
final ListIterator<E> li = this.listIterator(); // 获取列表的迭代器
while (li.hasNext()) { // 遍历列表
li.set(operator.apply(li.next())); // 应用操作符并替换元素
}
}
例如,如果你有一个 List,你可以使用这个方法将所有元素乘以 2:
list.replaceAll(x -> x * 2);
sort(Comparator<? super E> c)
这个方法接受一个 Comparator<? super E> 作为参数,用于比较列表中的元素。它首先将列表转换为数组,然后使用 Arrays.sort 方法对数组进行排序,最后再将排序后的数组元素复制回列表中。
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray(); // 将列表转换为数组
Arrays.sort(a, (Comparator) c); // 对数组进行排序
ListIterator<E> i = this.listIterator(); // 获取列表的迭代器
for (Object e : a) { // 遍历排序后的数组
i.next(); // 移动迭代器到下一个位置
i.set((E) e); // 将排序后的元素设置回列表
}
}
例如,如果你有一个 List,你可以使用这个方法按字母顺序对元素进行排序:
list.sort(Comparator.naturalOrder());
spliterator()
这个方法返回一个 Spliterator,它是 Java 8 引入的一个新接口,用于并行遍历元素。Spliterator 可以用于分割和提供对数据元素的访问,特别是在并行处理时非常有用。
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED); // 返回一个有序的 Spliterator
}
Spliterators.spliterator(this, Spliterator.ORDERED) 调用会创建一个与列表相对应的 Spliterator,它具有 ORDERED 特征,表示元素是有序的。