集合框架概念
Java集合框架是用于存储和操作数据的一组接口和类的集合。它提供了一种便捷的方式来处理和操作数据,包括查找、插入、删除等操作。集合框架提供了多种类型的集合,如List、Set、Map等。
数组是一种固定长度且类型相同的数据结构,而集合是一种动态调整大小并且可以存储不同类型元素的数据结构。集合提供了更多的功能和灵活性。与数组相比,集合可以自动调整大小,简化了添加或删除元素的过程,并提供了更多的方法和算法。
集合的框架
Java
集合框架是用于存储和操作数据的一组接口和类的集合。它提供了一种便捷的方式来处理和操作数据,包括查找、插入、删除等操作。Java集合框架分为两个基本接口:Collection
接口和Map
接口。
一、Collection
接口: Collection
接口是集合框架中最基本的接口,定义了一组通用的方法,适用于所有的集合类型。它有两个子接口:List
和Set
。
-
List
接口:Lis
t接口表示有序的、可重复的集合,可以按照元素的索引位置进行访问和操作。常见的List实现类有ArrayList
和LinkedList
。
-
ArrayList
:基于数组实现,支持快速随机访问元素,但插入和删除操作相对较慢。 -
LinkedList
:基于链表实现,支持高效地在任意位置插入和删除元素,但访问元素需要遍历链表。
-
Set接口: Set接口表示无序的、不可重复的集合,每个元素在Set中只出现一次。常见的
Set
实现类有HashSet
和TreeSet
。
-
HashSet
:基于哈希表实现,使用哈希值来快速访问元素,不保证元素的顺序。 -
TreeSet
:基于红黑树实现,对元素进行自然排序或者指定排序规则。
二、Map接口: Map接口表示键值对的集合,每个键是唯一的,但值可以重复。Map
提供了根据键快速查找和访问值的功能。常见的Map
实现类有HashMap
和TreeMap
。
-
HashMap
:基于哈希表实现,通过键的哈希值来快速查找和访问值,不保证元素的顺序。 -
TreeMap
:基于红黑树实现,可以对键进行自然排序或者指定排序规则。
Collection接口
List集合
常用的List接口方法:
方法 | 描述 |
---|---|
void add(int index, E element) | 在指定的索引位置插入指定的元素 |
E remove(int index) | 删除指定索引位置的元素并返回被删除的元素 |
boolean remove(Object o) | 从集合中删除指定的元素,如果集合中存在该元素,则执行删除操作并返回true |
E get(int index) | 获取指定索引位置的元素 |
E set(int index, E element) | 将指定索引位置的元素替换为新的元素并返回原来的元素 |
int indexOf(Object o) | 返回指定元素在集合中第一次出现的索引,如果集合不包含该元素则返回-1 |
int size() | 获取当前集合中元素的个数 |
boolean isEmpty() | 判断当前集合是否为空,如果为空则返回true,否则返回false |
boolean contains(Object o) | 判断集合中是否包含指定的元素,如果包含则返回true,否则返回false |
void clear() | 删除当前集合中的所有元素 |
List
实现类——ArrayList
集合
优点:
实现了List接口 可以动态扩容(我们只管存,长度不够,底层会自动的扩容) 通过下标可以快速访问数据 ArrayList底层是数组,对数组做了封装 可以存储任意类型的数据,包括null,并且数据可以重复,但是多线程访问时不安全
缺点:
由于ArrayList的底层是数组结构,所以ArrayList的查询效率高,可通过下标直接查询,但是他的添加和删除效率低,原因是因为添加和删除操作要涉及到数据的迁移。
常见方法:
add();//添加元素 size();//集合长度 indexOf(); //查找某个元素在集合中第一次出现的位置,返回的是该数据的下标 contains();//查找某个元素是否存在与集合中,返回为boolean值 get();//返回指定下标的数据 remove();//移除下标所在的元素 isEmpty();//判断集合是否为空
以下是一个使用ArrayList
集合的Java代码示例:
import java.util.ArrayList; public class ArrayListExample { public static void main(String[] args) { // 创建一个ArrayList对象 ArrayList<String> fruits = new ArrayList<>(); // 添加元素 fruits.add("苹果"); fruits.add("香蕉"); fruits.add("橙子"); // 获取元素 System.out.println("水果列表中的元素:"); for (String fruit : fruits) { System.out.println(fruit); } // 在指定位置插入元素 fruits.add(1, "草莓"); // 修改指定位置的元素 fruits.set(0, "梨子"); // 删除指定位置的元素 fruits.remove(2); // 遍历并打印最终的水果列表 System.out.println("使用for循环遍历:"); for (int i = 0; i < fruits.size(); i++) { String fruit = fruits.get(i); System.out.println(fruit); } System.out.println("使用增强for循环遍历:"); for (String fruit : fruits) { System.out.println(fruit); } System.out.println("使用迭代器遍历:"); Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); System.out.println(fruit); } } }
List实现类——LinkedList
集合
LinkedList
是另一个常见的List接口的实现类。与ArrayList
不同,LinkedList
基于链表数据结构实现,而不是数组。下面对LinkedList
集合进行详细介绍:
特点:
-
实现了List接口:
LinkedList
实现了List接口,因此具备List接口的一些基本特性,如按索引访问元素、添加、删除等操作。 -
链表结构:
LinkedList
底层使用双向链表实现,每个节点都包含一个指向前一个节点和后一个节点的指针。这使得在LinkedList
中插入和删除元素的效率比较高,因为只需要改变指针的指向而不需要像数组那样进行元素的迁移。然而,由于需要维护节点之间的链接关系,查询元素的效率比较低。 -
可以存储任意类型的数据,包括null,并且数据可以重复。
优点:
-
插入和删除效率高:由于
LinkedList
的插入和删除只需要改变指针的指向,不需要进行大量元素的迁移,所以在这些操作上具有较高的效率。 -
支持头部和尾部操作:
LinkedList
提供了特定的方法用于在头部和尾部执行添加和删除操作,如addFirst
、addLast
、removeFirst
、removeLast
等。
缺点:
-
查询效率低:由于
LinkedList
是通过链表实现的,要查询特定位置的元素就必须从头开始遍历链表,直到找到对应的位置。这使得查询效率较低。 -
占用内存较大:相对于使用数组存储数据的
ArrayList
,LinkedList
由于需要维护节点之间的指针关系,会占用更多的内存空间。
常见方法: LinkedList
提供了与ArrayList
类似的一些常见方法,如add、size、indexOf、contains、get、remove
等。此外,LinkedList
还提供了一些特定于链表结构的方法,如addFirst、addLast、getFirst、getLast、removeFirst、removeLast
等,可以方便地在链表的头部和尾部进行操作。
下面是一些常见方法的示例:
import java.util.LinkedList; public class LinkedListExample { public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.add("apple"); list.add("banana"); list.add("orange"); int size = list.size(); // 获取集合大小 String firstElement = list.getFirst(); // 获取第一个元素 String lastElement = list.getLast(); // 获取最后一个元素 boolean contains = list.contains("banana"); // 判断集合是否包含某个元素 int index = list.indexOf("orange"); // 查找元素的索引位置 list.removeFirst(); // 移除第一个元素 list.removeLast(); // 移除最后一个元素 } }
需要注意的是,LinkedList
具有非线程安全的特性,如果在多线程环境中进行并发访问,需要额外考虑线程安全性。
Set集合
下面是常用的Set接口方法及其描述:
方法 | 描述 |
---|---|
add(E element) | 将指定元素添加到集合中,并返回添加操作是否成功的布尔值。 |
remove(Object element) | 从集合中移除指定元素,并返回移除操作是否成功的布尔值。 |
contains(Object element) | 判断集合是否包含指定元素,如果包含则返回true,否则返回false。 |
size() | 返回集合中元素的个数。 |
isEmpty () | 判断集合是否为空,如果为空则返回true,否则返回false。 |
clear() | 清空集合中的所有元素。 |
iterator() | 返回一个迭代器,用于遍历集合中的元素。 |
这些方法是 Set 接口的一些常见方法,可以对集合进行元素的添加、删除和查找等操作。需要注意的是,Set 集合不允许包含重复的元素。
Set实现类——HashSet
HashSet
是Set接口的一个常见实现类,它基于哈希表数据结构实现。下面对HashSet
进行详细介绍:
特点:
-
实现了Set接口:
HashSet
实现了Set接口,因此具备Set接口的一些基本特性,如不允许重复元素、无序等。 -
哈希表结构:
HashSet
底层使用哈希表实现,每个元素都存储在哈希表的一个桶(bucket)中。HashSet
使用元素的hashCode
值来确定桶的位置,如果不同元素的hashCode
值相同,会通过equals()
方法比较来判断是否为相同元素。 -
无序:
HashSet
集合中的元素没有固定的顺序,即不保证元素的存储和遍历顺序相同。
优点:
-
插入和查询效率高:由于
HashSet
底层使用哈希表实现,插入和查询元素的效率都是很高的,平均时间复杂度为O(1)。通过哈希表的索引计算,可以快速定位桶和元素。 -
不允许重复元素:
HashSet
基于哈希表的特性,保证集合中不会有重复的元素。新增元素时会先计算其hashCode
值,再进行equals()
方法的比较,如果已存在相同元素,则不会添加到集合中。
缺点:
-
不保证有序:由于
HashSet
是无序的,对于需要有序的需求,需要使用TreeSe
等其他实现类。 -
不是线程安全的:
HashSet
是非线程安全的,如果在多线程环境中进行并发访问,需要额外考虑线程安全性。
常见方法: HashSet
提供了一些常见的方法,如add、remove、contains、size、isEmpty
等,这些方法在Set接口中都有定义。此外,HashSet
还具有Set
接口的特点,如不允许重复元素等。
下面是一个示例代码,演示了HashSet
的基本用法:
import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("apple"); set.add("banana"); set.add("orange"); boolean contains = set.contains("banana"); // 判断集合是否包含某个元素 int size = set.size(); // 获取集合大小 set.remove("orange"); // 移除元素 boolean isEmpty = set.isEmpty(); // 判断集合是否为空 } }
需要注意的是,当自定义类作为HashSet
集合中的元素时,需要正确重写hashCode()
和equals()
方法,以确保HashSet
能够正常工作。
import java.util.HashSet; class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } // 重写hashCode方法 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } // 重写equals方法 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class HashSetExample { public static void main(String[] args) { HashSet<Student> set = new HashSet<>(); Student student1 = new Student("Alice", 20); Student student2 = new Student("Bob", 21); Student student3 = new Student("Alice", 20); set.add(student1); set.add(student2); set.add(student3); System.out.println(set.size()); // 输出结果为2 boolean contains = set.contains(new Student("Alice", 20)); System.out.println(contains); // 输出结果为true } }
在以上示例中,我们创建了一个名为Student的自定义类,并在HashSet
中存储了三个不同的Student
对象。由于我们重写了hashCode()
和equals()
方法,HashSet
能正确判断对象的相等性,并且不会添加重复的元素。
需要注意的是,在重写hashCode()
方法时,我们根据对象的成员变量来计算hashCode
值。在重写equals()方法时,我们通过比较对象的各个属性来判断两个对象是否相等。
Set实现类——TreeSet
TreeSet
是Java中Set接口的一个常见实现类,它基于红黑树数据结构实现。下面对TreeSet
进行详细介绍:
特点:
-
实现了Set接口:
TreeSet
实现了Set接口,因此具备Set接口的一些基本特性,如不允许重复元素、无序等。 -
红黑树结构:
TreeSet
底层使用红黑树实现,每个元素都存储在红黑树的一个节点中。红黑树是一种平衡的二叉搜索树,通过对节点进行颜色标记和旋转操作来保持平衡。 -
自然排序或自定义排序:
TreeSet
可以按照元素的自然顺序(例如,数字的升序,字符串的字典顺序)或者通过提供Comparator接口的自定义比较器来进行排序。
优点:
-
有序性:
TreeSet
中的元素按照排序规则进行排序,因此可以保持有序状态。对于需要有序遍历或获取范围内元素的需求,TreeSet
非常适用。 -
插入和查询效率高:由于
TreeSet
底层使用红黑树实现,插入和查询元素的效率都是很高的,平均时间复杂度为O(log N)。红黑树的平衡性保证了树的高度较低。 -
不允许重复元素:
TreeSet
保证集合中不会有重复的元素。新增元素时会根据排序规则找到合适的位置插入,如果已存在相同元素,则不会添加到集合中。
缺点:
-
不适用于大量数据的实时增删:红黑树的插入和删除操作需要调整树结构以保持平衡,因此在频繁插入和删除大量数据时,性能可能会受到一定影响。
-
不是线程安全的:
TreeSet
是非线程安全的,如果在多线程环境中进行并发访问,需要额外考虑线程安全性。
常见方法: TreeSet
提供了一些常见的方法,如add、remove、contains、size、isEmpty
等,这些方法在Set接口中都有定义。此外,TreeSet
还具有Set接口的特点,如不允许重复元素等。
下面是一个示例代码,演示了TreeSet
的基本用法:
import java.util.TreeSet; public class TreeSetExample { public static void main(String[] args) { TreeSet<String> set = new TreeSet<>(); set.add("apple"); set.add("banana"); set.add("orange"); boolean contains = set.contains("banana"); // 判断集合是否包含某个元素 int size = set.size(); // 获取集合大小 set.remove("orange"); // 移除元素 boolean isEmpty = set.isEmpty(); // 判断集合是否为空 // 遍历集合(按照元素的升序排列) for (String element : set) { System.out.println(element); } } }
需要注意的是,当自定义类作为TreeSet
集合中的元素时,需要实现Comparable接口或者提供Comparator来指定排序规则,以确保TreeSet
能够正常工作。
import java.util.Comparator; import java.util.TreeSet; // 自定义类Student class Student { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } } // 自定义比较器按照学生成绩进行降序排序 class ScoreComparator implements Comparator<Student> { @Override public int compare(Student s1, Student s2) { // 根据分数进行比较 if (s1.getScore() > s2.getScore()) { return -1; // 返回负数表示s1排在s2之前 } else if (s1.getScore() < s2.getScore()) { return 1; // 返回正数表示s1排在s2之后 } else { return 0; // 分数相等,保持原有顺序 } } } public class TreeSetExample { public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(new ScoreComparator()); set.add(new Student("Alice", 80)); set.add(new Student("Bob", 90)); set.add(new Student("Charlie", 75)); for (Student student : set) { System.out.println(student.getName() + " - " + student.getScore()); } } }
在上述示例中,我们定义了一个Student
类作为TreeSet
中的元素,并实现了一个ScoreComparator
类来指定按照学生成绩进行降序排序。通过在创建TreeSet
对象时传入Comparator对象,可以确保TreeSet
按照指定的排序规则对元素进行排序。
运行以上代码,输出如下:
Bob - 90 Alice - 80 Charlie - 75
以上示例演示了如何自定义类作为TreeSet
集合中的元素,并通过实现Comparator接口来指定排序规则。
Map接口
常用的Map接口方法:
方法 | 描述 |
---|---|
V get(Object key) | 返回与指定键关联的值,如果键不存在则返回null |
V put(K key, V value) | 在Map中将指定的值与指定的键关联,并返回以前与该键关联的值,如果键不存在则返回null |
V remove(Object key) | 从Map中删除与指定键关联的映射关系,并返回以前与该键关联的值,如果键不存在则返回null |
boolean containsKey(Object key) | 判断Map中是否包含指定的键 |
boolean containsValue(Object value) | 判断Map中是否包含指定的值 |
int size() | 返回Map中键值对的数量 |
boolean isEmpty() | 判断Map是否为空 |
void clear() | 从Map中删除所有键值对 |
Set<K> keySet() | 返回Map中所有键的Set集合 |
Collection<V> values() | 返回Map中所有值的集合 |
Set<Map.Entry<K, V>> entrySet() | 返回Map中所有键值对的Set集合 |
map集合
Map
是键值对的集合,每个键是唯一的,但值可以重复。 HashMap
是基于哈希表实现的Map集合,通过键的哈希值来快速访问元素。 TreeMap
是基于红黑树实现的Map集合,可以对键进行自然排序或者指定排序规则。
Map实现类——HashMap
HashMap
是Java集合框架中的一个实现了Map接口的类,它以键值对的形式存储数据,并且允许将null作为键和值。下面详细介绍HashMap
的特点和使用方法:
-
哈希表实现:
HashMap
内部使用哈希表来存储键值对。哈希表是一个数组,每个数组元素称为桶(bucket)。当要存储一个键值对时,HashMap
会根据键的哈希码确定对应的桶,并将键值对存储在桶中。 -
键的唯一性:
HashMap
中的键是唯一的,不允许重复。当插入一个键值对时,HashMap
会根据键的哈希码以及equals方法判断是否已存在相同的键。如果存在相同的键,则新的值会覆盖原有的值。 -
允许null键和null值:
HashMap
允许键和值为null。可以插入null键和null值,并且通过get方法获取到的值也可以为null。但需要注意,由于键的唯一性,HashMap
中最多只能有一个键为null的键值对。 -
无序性:
HashMap
中的键值对是无序存储的,即插入顺序和遍历顺序不一致。这是因为HashMap
内部使用哈希表来存储数据,哈希表并不保持元素的特定顺序。 -
动态扩容:
HashMap
具有动态扩容的特性,即在哈希表容量不足时自动进行扩容。扩容会创建一个更大的哈希表,然后将原有的键值对重新分配到新的桶中,这样可以提高桶的利用率和减少冲突的概率。 -
遍历方式:可以使用
entrySet()
方法来获取HashMap
中所有键值对的集合,然后通过迭代器或增强型for循环遍历。也可以使用keySet()
方法获取键的集合,再通过键来获取对应的值。 -
效率高:
HashMap
的查询、插入和删除操作都具有较高的效率,时间复杂度平均为O(1)。但在极端情况下,如哈希冲突严重或哈希函数设计不合理等情况,可能会导致性能下降。
下面是一个使用Java代码演示HashMap
的示例:
import java.util.HashMap; import java.util.Map; public class HashMapExample { public static void main(String[] args) { // 创建一个HashMap实例 Map<String, Integer> hashMap = new HashMap<>(); // 添加键值对 hashMap.put("apple", 1); hashMap.put("banana", 2); hashMap.put("orange", 3); // 获取键的值 int appleValue = hashMap.get("apple"); System.out.println("The value of 'apple' is: " + appleValue); // 判断是否包含指定的键 boolean hasKey = hashMap.containsKey("banana"); System.out.println("Has 'banana': " + hasKey); // 遍历HashMap中的键值对 System.out.println("HashMap contains:"); for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { String key = entry.getKey(); int value = entry.getValue(); System.out.println(key + " -> " + value); } // 删除键值对 hashMap.remove("orange"); // 判断HashMap是否为空 boolean isEmpty = hashMap.isEmpty(); System.out.println("Is HashMap empty: " + isEmpty); } }
通过上述代码示例,我们可以看到HashMap
的基本用法。可以根据具体的业务需求,使用put
方法添加键值对,使用get
方法获取键的值,使用containsKey
方法判断是否包含指定的键,使用entrySet
方法遍历所有键值对,并且可以使用remove
方法删除指定的键值对,使用isEmpty
方法判断HashMap
是否为空。
Map实现类——TreeMap
TreeMap
是Java
集合框架中的一个实现了SortedMap
接口的类,它基于红黑树(Red-Black Tree)数据结构实现。TreeMap
具有以下特点:
-
键的排序:
TreeMap
中的键值对是按照键的自然顺序或自定义比较器进行排序的。如果使用默认构造函数创建TreeMap
,则键必须实现Comparable
接口,并且键的类型应该是可比较的。如果创建TreeMap
时提供了自定义的比较器Comparator
,则使用该比较器进行排序。 -
有序性:
TreeMap
中的键值对按照键的顺序存储,可以通过键的顺序进行遍历。这使得TreeMap
在需要按顺序访问元素的场景下非常有用。 -
红黑树结构:
TreeMap
内部使用红黑树数据结构实现,红黑树是一种自平衡的二叉搜索树。红黑树的特点使得插入、删除和查找操作的时间复杂度为O(log N),保证了较好的性能。 -
支持键的范围检索:
TreeMap
提供了一些与范围有关的方法,如firstKey()
返回最小的键,lastKey()
返回最大的键,headMap(K toKey)
返回小于toKey
的所有键值对,tailMap(K fromKey)
返回大于等于fromKey
的所有键值对,subMap(K fromKey, K toKey)
返回范围在[fromKey, toKey)
之间的键值对。
下面是一个使用TreeMap
的示例:
import java.util.TreeMap; public class TreeMapExample { public static void main(String[] args) { // 创建一个TreeMap实例 TreeMap<String, Integer> treeMap = new TreeMap<>(); // 添加键值对 treeMap.put("apple", 1); treeMap.put("banana", 2); treeMap.put("orange", 3); // 获取最小键和最大键 String minKey = treeMap.firstKey(); String maxKey = treeMap.lastKey(); System.out.println("Min key: " + minKey); System.out.println("Max key: " + maxKey); // 获取小于"banana"的键值对 TreeMap<String, Integer> headMap = treeMap.headMap("banana"); System.out.println("Head map: " + headMap); // 获取大于等于"orange"的键值对 TreeMap<String, Integer> tailMap = treeMap.tailMap("orange"); System.out.println("Tail map: " + tailMap); // 获取范围在["apple", "orange")之间的键值对 TreeMap<String, Integer> subMap = treeMap.subMap("apple", "orange"); System.out.println("Sub map: " + subMap); } }
通过上述示例,我们可以看到TreeMap
的基本用法。可以使用put方法添加键值对,使用firstKey
和lastKey
方法获取最小和最大的键,使用headMap、tailMap和subMap
方法获取指定键范围内的子映射。TreeMap
会根据键的顺序进行存储和排序,并且支持红黑树的快速插入、删除和检索操作。
需要注意的是,由于TreeMap
使用红黑树作为底层数据结构,其实现是相对复杂的,因此插入、删除和查找操作的性能较HashMap
略低。如果不需要有序性和范围检索的特性,可以考虑使用HashMap
等其他实现类。
总结
一、概念: Java集合框架是用于存储和操作数据的一组接口和类的集合。它提供了一种便捷的方式来处理和操作数据,包括查找、插入、删除等操作。集合框架提供了多种类型的集合,如List、Set、Map等。
数组是一种固定长度且类型相同的数据结构,而集合是一种动态调整大小并且可以存储不同类型元素的数据结构。集合提供了更多的功能和灵活性。与数组相比,集合可以自动调整大小,简化了添加或删除元素的过程,并提供了更多的方法和算法。
二、集合的架构: Java集合框架主要由以下几个核心接口组成:
-
Collection接口:是集合框架中最基本的接口,定义了一组通用的方法,适用于所有的集合类型。
-
List接口:继承自Collection接口,表示有序的、可重复的集合。
-
Set接口:继承自Collection接口,表示无序的、不可重复的集合。
-
Map接口:表示键值对的集合,每个键是唯一的。
三、Collection接口: 3.1 List集合:
-
List是有序的集合,允许重复元素。
-
ArrayList
是基于数组实现的List集合,支持快速随机访问元素。 -
LinkedList
是基于链表实现的List集合,支持高效地在任意位置插入和删除元素。
3.2 Set集合:
-
Set是无序的集合,不允许重复元素。
-
HashSet
是基于哈希表实现的Set集合,使用哈希值来快速访问元素。 -
TreeSet
是基于红黑树实现的Set集合,对元素进行自然排序或者指定排序规则。
四、Map接口:
-
Map是键值对的集合,每个键是唯一的,但值可以重复。
-
HashMap
是基于哈希表实现的Map集合,通过键的哈希值来快速访问元素。 -
TreeMap
是基于红黑树实现的Map集合,可以对键进行自然排序或者指定排序规则。