# 集合体系
# 单列集合 collection
# 遍历
# 迭代器
迭代器在遍历集合的时候是不依赖索引的
迭代器需要掌握三个方法
// 迭代器 ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add("ddd"); Iterator<String> it = list.iterator(); while (it.hasNext()){ String str = it.next(); System.out.println(str); }
迭代器的四个细节
- 如果当前位置没有元素,继续获取会报错 NoSuchElementException
- 迭代器遍历完成之后,指针不会复位,如果需要重新遍历,需要再次创建新的迭代器对象
- 循环中只能使用一次next方法,否则会遍历到没有元素的位置,报错***NoSuchElementException***
- 迭代器遍历时,不能使用集合的方法进行增加或者删除,只能使用迭代器自带的remove——方法删除
# 增强for
不能进行删除
// 增强for遍历,本质上还是迭代器(iterator)
for (String str : list){
System.out.println(str);
}
# lambda表达式
不能进行删除
// forEach方法遍历每个元素,传递到s
list.forEach(s -> System.out.println(s));
# List
添加的元素是有序(存的顺序和取是顺序相同)、可重复、有索引
# List 特有方法
// 添加元素,在指定位置插如元素,原来索引上的元素会依次向后移
list.add(3,"fff");
System.out.println(list);
// 删除元素,删除指定位置的元素,返回被删除的元素
System.out.println(list.remove(3));
System.out.println(list);
// 修改元素,将指定位置的元素更改为提供的元素,返回被修改的元素
System.out.println(list.set(2, "CCC"));
System.out.println(list);
// 获取元素,获取指定位置的元素
System.out.println(list.get(4));
# List 五种遍历方式
迭代器遍历
遍历的过程中需要删除时使用
// 1.迭代器遍历 Iterator<String> it = list.iterator(); while (it.hasNext()){ String str = it.next(); if (str.equals("bbb")){ it.remove(); // 删除元素 } System.out.println(str); }
列表迭代器遍历
遍历的过程中需要添加元素时使用
// 2.列表迭代器遍历 // 在遍历过程中可以添加元素 ListIterator<String> listIt = list.listIterator(); while (listIt.hasNext()){ String str = listIt.next(); if (str.equals("ccc")){ listIt.add("CCC"); // 添加元素 } System.out.println(str); }
增强for遍历
仅仅需要遍历时使用
// 3.增强for for (String str: list){ System.out.println(str); }
Lambda表达式
仅仅需要遍历时使用
// 4.lambda表达式 list.forEach( s -> System.out.println(s) );
普通for遍历
如果遍历的时候需要操作索引时使用
// 5.普通for循环 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
# Set
# HashSet
添加的元素是无序、不重复、无索引
底层原理
HashSet集合底层采取哈希表存储数据
哈希表是一种对于增删改查数据性能都较好的结构
哈希表组成
- jdk8之前:数组+链表
- jdk8开始:数组+链表+红黑树
哈希值
根据hashCode方法算出来的int类型的整数
该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
如果集合中存入的是自定义对象,必须要重写hashCode和equals方法
// 1.创建对象
Student s1 = new Student("张三",20);
Student s2 = new Student("张三",20);
// 2.如果没有重写hashCode方法,计算出的hash值是不同的
System.out.println(s1.hashCode());// 1523554304
System.out.println(s2.hashCode());// 1175962212
// 3.重写hashCode方法,计算的hash值就是相同的
System.out.println(s1.hashCode());// 24022540
System.out.println(s2.hashCode());// 24022540
存入数据过程
创建一个默认长度16,默认 加载因 为0.75的数组
当数组存入的数据达到(16*0.75=12)个时,数组就会扩容两倍
根据元素的哈希值跟数组的长度计算出应存入的位置
int index = (数组长度 -1) & 哈希值
判断当前位置是否为null,如果是null就直接存入
如果不为null,调用equals方法比较属序值
一样就不存入数据 不一样则存入数据,形成链表
jdk8之前:新元素存入数组,老元素挂在新元素下面
jkd8以后:新元素直接挂在老元素下面
当链表长度大于8且数组长度大于等于64时,当前链表就会转为红黑树
# LinkedHashSet
有序、不重复、无索引
这里的有序是指保证存储和取出的元素顺序一致
HashSet的子类,底层依然是哈希表,只是每个元素多了一个双链表的机制记录存储的顺序
# TreeSet
可排序、不重复、无索引
底层基于红黑树实现排序,增删改查性能都很好
自定义排序规则的方式
Javabean类实现Comparable接口,指定比较规则
public class 类名 implements Comparable<TestStudent> { @Override // 返回值 // 负数存在红黑树左边 // 正数存在红黑树右边 // 0表示已经存在,丢弃 public int compareTo(类名 o) { return this.参数 - o.参数; }
创建集合时,自定义Compartor比较器对象,指定比较规则
TreeSet<类名> ts = new TreeSet<>(new Comparator<类名>() { @Override // 返回值与方式一同理 public int compare(类名 o1, 类名 o2) { return o1.参数 - o2.参数; } });
默认使用方式一,如果方式一不满足需求,使用方式二
如果两种方式都存在,则使用的是第二种排序方式
# 使用场景
- 如果想要集合中的元素可重复
- 使用用ArrayList集合,基于数组(用的最多)
- 如果想要集合中的元素可重复,而且当前的增删改查操作明显多于查询
- 使用LinkedList集合,基于链表
- 如果想对集合中的元素去重
- 使用HashSet集合,基于哈希表(用的最多)
- 如果相对集合中的元素去重并保证存取数据
- 使用LinkedHashSet集合,基于哈希表的双链表,效率低于HashSet
- 如果想对集合中的元素进行排序
- 使用TreeSet集合,基于红黑树
# 双列集合 map
Map(映射)是一种常用的数据结构,它提供了一种键值对(Key-Value)的存储方式。Map接口是Java集合框架中的一员,它定义了一系列操作用于存储、检索、删除和操作键值对。
# HashMap
HashMap是基于哈希表实现的,它提供了快速的插入、删除和查找操作。键和值都可以为null,并且不保证元素的顺序。
# TreeMap
TreeMap基于红黑树(自平衡的二叉查找树)实现,它根据键的自然顺序或自定义比较器进行排序。TreeMap的元素按照键的有序状态进行存储。
# LinkedHashMap
LinkedHashMap是HashMap的一个子类,它保留了元素插入的顺序。在遍历时,元素按照插入顺序或最近访问顺序进行迭代。
# ConcurrentHashMap
ConcurrentHashMap是线程安全的HashMap实现,它支持高并发操作。它采用分段锁的方式来提供更好的并发性能。
Map接口提供了一系列常用的方法,包括:
- 添加元素:
put(key, value)
- 获取元素:
get(key)
- 删除元素:
remove(key)
- 判断是否包含某个键:
containsKey(key)
- 判断是否包含某个值:
containsValue(value)
- 获取Map的大小:
size()
- 清空Map:
clear()
- 获取所有键的集合:
keySet()
- 获取所有值的集合:
values()
- 获取所有键值对的集合:
entrySet()
# 遍历
使用Entry遍历:
Map<String, Integer> map = new HashMap<>(); // 添加键值对 map.put("A", 1); map.put("B", 2); map.put("C", 3); for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); // 对键值对进行操作 System.out.println("Key: " + key + ", Value: " + value); }
分别遍历键和值:
Map<String, Integer> map = new HashMap<>(); // 添加键值对 map.put("A", 1); map.put("B", 2); map.put("C", 3); // 遍历键 for (String key : map.keySet()) { // 对键进行操作 System.out.println("Key: " + key); } // 遍历值 for (Integer value : map.values()) { // 对值进行操作 System.out.println("Value: " + value); }
# 使用场景
HashMap:
- HashMap是最常用的Map实现类之一,适用于大多数情况。它提供了快速的插入、删除和查找操作。由于HashMap不保证元素的顺序,因此适用于不需要保持元素有序的场景。
LinkedHashMap:
- LinkedHashMap是HashMap的子类,它保留了元素插入的顺序。在遍历时,元素按照插入顺序或最近访问顺序进行迭代。适用于需要保持元素插入顺序的场景。
TreeMap:
- TreeMap基于红黑树实现,它根据键的自然顺序或自定义比较器进行排序。适用于需要按照键的有序状态进行存储和遍历的场景。
ConcurrentHashMap:
- ConcurrentHashMap是线程安全的HashMap实现,它支持高并发操作。通过采用分段锁的方式来提供更好的并发性能。适用于多线程环境下需要高并发访问的场景。