简介
在Java中,集合是用于存储和操作一组对象的数据结构。Java提供了丰富的集合框架,主要包括(List)列表,集(Set),映射(Map),队列(Queue),先说结论
-
如果需要随机访问和快速的插入/删除操作,选择
ArrayList
。 -
如果需要频繁插入/删除操作,选择
LinkedList
。 -
如果不允许重复元素并且顺序很重要,选择
LinkedHashSet
或者TreeSet
。 -
如果需要进行键值对的存储和检索,选择
HashMap
或者LinkedHashMap
。 -
如果需要按照键的顺序存储键值对,选择
TreeMap
。
下面我们分别介绍。
List(列表)
允许重复元素,有序。常见的实现类有 ArrayList
、LinkedList
、Vector
。
ArrayList
ArrayList
是在 Java 编程中常用的集合类之一,它提供了便捷的数组操作,并在动态性、灵活性和性能方面取得了平衡。如果需要频繁在中间插入和删除元素,或者需要在多线程环境中使用,可能需要考虑其他集合实现。
特点
1.动态大小: ArrayList
的大小是动态可变的,可以根据需要自动增加或缩小。这与 Vector
相似,但相对于 LinkedList
,它的随机访问效率更高。
2.随机访问高效: 由于 ArrayList
基于动态数组实现,可以通过索引直接访问元素,因此在需要频繁随机访问元素的场景下,ArrayList
的性能通常优于 LinkedList
。
3.适用于大部分场景: 在大多数情况下,ArrayList
是一个通用、高效的集合类。它适用于存储和随机访问元素,但不适用于在中间或开头频繁插入和删除元素的情况。
4.非同步: ArrayList
不是线程安全的,不支持多线程并发操作。如果需要在多线程环境中使用,可以考虑使用 Vector(这个比较落后了,所以就不提了)
或使用 Collections.synchronizedList
方法包装 ArrayList
。
使用方法
首先先引用,然后初始化。
import java.util.ArrayList;//引入ArrayList类
public class Test01 {
ArrayList<String> objectName = new ArrayList<>();//初始化
}
objectName:对象名。
ArrayList<String>:这<>里面的是泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
一点示范
import java.util.ArrayList;//引入ArrayList类
public class Test01 {
public static void main(String[] args) {
ArrayList<String> objectName = new ArrayList<>();//初始化
objectName.add("Changsha");//add()
System.out.println(objectName);
objectName.add("Shenyang");
System.out.println(objectName);
objectName.set(0,"Shanghai");//set()
System.out.println(objectName);
System.out.println(objectName.get(1));//get()
objectName.remove(1);//remove()
System.out.println(objectName);
}
}
运行结果
用法表格
1 | add() | 将元素插入到指定位置的 arraylist 中 |
2 | addAll() | 添加集合中的所有元素到 arraylist 中 |
3 | clear() | 删除 arraylist 中的所有元素 |
4 | clone() | 复制一份 arraylist |
5 | contains() | 判断元素是否在 arraylist |
6 | get() | 通过索引值获取 arraylist 中的元素 |
7 | indexOf() | 返回 arraylist 中元素的索引值 |
8 | removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
9 | remove() | 删除 arraylist 里的单个元素 |
10 | size() | 返回 arraylist 里元素数量 |
11 | isEmpty() | 判断 arraylist 是否为空 |
12 | subList() | 截取部分 arraylist 的元素 |
13 | set() | 替换 arraylist 中指定索引的元素 |
14 | sort() | 对 arraylist 元素进行排序 |
15 | toArray() | 将 arraylist 转换为数组 |
16 | toString() | 将 arraylist 转换为字符串 |
17 | ensureCapacity() | 设置指定容量大小的 arraylist |
18 | lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
19 | retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
20 | containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
21 | trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
22 | removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
23 | replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
24 | removeIf() | 删除所有满足特定条件的 arraylist 元素 |
25 | forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
LinkedList
特点
1.双向链表结构:这是它与ArrayList最主要的差别, LinkedList
的底层数据结构是双向链表,每个节点都包含对前一个和后一个元素的引用。
2.动态大小: 类似于 ArrayList
,LinkedList
的大小也是动态可变的,可以根据需要自动增加或缩小。
3.插入和删除效率高: 由于链表结构,LinkedList
在中间插入和删除元素的操作比 ArrayList
效率更高。因此在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
4.非随机访问效率相对较低: 与 ArrayList
不同,LinkedList
的随机访问效率相对较低。如果需要频繁随机访问元素,ArrayList
可能更适合。
5.迭代效率: 在迭代时,LinkedList
的性能较差。由于访问节点需要跳跃指针,相比于数组的连续存储,会增加迭代的开销。
6.占用更多内存: 由于每个节点都需要存储额外的引用,相对于 ArrayList
,LinkedList
在内存占用上可能会更多。
7.非同步: LinkedList
也不是线程安全的,不支持多线程并发操作。如果需要在多线程环境中使用,可以考虑使用 Collections.synchronizedList
方法包装 LinkedList
。
8.特定场景的优势: 在某些特定的场景中,如实现栈、队列或双端队列等数据结构时,LinkedList
可能更为适用。
使用方法
同样是引用和初始化
import java.util.LinkedList;
public class Test01 {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
}
}
linkedList:对象名
LinkedList<String>:这<>里面的是泛型数据类型,用于设置 linkedList的数据类型,只能为引用数据类型。
一些示范
import java.util.LinkedList;
public class Test01 {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.add("Banana");
linkedList.add("Orange");
System.out.println(linkedList);
// 在指定位置插入元素
linkedList.add(1, "Grapes");
System.out.println(linkedList);
//获取链表中的元素:
String element = linkedList.get(0);
System.out.println(element);
//更新链表中的元素
linkedList.set(0, "NewElement");
System.out.println(linkedList);
}
}
运行结果
常见用法
1 | linkedList.add("Element"); | 在链表末尾添加元素 |
2 | linkedList.add(index, "Element"); | 在指定位置插入元素 |
3 | linkedList.get(index); | 获取链表中的元素 |
4 | linkedList.set(index, "NewElement"); | 更新链表中的元素 |
5 | linkedList.remove(index); | 删除指定位置的元素 |
6 | int size = linkedList.size(); | 获取链表的大小 |
7 | linkedList.isEmpty(); | 判断链表是否为空 |
8 | linkedList.contains("Element"); | 查找元素是否存在 |
9 | linkedList.getFirst();linkedList.getLast(); | 获取第一个和最后一个元素 |
10 | linkedList.removeFirst(); linkedList.removeLast(); | 删除第一个和最后一个元素 |
11 | Iterator<String> iterator = linkedList.iterator(); while (iterator.hasNext()) { String element = iterator.next(); // 处理元素 } | 迭代器遍历链表 |
12 | ListIterator<String> iterator = linkedList.listIterator(linkedList.size()); while (iterator.hasPrevious()) { String element = iterator.previous(); // 处理元素 } | 反向遍历链表 |
HashMap
特点
-
键值对存储:
HashMap
使用键值对(Key-Value)的方式存储数据。每个键都关联着一个唯一的值。这使得通过键来查找、插入和删除元素非常高效。 -
无序性:
HashMap
不保证元素的顺序,即元素不会按照插入顺序或其他顺序存储。如果需要有序的键值对集合,可以考虑使用LinkedHashMap
。 -
允许 null 键和值:
HashMap
允许键和值都为null
。这意味着你可以将null
作为键或值插入到HashMap
中。 -
非同步性:
HashMap
不是线程安全的。在多线程环境下,可能需要通过同步措施或者使用ConcurrentHashMap
等线程安全的集合来确保安全性。 -
初始容量和负载因子:
HashMap
允许指定初始容量和负载因子。初始容量是HashMap
在创建时的容量,负载因子是用来控制HashMap
在容量不足时扩容的程度。 -
性能高效: 在非多线程环境下,
HashMap
提供快速的查找、插入和删除操作。时间复杂度为 O(1),但在某些情况下,可能会退化为 O(n)。(好的性能需要减少和处理好哈希冲突(链地址法和开放地址法)) -
容量动态调整:
HashMap
在元素数量达到一定阈值时会自动进行扩容,以保持其性能。负载因子决定了什么时候进行扩容。 -
重复键处理: 如果插入了重复的键,新值会覆盖旧值。这意味着同一个键对应的值是唯一的。
使用方法
引用和初始化
import java.util.HashMap;
public class Test01 {
public static void main(String[] args) {
//初始化HashMap
HashMap<String,Integer> hashMap = new HashMap<>();
}
}
hashMap:对象名
HashMap<String>:这<>里面的是泛型数据类型,用于设置 hashMap的数据类型,只能为引用数据类型。
一些示范
import java.util.HashMap;
public class Test01 {
public static void main(String[] args) {
//初始化HashMap
HashMap<String,Integer> hashMap = new HashMap<>();
//put(Object key, Object value)
hashMap.put("one",1);//对应键one放入了一个值1
hashMap.put("two",2);//对应键two放入了一个值2
System.out.println(hashMap);
//get(Object key)
System.out.println(hashMap.get("one"));//返回与指定键相关联的值,如果该键不存在,则返回null。
//size()
System.out.println(hashMap.size());//返回HashMap中键值对的数量。
//remove(Object key)
hashMap.remove("one");//删除HashMap中与指定键相关联的值,并返回被删除的值。
System.out.println(hashMap);
//clear()
hashMap.clear();//删除HashMap中所有键值对。
System.out.println(hashMap);
}
}
运行结果
常见用法
1 | put(key, value) | 插入键值对,将指定的键和值关联起来。如果键已存在,则新值会替换旧值。 |
2 | get(key) | 获取指定键对应的值。如果键不存在,返回 null。 |
3 | containsKey(key) | 检查是否包含指定的键。 |
4 | containsValue(value) | 检查是否包含指定的值。 |
5 | remove(key) | 移除指定键及其关联的值。 |
6 | size() | 返回 HashMap 中键值对的数量。 |
7 | isEmpty() | 检查 HashMap 是否为空。 |
8 | keySet() | 返回包含所有键的 Set 集合。 |
9 | values() | 返回包含所有值的 Collection 集合。 |
10 | entrySet() | 返回包含所有键值对的 Set 集合。 |
11 | putAll(map) | 将另一个 Map 的所有键值对插入到当前 HashMap。 |
12 | clear() | 移除所有键值对,使 HashMap 变为空。 |
LinkedHashMap
特点
LinkedHashMap的基本特点和HashMap大致相同,主要的不同点是:它采用了一个双向链表保存插入时的顺序。并且,它构造时有一个叫accessOrder的参数,设定为true的时候可以会启用按照访问的顺序排序。通过这个可以实现LRU(Least Recently Used 又称缓存策略),通过这个就可以实现你手机电脑里开了很多程序,内存不够用时,自动移除你前面的进程。(对,你打手游另外开了几个应用,回来发现游戏退了就是因为这个)。
初始化
import java.util.LinkedHashMap;
public class Test01 {
public static void main(String[] args) {
LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<>(16, 0.75f,true);
}
}
三个参数分别为初始容量,负载因子达到多少时扩容,是否按照访问顺序排序
使用方法
下面演示怎么实现简单的LRU缓存策略
import java.util.LinkedHashMap;
import java.util.Map;
//1。首先你要创建一个类继承LinkedHashMap类
public class Test01<K, V> extends LinkedHashMap<K, V> {
private static final int MAX_ENTRIES = 3; // 设置最大的条目数量
// 2.调用父类构造函数并传入参数
public Test01() {
super(MAX_ENTRIES, 0.75f, true);//这里true就表示可以启用按照访问的顺序排序
}
// 3.重写 removeEldestEntry 方法,控制是否移除最旧的条目
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > MAX_ENTRIES;
}
public static void main(String[] args) {
//4.这里就通过Test01类构造这个重写了removeEldestEntry的LinkedHashMap
Test01<Integer, String> lruCache = new Test01<>();
lruCache.put(1, "One");
lruCache.put(2, "Two");
lruCache.put(3, "Three");
System.out.println("Initial LinkedHashMap: " + lruCache);
//这里就实现了LRU缓存策略了1,“One”键值对被移除
lruCache.put(4, "Four"); // 超出最大条目数,会触发 removeEldestEntry 移除最旧的条目
System.out.println("LRU Cache after adding one more entry: " + lruCache);
}
}
运行结果
常见用法
大多与HashMap一致,可翻看前面的内容食用,这里列出一些不同的
-
putIfAbsent(key, value): 如果指定的键尚未与值关联(或映射到
null
),则将它与给定的值关联,并返回null
;如果该键已经有与之关联的非null
值,则不进行任何操作,并返回当前值。 -
removeEldestEntry(Entry<K,V> eldest): 当重写此方法时,可以在每次添加新条目后删除最旧的条目。这是实现 LRU 缓存的一个常见用法。如果希望限制
LinkedHashMap
的大小,可以重写此方法来返回true
,然后在插入新元素后移除最旧的元素。 -
replace(oldKey, oldValue, newKey, newValue): 替换指定键和关联值的条目,如果当前键值对匹配给定的键和值。如果不匹配,则不进行替换。
-
replaceAll(BiFunction<K, V, V> function): 对每个条目的值应用给定的函数,并将结果替换为原始值。
-
merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): 将指定键的值与给定值按照给定的 remapping 函数进行合并。如果指定键的值不存在,则将给定值关联到该键;如果指定键的值存在,则使用 remapping 函数计算新值并关联到该键。
先发一半,后面的内容过会发。