提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
1.集合的概述
1.1 集合与数组的区别
相同点: 都是存储数据的容器
不同点:
①、长度方面:数组一旦初始化后,其长度是固定不变的。而集合是可变长的。
②、存储的类型:数组即可存储引用数据类型,也可存储基本数据类型。
集合只能存储引用数据类型。
1.2 集合的继承体系
集合的分类:
单列集合:类似于长度可变的“一维数组”
双列集合:类似于两个长度可变的“一维数组”
继承体系:
1.3 包装类
包装类型 : 基本数据类型所对应的引用数据类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
创建包装类型的对象 --> 不需要使用构造方法,因为Java语法提供了 自动装箱 和 自动拆箱 的概念
自动装箱 : 从基本数据类型 --> 引用数据类型
Integer num = 100; //隐藏代码 : Integer num = Integer.valueOf(100);
自动拆箱 : 从引用数据类型 --> 基本数据类型
int i = num; //隐藏代码 : int i = num.intValue();
包装类型中都有一个 解析字符串的基本值方法 (字符串类型的基本值 ---> 对应基本值) //Character中
没有此方法
static 对应基本数据类型 parseXxxx(字符串类型的基本值)
举例 : Integer -> static int parseInt(字符串类型的整数值);
举例 : Double -> static double parseDouble(字符串类型的小数值);
举例 : Boolean -> static boolean parseBoolean(字符串类型的布尔值);
2.单列集合
2.1 Collection接口
Collection概述:
Collection 是单列集合的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set
和 List
)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
Collection特点: 长度可变、只能存储对象
Collection继承体系图:
Collection常用的方法:
添加元素:
boolean add(E e) : 向集合中添加元素。
删除元素:
E remove(E e) : 移除集合中的元素。
void clear() : 清空集合中的元素。
查看相关:
int size() : 查看集合的长度。
判断相关:
boolean equals(Object o) : 比较此 collection 与指定对象是否相等。
boolean contains(Object o) : 判断集合中是否包含此元素。
boolean containsAll(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
boolean isEmpty() : 判断集合是否为空。
转换功能:
Object[] toArray() : 将集合转换成数组。
其它:
Iterator iterator() : 返回迭代器接口
Iterator接口的两个方法:
boolean hasNext() : 查看下一个位置是否有元素
E next() : 获取下一个位置的元素值,并将指针指向下一个位置
代码示例:
public class Demo01 {
public static void main(String[] args) {
// 创建集合,由于接口不能实例化,所有用多态的形式来表示
Collection<String> collection = new ArrayList<>();
// 1.添加元素add(E e)
collection.add("张三");
collection.add("李四");
collection.add("王五");
collection.add("赵六");
collection.add("李七");
System.out.println("collection = " + collection);
System.out.println("-----------------------------");
// 2.移除 remove(E e):
collection.remove("李四");
System.out.println("collection = " + collection);
System.out.println("-----------------------------");
// 3.查看此时长度size();
System.out.println("collection.size() = " + collection.size());
System.out.println("-----------------------------");
// 4.判断相关
// 判断集合是否为空isEmpty()
System.out.println("collection.isEmpty() = " + collection.isEmpty());
// 判断集合是否相同
Collection<String> collection1 = new ArrayList<>();
collection1.add("张三");
System.out.println("collection.equals(collection1) = " + collection.equals(collection1));
// 判断集合中是否包含此元素 contains(Object o)
System.out.println("collection.contains(\"张三\") = " + collection.contains("张三"));
// 判断集合是否包含collection1集合 containsAll()
System.out.println("collection.containsAll(collection1) = " + collection.containsAll(collection1));
System.out.println("-----------------------------");
// 5.转换相关:toArray()
Object[] objects = collection.toArray();
System.out.println("Arrays.toString(objects) = " + Arrays.toString(objects));
System.out.println("-----------------------------");
}
}
遍历操作:
①、将集合转成数组,遍历数组
②、增强for循环遍历
③、通过iterator方法调用迭代器进行迭代
public class Demo02 {
public static void main(String[] args) {
// 创建集合,由于接口不能实例化,所有用多态的形式来表示
Collection<String> collection = new ArrayList<>();
collection.add("张三");
collection.add("李四");
collection.add("王五");
collection.add("赵六");
collection.add("李七");
// 1.将集合转成数组,遍历数组
// ①、将集合转成数组
Object[] objects = collection.toArray();
// ②、遍历数组
for (int i = 0; i < objects.length; i++) {
String strName = (String) objects[i];
System.out.println("strName = " + strName);
}
System.out.println("---------------------------");
// 2.增强for
for (String name : collection) {
System.out.println("name = " + name);
}
System.out.println("---------------------------");
// 3.迭代器方式
// ①、获取迭代器
Iterator<String> iterator = collection.iterator();
// ②、通过while循环进行迭代
while (iterator.hasNext()){
String nextName = iterator.next();
System.out.println("nextName = " + nextName);
}
}
}
2.2 List接口
概述: 此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2)
的元素对 e1
和 e2
,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。
List接口的特点: 长度可变,存储引用数据类型、元素有索引、元素可以重复、元素存取有序
常用的方法:
添加相关的操作 :
boolean add(E e) : 向集合的末尾添加一个元素
void add(int index , E e) : 向集合的index索引后添加一个元素
删除相关操作 :
E remove(int index) : 根据索引删除集合中的元素。
boolean remove(Object o) : 根据对象删除集合中第一次出现的元素
void clear() : 清空集合中所有的元素
修改相关的操作 :
E set(int index , E e) : 根据索引修改元素的内容
查看相关的操作 :
int size() : 查看集合的长度
E get(int index) : 根据索引获取集合中的元素
int indexOf(Object o) : 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o) : 返回此列表中最后一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
boolean contains(Object o) : 判断集合中是否包含此元素。
boolean containsAll(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
boolean isEmpty() : 判断集合是否为空。
集合转换的操作
Object[] toArray() : 将集合转成Object类型数组
集合截取:
List<E> subList(int fromIndex, int toIndex) : 返回列表中指定的 fromIndex(包括)和toIndex(不包括)之间的部分视图。
其它操作:
listIterator() : 返回listIterator迭代器
iterator() : 返回Iterator迭代器
代码演示:
public class Demo01 {
public static void main(String[] args) {
// 创建List集合,采用多态形式
List<String> nameList = new ArrayList<>();
// 1.添加元素
nameList.add("张三");
nameList.add("李四");
nameList.add("王五");
nameList.add("赵六");
nameList.add("李七");
System.out.println("nameList = " + nameList);
nameList.add(2,"康八");
System.out.println("nameList = " + nameList);
System.out.println("===========");
// 2. 修改 set()
nameList.set(4,"王修改");
System.out.println("nameList = " + nameList);
System.out.println("===========");
// 3.查看
// 查看集合大小size();
System.out.println("nameList.size() = " + nameList.size());
// 获取指定索引的内容 get()
System.out.println("nameList.get(3) = " + nameList.get(3));
// 查找第一次出现的索引 indexOf()
System.out.println("nameList.indexOf(\"张三\") = " + nameList.indexOf("张三"));
// 查找最后依次出现的索引lastIndexOf()
System.out.println("nameList.lastIndexOf(\"李四\") = " + nameList.lastIndexOf("李四"));
// 判断集合是否为空isEmpty()
System.out.println("nameList.isEmpty() = " + nameList.isEmpty());
// 判断集合是否包含此元素contains()
System.out.println("nameList.contains(\"李四\") = " + nameList.contains("李四"));
// 判断集合是否包含此集合
List<String> list = new ArrayList<>();
list.add("李四");
System.out.println("nameList.containsAll(list) = " + nameList.containsAll(list));
System.out.println("===========");
// 3.将集合转成数组toArray()
Object[] objects = nameList.toArray();
System.out.println("Arrays.toString(objects) = " + Arrays.toString(objects));
System.out.println("===========");
// 4.集合截取
List<String> subList = nameList.subList(2, 3);
System.out.println("subList = " + subList);
System.out.println("===========");
// 5.删除
nameList.remove("李四");
System.out.println("nameList = " + nameList);
nameList.remove(3);
System.out.println("nameList = " + nameList);
nameList.clear();
System.out.println("nameList = " + nameList);
}
}
集合遍历:
①、将集合转成数组,遍历数组
②、普通for循环遍历:通过size()方法得到集合的大小,再通过for循环对集合进行遍历,通过get()方法获取元素
③、增强for
④、Iterator迭代器遍历
⑤、ListIterator迭代器遍历
public class Demo02 {
public static void main(String[] args) {
// 创建List集合,采用多态形式
List<String> nameList = new ArrayList<>();
// 添加元素
nameList.add("张三");
nameList.add("李四");
nameList.add("王五");
nameList.add("赵六");
nameList.add("李七");
// 1.将集合转成数组
Object[] objects = nameList.toArray();
for (int i = 0; i < objects.length; i++) {
String name = (String) objects[i];
System.out.println("name = " + name);
}
System.out.println("----------------------------");
// 2.增强for
for (String name : nameList) {
System.out.println("name = " + name);
}
System.out.println("----------------------------");
// 3.iterator迭代器遍历
Iterator<String> iterator = nameList.iterator();
while (iterator.hasNext()){
String name = iterator.next();
System.out.println("name = " + name);
}
System.out.println("----------------------------");
// 4.for循环
for (int i = 0; i < nameList.size(); i++) {
String name = nameList.get(i);
System.out.println("name = " + name);
}
System.out.println("----------------------------");
// 5.ListIterator迭代器
ListIterator<String> stringListIterator = nameList.listIterator();
while (stringListIterator.hasNext()){
String name = stringListIterator.next();
System.out.println("name = " + name);
}
}
}
注意事项:
1.Iterator迭代器迭代时会发生CurrentModifcationException并发修改异常
产生的原因:在使用迭代器遍历集合时,对集合同时进行了增删改的操作。
原理:在迭代器创建时,同时会创建一个镜像集合。当调用next()方法是,首先会比较两个镜像的内容是否相同。
若不相同则产生CurrentModifationException异常。
解决方法:在使用迭代器遍历集合时,最好不要对集合进行增删改的操作。
2.增强for循环在遍历集合时,其底层原理就是迭代器。而遍历数组时,其底层原始是普通for循环。
迭代器迭代的原理:
2.3 ArrayList类
概述: List接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null
在内的所有元素。除了实现 List
接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
ArrayList集合的特点: 底层是通过数组结构实现的,其查询快,插入删除慢。
常用的方法:
-- 创建方式
ArrayList<E> list = new ArrayList<E>();
-- 常用的方法:
添加相关的操作 :
boolean add(E e) : 向集合的末尾添加一个元素
void add(int index , E e) : 向集合的index索引后添加一个元素
删除相关操作 :
E remove(int index) : 根据索引删除集合中的元素。
boolean remove(Object o) : 根据对象删除集合中第一次出现的元素
void clear() : 清空集合中所有的元素
修改相关的操作 :
E set(int index , E e) : 根据索引修改元素的内容
查看相关的操作 :
int size() : 查看集合的长度
E get(int index) : 根据索引获取集合中的元素
int indexOf(Object o) : 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o) : 返回此列表中最后一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
boolean contains(Object o) : 判断集合中是否包含此元素。
boolean containsAll(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
boolean isEmpty() : 判断集合是否为空。
集合转换的操作
Object[] toArray() : 将集合转成Object类型数组
集合截取:
List<E> subList(int fromIndex, int toIndex) : 返回列表中指定的 fromIndex(包括)和toIndex(不包括)之间的部分视图。
其它操作:
listIterator() : 返回listIterator迭代器
iterator() : 返回Iterator迭代器
集合遍历:
①、将集合转成数组,遍历数组
②、普通for循环遍历:通过size()方法得到集合的大小,再通过for循环对集合进行遍历,通过get()方法获取元素
③、增强for
④、Iterator迭代器遍历
⑤、ListIterator迭代器遍历
ArrayList集合是实现List接口中的方法,方法的使用基本上和List接口一样,说以这里就不再重复写代码,参考List集合的代码示例
ArrayList底层扩容原理:
总结分析:
1.当创建一个AerrayList集合时,源码会创建一个长度为0的Object[]数组。
2.当向ArrayList集合中添加第一个元素时,源码会创建一个长度为10的数组来替换原来的数组 -> 扩容10
3.当向ArrayList集合中添加第十一个元素时,源码会创建一个长队为15的数组来替换原来的数组 -> 扩容15
4.依次类推,每当集合进行扩容时,源码都会创建一个长度为 ( 原数组长度 × 1.5 ) 的新数组来替换原数组 -> 扩容 1.5倍
5.当底层数组长度没有超过Integer.MAX_VALUES - 8(整型的最大值 - 8) ,则新数组的长度为:MAX_ARRAY_SIZE(也就是整型的最大值-8),
当底底层数组的长度超过Integer.MAX_VALUES - 8时,则新数组的长度为整型最大值(Integer.MAX_VALUES).
详细的底层原理分析:点击进入ArrayList底层源码解析
2.4 LinkedList类
概述: List
接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null
)。除了实现 List
接口外,LinkedList
类还为在列表的开头及结尾 get
、remove
和 insert
元素提供了统一的命名方法
LinkedList常用的方法:
-- 创建方式
LinkedList<E> list = new LinkedList<>();
-- 常用的方法:
添加相关的操作 :
boolean add(E e) : 向集合的末尾添加一个元素
void add(int index , E e) : 向集合的index索引后添加一个元素
删除相关操作 :
E remove(int index) : 根据索引删除集合中的元素。
boolean remove(Object o) : 根据对象删除集合中第一次出现的元素
void clear() : 清空集合中所有的元素
修改相关的操作 :
E set(int index , E e) : 根据索引修改元素的内容
查看相关的操作 :
int size() : 查看集合的长度
E get(int index) : 根据索引获取集合中的元素
int indexOf(Object o) : 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o) : 返回此列表中最后一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
boolean contains(Object o) : 判断集合中是否包含此元素。
boolean containsAll(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
boolean isEmpty() : 判断集合是否为空。
集合转换的操作
Object[] toArray() : 将集合转成Object类型数组
集合截取:
List<E> subList(int fromIndex, int toIndex) : 返回列表中指定的 fromIndex(包括)和toIndex(不包括)之间的部分视图。
其它操作:
listIterator() : 返回listIterator迭代器
iterator() : 返回Iterator迭代器
--新增的方法
E getFirst() : 获取第一个元素
E getLast() : 获取最后一个元素
E removeFirst() : 移除第一个元素
E removeLast() : 移除最后一个元素
void addFirt() : 在头元素中添加元素
void addlast() : 在尾部添加元素
E peekFirst() : 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
E peekLast() : 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
E pollFirst() : 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
E pollLast() : 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
boolean offerFirst(E e) : 在此列表的开头插入指定的元素。
boolean offerLast(E e) : 在此列表末尾插入指定的元素。
代码演示:(只演示新增的方法,其它方法参考2.2 List接口中的演示)
public class Demo01 {
public static void main(String[] args) {
LinkedList<String> nameList = new LinkedList<>();
// 添加元素
nameList.add("张三");
nameList.add("李四");
nameList.add("王五");
nameList.add("赵六");
nameList.add("李七");
System.out.println("nameList = " + nameList);
System.out.println("===========");
// E getFirst() : 获取第一个元素
System.out.println("nameList.getFirst() = " + nameList.getFirst());
// E getLast() : 获取最后一个元素
System.out.println("nameList.getLast() = " + nameList.getLast());
System.out.println("==========");
// E removeFirst() : 移除第一个元素
nameList.removeFirst();
System.out.println("nameList = " + nameList);
// E removeLast() : 移除最后一个元素
nameList.removeLast();
System.out.println("nameList = " + nameList);
System.out.println("==========");
// void addFirt() : 在头元素中添加元素
nameList.addFirst("张三");
System.out.println("nameList = " + nameList);
// void addlast() : 在尾部添加元素
nameList.addLast("李七");
System.out.println("nameList = " + nameList);
System.out.println("===========");
// E peekFirst() : 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println("nameList.peekFirst() = " + nameList.peekFirst());
// E peekLast() : 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
System.out.println("nameList.peekLast() = " + nameList.peekLast());
System.out.println("nameList = " + nameList);
System.out.println("===========");
// E pollFirst() : 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println("nameList.pollFirst() = " + nameList.pollFirst());
// E pollLast() : 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
System.out.println("nameList.pollLast() = " + nameList.pollLast());
System.out.println("nameList = " + nameList);
System.out.println("==========");
// boolean offerFirst(E e) : 在此列表的开头插入指定的元素。
System.out.println("nameList.offerFirst(\"aaa\") = " + nameList.offerFirst("aaa"));
// boolean offerLast(E e) : 在此列表末尾插入指定的元素。
System.out.println("nameList.offerLast(\"bbb\") = " + nameList.offerLast("bbb"));
System.out.println("nameList = " + nameList);
}
}
集合的遍历:
①、将集合通过toArray()方法转成数组,再对数组进行遍历
②、通过size()方法得到集合的大小,再通过for循环对集合进行遍历,通过get()方法获取元素
③、通过增强for循环直接获取
④、通过迭代器进行循环获取
⑤、通过列表迭代器进行循环获取
2.5 Vector类
Vector 是Java中最早的集合,在JDK1.2版本后并入集合体系当中。
Vector 中的方法绝大多数与上述集合相同。
Vector 是线程安全的集合,在vector集合中巨大多数方法都被Synchronized关键字修饰。 若多线程情况下,想要保证集合线程安全,就用此集合。
2.6 Set接口
概述: 一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2)
的元素对 e1
和 e2
,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
Set集合的特点: 元素唯一、存取无序、元素无索引值。
常用的方法:
-- 创建方法
Set<E> set = new Set<E>();
-- 常用的方法
添加操作:
boolean add(E e) : 向集合中添加元素。
删除操作:
boolean remove(Object o) : 删除集合中的元素。
void clear() : 清空集合中的元素。
查看相关操作:
int size() : 查看集合的大小
判断相关的操作:
boolean isEmpty() : 判断集合释是否为空。
boolean contains(Object 0) : 判断集合包含此元素对象。
boolean containsArr(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
遍历相关操作:
Object[] toArray() : 将集合转成数组。
Iterator<E> iterator() : 返回Iterator迭代器
代码示例:
public class Demo01 {
public static void main(String[] args) {
// 创建集合
Set<String> set = new HashSet<>();
// 1.添加元素
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("5");
System.out.println("set = " + set);
// 2.查看集合大小size()
System.out.println("set.size() = " + set.size());
// 3.判断相关操作
// 判断集合是否为空isEmpty()
System.out.println("set.isEmpty() = " + set.isEmpty());
// 判读集合是否包含此元素contains(Object o)
System.out.println("set.contains(\"2\") = " + set.contains("2"));
// 判断集合是否包含此集合中的内容containsAll()
Set<String> set2 = new HashSet<>();
set2.add("3");
System.out.println("set.containsAll(set2) = " + set.containsAll(set2));
// 4.删除相关操作
set.remove("1");
System.out.println("set = " + set);
set.clear();
System.out.println("set = " + set);
}
}
集合遍历操作:
①、将集合转成数组,遍历数组
②、增强for循环
③、利用迭代器迭代
public class Demo02 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("5");
// 1.将集合转成数组
Object[] objects = set.toArray();
for (Object object : objects) {
String str = (String) object;
System.out.println("str = " + str);
}
System.out.println("============");
// 2.增强for
for (String num : set) {
System.out.println("num = " + num);
}
System.out.println("============");
// 3.迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
iterator.next();
}
}
}
2.7 HashSet类
概述: 此类实现 Set
接口,由哈希表(实际上是一个 HashMap
实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null
元素。
HashSet集合的特点: 存取无序性,元素唯一性,无索引值。
常用的方法:
-- 创建对象
HashSet<E> hashSet = new HashSet<>();
-- 方法
添加操作:
boolean add(E e) : 向集合中添加元素。
删除操作:
boolean remove(Object o) : 删除集合中的元素。
void clear() : 清空集合中的元素。
查看相关操作:
int size() : 查看集合的大小
判断相关的操作:
boolean isEmpty() : 判断集合释是否为空。
boolean contains(Object 0) : 判断集合包含此元素对象。
boolean containsArr(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
遍历相关操作:
Object[] toArray() : 将集合转成数组。
Iterator<E> iterator() : 返回Iterator迭代器
遍历操作:
①、将集合转成数组,遍历数组
②、增强for循环
③、利用迭代器迭代
由于是Set接口的实现类,有没有特殊的方法。演示案例参考2.6 Set接口代码示例。
HashSet集合扩容原理:
底层源码分析:
结论 :
本质 : hash表去重是根据对象的hash值和equals方法进行去重的
默认 : hash表是按照对象的地址值进行去重 -> 没有重写hashCode
期望 : hash表按照对象的属性值去重 -> 请在对象所在的类中重写hashCode和equals方法
源码分析 :
去重的核心代码 :
if(老元素.hashCode() == 新元素.hashCode() && (老元素 == 新元素 || 新元素.equals(老元素)))
//判断为true就去重 判断为false就添加
//源码分析基于jdk7版本
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
为什么要加 (k = e.key) == key : 提高效率
1. 为同一个对象添加 提高效率
2. 为基本数据类型的元素添加 提高效率
详细的HashSet底层原理分析参考:点击进入HashSet底层原理详解
2.7 LinkedHashSet类
**概述:**具有可预知迭代顺序的 Set
接口的哈希表和链接列表实现。此实现与 HashSet
的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。
常用的方法:
-- 创建对象
LinkedHashSet<E> lhs = new LinkedHashSet<>();
-- 常用的方法:(无特殊的方法)
添加操作:
boolean add(E e) : 向集合中添加元素。
删除操作:
boolean remove(Object o) : 删除集合中的元素。
void clear() : 清空集合中的元素。
查看相关操作:
int size() : 查看集合的大小
判断相关的操作:
boolean isEmpty() : 判断集合释是否为空。
boolean contains(Object 0) : 判断集合包含此元素对象。
boolean containsArr(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
遍历相关操作:
Object[] toArray() : 将集合转成数组。
Iterator<E> iterator() : 返回Iterator迭代器
遍历方式:
①、将集合转成数组,遍历数组
②、增强for循环
③、利用迭代器迭代
由于是Set接口的实现类,有没有特殊的方法。演示案例参考2.6 Set接口代码示例。
2.8 TreeSet集合
概述: 基于 TreeMap
的 NavigableSet
实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator
进行排序,具体取决于使用的构造方法。 其底层是由红黑树组成。
TreeSet集合的特点: 元素唯一,元素存取无序,元素无索引
常用的方法:
-- 创建对象
LinkedHashSet<E> lhs = new LinkedHashSet<>();
-- 常用的方法:(无特殊的方法)
添加操作:
boolean add(E e) : 向集合中添加元素。
删除操作:
boolean remove(Object o) : 删除集合中的元素。
void clear() : 清空集合中的元素。
查看相关操作:
int size() : 查看集合的大小
判断相关的操作:
boolean isEmpty() : 判断集合释是否为空。
boolean contains(Object 0) : 判断集合包含此元素对象。
boolean containsArr(Collection<?> c) : 判断集合是否包含此集合中的所有元素。
遍历相关操作:
Object[] toArray() : 将集合转成数组。
Iterator<E> iterator() : 返回Iterator迭代器
-- 新增方法:
E first() : 返回此 set 中当前第一个(最低)元素
E last() : 返回此 set 中当前最后一个(最高)元素。
E floor(E e) : 返回此 set 中小于等于给定元素的最大元素;
E pollFirst() : 获取并移除第一个(最低)元素;如果此 set 为空,则返回 null。
E pollLast() : 获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null。
由于是Set接口的实现类,很多功能是实现Set接口的。这里演示新增的方法其它案例参考2.6 Set接口代码示例。
public class d_treeSet {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<>();
set.add("1");
set.add("3");
set.add("2");
set.add("5");
set.add("4");
System.out.println("set = " + set);
// E first() : 返回此 set 中当前第一个(最低)元素
System.out.println("set.first() = " + set.first());
// E last() : 返回此 set 中当前最后一个(最高)元素。
System.out.println("set.last() = " + set.last());
// E floor(E e) : 返回此 set 中小于等于给定元素的最大元素;
System.out.println("set.floor(\"3\") = " + set.floor("3"));
// E pollFirst() : 获取并移除第一个(最低)元素;如果此 set 为空,则返回 null。
System.out.println("set.pollFirst() = " + set.pollFirst());
System.out.println("set = " + set);
// E pollLast() : 获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null。
System.out.println("set.pollLast() = " + set.pollLast());
System.out.println("set = " + set);
}
}
遍历方式:
①、将集合转成数组,遍历数组
②、增强for循环
③、利用迭代器迭代
使用TreeSet条件 (实现二者其一):
- 使用的元素必须是实现自然排序(Comparable接口)。
- 在实例化TreeSet时,用有参构造传入一个定制排序规则(也就是Comparator接口的实现类)。
- 自定义类型的元素存储在TreeSet集合中的排序规则 : 如果元素的类型没有提供排序规则,那么直接报错ClassCastException (添加不进去)
2.9 自然排序、定制排序
2.9.1 常用排序的默认规则:
1.Integer : 默认升序 (由源码提供)
2.String : 默认升序(字符串中字符的ASCII码值的升序 ) //越靠前的字符ASCII值的优先级越高("10" 会排在在 "2" 字符串的前面)
汉字字符串排序无规则 (因为不知道汉字的码值,所以说是无规则的)
2.9.2 自然排序 Comparable 接口:
Comparable概述 : 此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
排序规则:
this -> 代表这新元素
Object o -> 代表着旧元素
升序排列 -> 新元素 - 老元素 -> this - o
降序排列 -> 老元素 - 新元素 -> o - this
使用步骤:
①、定义一个描述类,实现Comparable接口
②、重写compareTo方法
③、在compareTo方法中编写排序逻辑
/**
* 案例需求
* 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
* 要求:按照年龄从小到大排序
*/
public class Demo04 {
public static void main(String[] args) {
// 创建TreeSet集合
TreeSet<Student> students = new TreeSet<>();
students.add(new Student("张三" ,20));
students.add(new Student("李四",15));
students.add(new Student("赵六",11));
students.add(new Student("王七",25));
System.out.println("students = " + students);
}
}
// 1.定义一个学生类,实现Comparable接口
class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 2.重写compareTo方法
@Override
public int compareTo(Student o) {
// 3.在方法内定义排序规则
return this.age - o.age;
}
}
2.9.3 比较器排序(Comparator接口)
Comparator概述: 强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
排序规则:
Object o1 -> 新元素
Object o2 -> 老元素
升序排列 -> 新元素 - 老元素 -> o1 - o2
降序排列 -> 老元素 - 新元素 -> o2 - o1
使用步骤:
①、通过使用匿名对象来创建Comparator接口的实现类对象。
②、重写Comparator接口中的抽象方法compare()方法
③、在方法内些排序规则
/**
* 案例需求
* 存储学生对象并遍历,创建TreeSet集合使用有参构造方法
* 要求:按照年龄从小到大排序
*/
public class Demo05 {
public static void main(String[] args) {
// 2.创建TreeSet集合,并传入一个Comparator的实现类提供排序规则
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
// 3.重写compare方法
@Override
public int compare(Student o1, Student o2) {
// 4.在方法内编写排序逻辑
return o1.getAge() - o2.getAge();
}
});
students.add(new Student("张三" ,20));
students.add(new Student("李四",15));
students.add(new Student("赵六",11));
students.add(new Student("王七",25));
System.out.println("students = " + students);
}
}
// 1.创建一个类,不提供排序规则
class Student{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.9.4 排序规则总结:
两种比较方式小结
自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
两种方式中关于返回值的规则
如果返回值为负数,表示当前存入的元素是较小值,存左边
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
如果返回值为正数,表示当前存入的元素是较大值,存右边
3.双列集合
3.1 Map<K,V>接口
概述: 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。k :键的类型 ,v :值的类型
Map集合的特点:
长度可变,存储引用数据类型
键:键元素唯一,键存取无序,键无索引值
值:元素不唯一、元素无序、元素无索引 、存取无序
Map集合体系图:
常用的方法:
-- 创建对象
Map<K,V> map = new HashMap<>();
-- 常用的方法:
添加相关的操作:
V put(k key, V value) : 向集合中添加元素
删除相关的操作 :
V remove(K key) : 根据键值删除元素,并返回删除的元素值
void clear() : 清空元素
boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
修改相关操作 :
V put(K key , V value) : 如果集合存在相同的键名,那么put就为修改,将该键所对应的值进行修改
查看相关操作 :
V get(K key) : 获取键对应的值
int size() : 获取集合的长度
判断相关操作
boolean isEmpty() : 判断集合是否为空
boolean containsKey(K key) : 判断是否存在传入的键
boolean containsValue(V value) : 判断是否存在传入的值
遍历相关的操作 :
Set<E> keySet() : 获取集合中所有的键,并返回一个Set集合
Set<Map.Entry<K,V>> entrySet() : 返回此映射中包含的映射关系的 Set 视图。
代码演示:
public class Demo01 {
public static void main(String[] args) {
// 创建集合
Map<Integer , String> map = new HashMap<>();
// 添加数据
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
map.put(4,"赵六");
map.put(5,"李七");
System.out.println("map = " + map);
//修改操作 V put(K key , V value) : 如果集合存在相同的键名,那么put就为修改,将该键所对应的值进行修改
map.put(1,"张三a");
System.out.println("map = " + map);
System.out.println("===============================");
// 查看相关操作 :
// V get(K key) : 获取键对应的值
System.out.println("map.get(2) = " + map.get(2));
// int size() : 获取集合的长度
System.out.println("map.size() = " + map.size());
System.out.println("===============================");
// 判断相关操作
// boolean isEmpty() : 判断集合是否为空
System.out.println("map.isEmpty() = " + map.isEmpty());
// boolean containsKey(K key) : 判断是否存在传入的键
System.out.println("map.containsKey(1) = " + map.containsKey(1));
// boolean containsValue(V value) : 判断是否存在传入的值
System.out.println("map.containsValue(\"李四\") = " + map.containsValue("李四"));
System.out.println("===============================");
}
}
集合遍历:
①、通过 keySet()获取所有键,在通过键找值
②、通过entrySet() 进行遍历
public class Demo02 {
public static void main(String[] args) {
// 创建集合
Map<Integer , String> map = new HashMap<>();
// 添加数据
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
map.put(4,"赵六");
map.put(5,"李七");
// 1.通过 keySet()获取所有键,在通过键找值
// ①、获取所有的键
Set<Integer> keys = map.keySet();
// ②、遍历键获取值
for (Integer key : keys) {
String name = map.get(key);
System.out.println(key + "=>" + name);
}
// 2、通过entrySet() 进行遍历
// ①、获取Set<Map.Entry<K,V>>
Set<Map.Entry<Integer, String>> entries = map.entrySet();
// ②、遍历entries
for (Map.Entry<Integer, String> entry : entries) {
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=>" + value);
}
}
}
3.2 HashMap类
概述: 基于哈希表的 Map
接口的实现。此实现提供所有可选的映射操作,并允许使用 null
值和 null
键。(除了非同步和允许使用 null 之外,HashMap
类与 Hashtable
大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap是 Map 接口使用频率最高的实现类。
HashMap集合的特点:
①、允许使用null键和null值,与HashSet一样,不保证映射的顺序。
②、所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写: equals()和hashCode()
③、所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类 要重写:equals()
④、一个key-value构成一个entry
⑤、所有的entry构成的集合是Set:无序的、不可重复的
⑥、HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true, hashCode 值也相等。
⑦、HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
常用的方法:
-- 创建对象
HashMap<K,V> map = new HashMap<>();
-- 常用的方法:
添加相关的操作:
V put(k key, V value) : 向集合中添加元素
删除相关的操作 :
V remove(K key) : 根据键值删除元素,并返回删除的元素值
void clear() : 清空元素
boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
修改相关操作 :
V put(K key , V value) : 如果集合存在相同的键名,那么put就为修改,将该键所对应的值进行修改
查看相关操作 :
V get(K key) : 获取键对应的值
int size() : 获取集合的长度
判断相关操作
boolean isEmpty() : 判断集合是否为空
boolean containsKey(K key) : 判断是否存在传入的键
boolean containsValue(V value) : 判断是否存在传入的值
遍历相关的操作 :
Set<E> keySet() : 获取集合中所有的键,并返回一个Set集合
Set<Map.Entry<K,V>> entrySet() : 返回此映射中包含的映射关系的 Set 视图。
遍历方式:
①、通过 keySet()获取所有键,在通过键找值
②、通过entrySet() 进行遍历
HashMap是Map的实现类,继承了大多数方法,而HashMap暂无新增方法,代码演示参考3.1Map接口中的代码演示
HashMap底层原理:
1.底层结构:
JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。
2.底层扩容原理:
A. 在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容;
B. 每次扩展的时候,都是扩展2倍;
C. 扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。
3.3 LinkedHashMap类
概述: Map
接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap
的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。
常用的方法(沿用Map方法):
-- 创建对象
LinkedHashMap<K,V> map = new LinkedHashMap<>();
-- 常用的方法:
添加相关的操作:
V put(k key, V value) : 向集合中添加元素
删除相关的操作 :
V remove(K key) : 根据键值删除元素,并返回删除的元素值
void clear() : 清空元素
boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
修改相关操作 :
V put(K key , V value) : 如果集合存在相同的键名,那么put就为修改,将该键所对应的值进行修改
查看相关操作 :
V get(K key) : 获取键对应的值
int size() : 获取集合的长度
判断相关操作
boolean isEmpty() : 判断集合是否为空
boolean containsKey(K key) : 判断是否存在传入的键
boolean containsValue(V value) : 判断是否存在传入的值
遍历相关的操作 :
Set<E> keySet() : 获取集合中所有的键,并返回一个Set集合
Set<Map.Entry<K,V>> entrySet() : 返回此映射中包含的映射关系的 Set 视图。
遍历方式:
①、通过 keySet()获取所有键,在通过键找值
②、通过entrySet() 进行遍历
HashMap是Map的实现类,继承了大多数方法,而LinkedHashMap暂无新增方法,代码演示参考3.1Map接口中的代码演示
3.4 TreeMap类
概述: 基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
重点关注的是:
①、TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。 TreeMap 可以保证所有的 Key-Value 对处于有序状态。
②、TreeSet底层使用红黑树结构存储数据
③、TreeMap 的 Key 的排序:
自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有 的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
常用的方法(沿用了Map的方法)
-- 创建对象
LinkedHashMap<K,V> map = new LinkedHashMap<>();
-- 常用的方法:
添加相关的操作:
V put(k key, V value) : 向集合中添加元素
删除相关的操作 :
V remove(K key) : 根据键值删除元素,并返回删除的元素值
void clear() : 清空元素
boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
修改相关操作 :
V put(K key , V value) : 如果集合存在相同的键名,那么put就为修改,将该键所对应的值进行修改
查看相关操作 :
V get(K key) : 获取键对应的值
int size() : 获取集合的长度
判断相关操作
boolean isEmpty() : 判断集合是否为空
boolean containsKey(K key) : 判断是否存在传入的键
boolean containsValue(V value) : 判断是否存在传入的值
遍历相关的操作 :
Set<E> keySet() : 获取集合中所有的键,并返回一个Set集合
Set<Map.Entry<K,V>> entrySet() : 返回此映射中包含的映射关系的 Set 视图。
遍历方式:
①、通过 keySet()获取所有键,在通过键找值
②、通过entrySet() 进行遍历
HashMap是Map的实现类,继承了大多数方法,而TreeMap暂无新增方法,代码演示参考3.1Map接口中的代码演示
3.5 HashTable类
Hashtable<K,V> : 最早的双列集合
从JDK1.2版本并入到集合体系,被HashMap<K,V>取代;
需不要线程安全 : 推荐使用 HashMap<K,V>
需要线程安全 : 推荐使用 ConcurrentHashMap<K,V> //虽然Hashtable<K,V>线程安全,但是效率极低
4.泛型
4.1 泛型相关概述
- 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类 型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。
- 从JDK1.5以后,Java引入了“参数化类型(Parameterized type)”的概念, 允许我们在创建集合时再指定集合元素的类型,正如:List,这表明 该List只能保存字符串类型的对象。
- JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持, 从而可以在声明集合变量、创建集合对象时传入类型实参。
4.2 自定泛型结构
泛型类:
泛型类的格式 :
public class 类名<泛型>{
//此类中就多了一种 泛型类型 供此类使用
//只有在具体创建此类对象时来确定泛型的具体数据类型
}
泛型方法:
泛型方法的格式 :
权限修饰符 状态修饰符 <泛型> 返回值类型 方法名(形式参数列表){
//此方法中就多了一种 泛型类型 供此方法使用
方法体;
//return ..;
}
泛型方法中的泛型都会把泛型用在方法的形参上,调用方法时传入的形参是什么类型此时的泛型就是什么类型
泛型接口:
泛型接口的格式 :
public interface 接口名<泛型>{
//此 接口名中就多了一种 泛型类型 供此 接口名使用
//只有在具体创建此 接口名的实现类对象时来确定泛型的具体数据类型
}
泛型沿用问题
4.3 泛型的通配符和泛型的上下限问题(重点)
泛型的通配符 <?>
泛型的下限 <? super 类型>
泛型的上限 <? extends 类型>
不管是上限还是下限都包含后面的类型!