文章目录
Java集合类的总结
Java的集合类(Collection Classes)是Java Collections Framework的一部分,提供了一套数据结构和算法,用于存储和操作一组对象。
集合类体系结构
Collection集合
数组和集合的区别【理解】
相同点
都是容器,可以存储多个数据。
不同点
数组的长度是不可变的,集合的长度是可变的。
数组可以存基本数据类型和引用数据类型。
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类。
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。
- JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现。
Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
Collection集合的遍历
Iterator迭代器遍历
-
迭代器:集合的专用遍历方式
-
Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
-
Iterator中的常用方法
- boolean hasNext(): 判断当前位置是否有元素可以被取出
- E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
示例:
public class IteratorDemo1 {
public static void main(String[] args) {
//创建集合对象
Collection<String> c = new ArrayList<>();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
c.add("javaee");
//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator<String> it = c.iterator();
//用while循环改进元素的判断和获取
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
增强for
-
介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
-
格式
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) { // 已经将当前遍历到的元素封装到变量中了,直接使用变量即可 }
-
示例
public class MyCollectonDemo1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); //1,数据类型一定是集合或者数组中元素的类型 //2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素 //3,list就是要遍历的集合或者数组 for(String str : list){ System.out.println(str); } } }
-
细节点注意:
-
报错
NoSuchElementException
-
迭代器遍历完毕,指针不会复位
-
循环中只能用一次next方法
-
迭代器遍历时,不能用集合的方法进行增加或者删除
public class A04_CollectionDemo4 {
public static void main(String[] args) {
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.获取迭代器对象
//迭代器就好比是一个箭头,默认指向集合的0索引处
Iterator<String> it = coll.iterator();
//3.利用循环不断的去获取集合中的每一个元素
while(it.hasNext()){
//4.next方法的两件事情:获取元素并移动指针
String str = it.next();
System.out.println(str);
}
//当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置
//System.out.println(it.next());//NoSuchElementException
//迭代器遍历完毕,指针不会复位
System.out.println(it.hasNext());
//如果我们要继续第二次遍历集合,只能再次获取一个新的迭代器对象
Iterator<String> it2 = coll.iterator();
while(it2.hasNext()){
String str = it2.next();
System.out.println(str);
}
}
}
Java集合类的主要类别和特点
1. List接口
List集合的概述和特点
- List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
List集合的特有方法
-
方法介绍
方法名 描述 void add(int index,E element) 在此集合中的指定位置插入指定的元素 E remove(int index) 删除指定索引处的元素,返回被删除的元素 E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 E get(int index) 返回指定索引处的元素 -
示例代码
public class MyListDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); //method1(list); //method2(list); //method3(list); //method4(list); } private static void method4(List<String> list) { // E get(int index) 返回指定索引处的元素 String s = list.get(0); System.out.println(s); } private static void method3(List<String> list) { // E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 //被替换的那个元素,在集合中就不存在了. String result = list.set(0, "qqq"); System.out.println(result); System.out.println(list); } private static void method2(List<String> list) { // E remove(int index) 删除指定索引处的元素,返回被删除的元素 //在List集合中有两个删除的方法 //第一个 删除指定的元素,返回值表示当前元素是否删除成功 //第二个 删除指定索引的元素,返回值表示实际删除的元素 String s = list.remove(0); System.out.println(s); System.out.println(list); } private static void method1(List<String> list) { // void add(int index,E element) 在此集合中的指定位置插入指定的元素 //原来位置上的元素往后挪一个索引. list.add(0,"qqq"); System.out.println(list); } }
ArrayList
-
特点:基于动态数组,支持随机访问(查询快)。增删效率低,尤其是中间位置。
-
常见用法:
List<String> arrayList = new ArrayList<>(); arrayList.add("A"); arrayList.add("B"); System.out.println(arrayList.get(1)); // 输出: B
-
⭐ArrayList的扩容机制
-
扩容时机一:
当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15。再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。
-
扩容时机二:
如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。
举个例子:
在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够,此时新数组的长度,就以实际情况为准,就是110。
-
LinkedList
-
特点:基于双向链表,增删效率高,尤其是头尾。随机访问效率低。
-
特有方法
方法名 说明 public void addFirst(E e) 在该列表开头插入指定的元素 public void addLast(E e) 将指定的元素追加到此列表的末尾 public E getFirst() 返回此列表中的第一个元素 public E getLast() 返回此列表中的最后一个元素 public E removeFirst() 从此列表中删除并返回第一个元素 public E removeLast() 从此列表中删除并返回最后一个元素 -
示例代码
public class MyLinkedListDemo4 { public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); // public void addFirst(E e) 在该列表开头插入指定的元素 //method1(list); // public void addLast(E e) 将指定的元素追加到此列表的末尾 //method2(list); // public E getFirst() 返回此列表中的第一个元素 // public E getLast() 返回此列表中的最后一个元素 //method3(list); // public E removeFirst() 从此列表中删除并返回第一个元素 // public E removeLast() 从此列表中删除并返回最后一个元素 //method4(list); } private static void method4(LinkedList<String> list) { String first = list.removeFirst(); System.out.println(first); String last = list.removeLast(); System.out.println(last); System.out.println(list); } private static void method3(LinkedList<String> list) { String first = list.getFirst(); String last = list.getLast(); System.out.println(first); System.out.println(last); } private static void method2(LinkedList<String> list) { list.addLast("www"); System.out.println(list); } private static void method1(LinkedList<String> list) { list.addFirst("qqq"); System.out.println(list); } }
Vector
- 特点:线程安全,类似ArrayList,但由于线程同步开销较大,性能较低。
- 常见用法:
List<String> vector = new Vector<>(); vector.add("A"); vector.add("B"); System.out.println(vector.get(1)); // 输出: B
Stack
- 特点:继承自Vector,代表后进先出(LIFO)的堆栈。
- 常见用法:
Stack<String> stack = new Stack<>(); stack.push("A"); stack.push("B"); System.out.println(stack.pop()); // 输出: B
2. Set接口
Set集合概述和特点
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
Set集合的使用
存储字符串并遍历:
public class MySet1 {
public static void main(String[] args) {
//创建集合对象
Set<String> set = new TreeSet<>();
//添加元素
set.add("ccc");
set.add("aaa");
set.add("aaa");
set.add("bbb");
// for (int i = 0; i < set.size(); i++) {
// //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
// }
//遍历集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------------------------");
for (String s : set) {
System.out.println(s);
}
}
}
HashSet
-
特点
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
-
常见用法:
// 存储字符串并遍历 public class HashSetDemo { public static void main(String[] args) { //创建集合对象 HashSet<String> set = new HashSet<String>(); //添加元素 set.add("hello"); set.add("world"); set.add("java"); //不包含重复元素的集合 set.add("world"); //遍历 for(String s : set) { System.out.println(s); } } }
哈希值(理解)
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值
哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
哈希表结构
JDK1.8以前
数组 + 链表
JDK1.8以后
节点个数少于等于8个
数组 + 链表
节点个数多于8个
数组 + 红黑树
LinkedHashSet
- 特点:维护插入顺序的HashSet。
- 常见用法:
Set<String> linkedHashSet = new LinkedHashSet<>(); linkedHashSet.add("A"); linkedHashSet.add("B"); System.out.println(linkedHashSet); // 输出: [A, B]
TreeSet
-
特点
- 不可以存储重复元素
- 没有索引
- 可以将元素按照规则进行排序
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
-
常见用法:
存储Integer类型的整数并遍历:
public class TreeSetDemo01 { public static void main(String[] args) { //创建集合对象 TreeSet<Integer> ts = new TreeSet<Integer>(); //添加元素 ts.add(10); ts.add(40); ts.add(30); ts.add(50); ts.add(20); ts.add(30); //遍历集合 for(Integer i : ts) { System.out.println(i); } } }
-
自然排序、比较器排序 比较方式总结
- 两种比较方式小结
- 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
- 两种方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
- 两种比较方式小结
3. Queue接口
Queue接口用于按顺序处理元素的集合。
PriorityQueue
- 特点:基于堆实现的优先队列,元素按自然顺序或指定比较器排序。
- 常见用法:
Queue<Integer> priorityQueue = new PriorityQueue<>(); priorityQueue.add(3); priorityQueue.add(1); priorityQueue.add(2); System.out.println(priorityQueue.poll()); // 输出: 1
LinkedList
- 特点:也实现了Queue接口,可以作为队列使用。
- 常见用法:
Queue<String> queue = new LinkedList<>(); queue.add("A"); queue.add("B"); System.out.println(queue.poll()); // 输出: A
4. Deque接口
Deque接口代表双端队列,允许在两端插入和删除元素。
ArrayDeque
- 特点:基于动态数组实现的双端队列。
- 常见用法:
Deque<String> arrayDeque = new ArrayDeque<>(); arrayDeque.addFirst("A"); arrayDeque.addLast("B"); System.out.println(arrayDeque.removeFirst()); // 输出: A
LinkedList
- 特点:也实现了Deque接口,可以作为双端队列使用。
- 常见用法:
Deque<String> deque = new LinkedList<>(); deque.addFirst("A"); deque.addLast("B"); System.out.println(deque.removeLast()); // 输出: B
5. Map接口
Map集合概述和特点
-
Map集合概述
interface Map<K,V> K:键的类型;V:值的类型
-
Map集合的特点
- 双列集合,一个键对应一个值
- 键不可以重复,值可以重复
-
Map集合的基本使用
public class MapDemo01 { public static void main(String[] args) { //创建集合对象 Map<String,String> map = new HashMap<String,String>(); //V put(K key, V value) 将指定的值与该映射中的指定键相关联 map.put("itheima001","林青霞"); map.put("itheima002","张曼玉"); map.put("itheima003","王祖贤"); map.put("itheima003","柳岩"); //输出集合对象 System.out.println(map); } }
Map集合的基本功能
-
方法介绍
方法名 说明 V put(K key,V value) 添加元素 V remove(Object key) 根据键删除键值对元素 void clear() 移除所有的键值对元素 boolean containsKey(Object key) 判断集合是否包含指定的键 boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,也就是集合中键值对的个数 -
示例代码
public class MapDemo02 { public static void main(String[] args) { //创建集合对象 Map<String,String> map = new HashMap<String,String>(); //V put(K key,V value):添加元素 map.put("张无忌","赵敏"); map.put("郭靖","黄蓉"); map.put("杨过","小龙女"); //V remove(Object key):根据键删除键值对元素 // System.out.println(map.remove("郭靖")); // System.out.println(map.remove("郭襄")); //void clear():移除所有的键值对元素 // map.clear(); //boolean containsKey(Object key):判断集合是否包含指定的键 // System.out.println(map.containsKey("郭靖")); // System.out.println(map.containsKey("郭襄")); //boolean isEmpty():判断集合是否为空 // System.out.println(map.isEmpty()); //int size():集合的长度,也就是集合中键值对的个数 System.out.println(map.size()); //输出集合对象 System.out.println(map); } }
Map集合的获取功能
-
方法介绍
方法名 说明 V get(Object key) 根据键获取值 Set keySet() 获取所有键的集合 Collection values() 获取所有值的集合 Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合 -
示例代码
public class MapDemo03 { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<String, String>(); //添加元素 map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); //V get(Object key):根据键获取值 // System.out.println(map.get("张无忌")); // System.out.println(map.get("张三丰")); //Set<K> keySet():获取所有键的集合 // Set<String> keySet = map.keySet(); // for(String key : keySet) { // System.out.println(key); // } //Collection<V> values():获取所有值的集合 Collection<String> values = map.values(); for(String value : values) { System.out.println(value); } } }
HashMap
-
特点
- HashMap底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法
-
常见用法:
Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("One", 1); hashMap.put("Two", 2); System.out.println(hashMap.get("One")); // 输出: 1
LinkedHashMap
- 特点:维护插入顺序的HashMap。
- 常见用法:
Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put("One", 1); linkedHashMap.put("Two", 2); System.out.println(linkedHashMap); // 输出: {One=1, Two=2}
TreeMap
-
特点
- TreeMap底层是红黑树结构
- 依赖自然排序或者比较器排序,对键进行排序
- 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
-
常见用法:
Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("One", 1); treeMap.put("Two", 2); System.out.println(treeMap); // 输出: {One=1, Two=2}
6. 线程安全的集合类
除了基本集合类,Java还提供了一些线程安全的集合类,适用于多线程环境。
ConcurrentHashMap
- 特点:线程安全的HashMap,支持高并发。
- 常见用法:
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>(); concurrentHashMap.put("One", 1); concurrentHashMap.put("Two", 2); System.out.println(concurrentHashMap.get("One")); // 输出: 1
CopyOnWriteArrayList
- 特点:线程安全的ArrayList,适合读多写少的场景。
- 常见用法:
List<String> cowArrayList = new CopyOnWriteArrayList<>(); cowArrayList.add("A"); cowArrayList.add("B"); System.out.println(cowArrayList.get(1)); // 输出: B
CopyOnWriteArraySet
- 特点:线程安全的Set,基于CopyOnWriteArrayList实现。
- 常见用法:
Set<String> cowArraySet = new CopyOnWriteArraySet<>(); cowArraySet.add("A"); cowArraySet.add("B"); System.out.println(cowArraySet); // 输出: [A, B]
通过上述更详细的分类和描述,可以更好地理解和选择适合的Java集合类来处理不同的场景和需求。
集合类的特点和使用场景
1. ArrayList
- 特点:动态数组实现,随机访问快,增删效率相对较低(尤其是中间位置的插入和删除)。
- 使用场景:需要频繁查询的场景,如需要存储大量数据并进行随机访问。
2. LinkedList
- 特点:双向链表实现,增删操作快,查询效率相对较低。
- 使用场景:需要频繁插入和删除的场景,如实现队列和双端队列。
3. HashSet
- 特点:基于哈希表实现,不保证顺序,查找、插入和删除操作效率高。
- 使用场景:需要快速查找和去重的场景,如存储唯一元素的集合。
4. LinkedHashSet
- 特点:基于哈希表和链表实现,维护插入顺序。
- 使用场景:需要快速查找且需要维护插入顺序的场景。
5. TreeSet
- 特点:基于红黑树实现,元素有序,查找、插入和删除操作效率较高(但比HashSet略低)。
- 使用场景:需要排序和范围查询的场景,如有序集合的实现。
6. HashMap
- 特点:基于哈希表实现,键值对无序,查找、插入和删除操作效率高。
- 使用场景:需要快速查找和插入键值对的场景,如缓存实现。
7. LinkedHashMap
- 特点:基于哈希表和链表实现,维护插入顺序。
- 使用场景:需要快速查找且需要维护插入顺序的场景,如需要按插入顺序遍历键值对。
8. TreeMap
- 特点:基于红黑树实现,键值对按键的自然顺序或指定比较器排序。
- 使用场景:需要排序和范围查询的场景,如有序映射的实现。
使用示例
以下是一些基本使用示例:
// 使用 ArrayList
List<String> arrayList = new ArrayList<>();
arrayList.add("A");
arrayList.add("B");
System.out.println(arrayList); // 输出: [A, B]
// 使用 HashSet
Set<String> hashSet = new HashSet<>();
hashSet.add("A");
hashSet.add("B");
hashSet.add("A");
System.out.println(hashSet); // 输出: [A, B]
// 使用 HashMap
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("One", 1);
hashMap.put("Two", 2);
System.out.println(hashMap); // 输出: {One=1, Two=2}
总结
Java的集合类提供了一套强大而灵活的数据结构和算法,用于处理不同类型的集合操作。根据具体的需求选择合适的集合类,可以显著提高程序的性能和可读性。