第十三章_Java中常用集合大整理
1.集合和数组的区别
-
集合
- 既可以存储基本数据类型还可以存储引用数据类型
- 定长–>是数组最大的特点,也是最大的缺点
-
数组
- 只能存储引用数据类型
- 长度可变
-
相同点
都是容器,可以存储多个数据
-
不同点
-
数组的长度是不可变的,集合的长度是可变的
-
数组可以存基本数据类型和引用数据类型
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
-
2.集合类体系结构
集合分为单列集合和双列集合,
-
单列集合(Collection):所有单列集合的顶级父类接口
- List(可重复) – 继承Collection
- ArrayList – 实现List
- LinkedList – 实现List
- Set(不可重复) – 继承Collection
- TreeSet – 实现Set
- HashSet – 实现Set
- LinkedHashSet – 继承HashSet
- List(可重复) – 继承Collection
-
存储的时候一个一个存
-
双列集合(Map):所有双列集合的顶级父类接口
- HashMap – 继承AbstractMap – 继承 Map
- LinkedHashMap – 继承HashMap
- TreeMap – 继承AbstractMap – 继承 Map
- HashMap – 继承AbstractMap – 继承 Map
-
存储的时候是一对一对存
-
图解
3.单列集合
单列集合框架:
3.1 Collection接口
- 概述
- 所有单列集合的顶级接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
- 创建Collection集合的对象
- 接口无法直接创建对象,使用多态的形式创建对象
- 具体的实现类创建。如:ArrayList
- Collection集合常用方法
- Collection中的方法在子类中都可以直接使用
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean addAll(Collection<? extends E>) | 将另一个集合元素添加到当前集合中。 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
Object[] toArray() | 把集合中的元素,存储到数组中 |
3.1.1 向集合添加元素
代码演示:
public class Test {
public static void main(String[] args) {
Collection<String> list1 = new ArrayList<>();
System.out.println("------------集合为空--------------");
System.out.println(list1);
System.out.println("------------方法:add--------------");
//添加
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
System.out.println(list1);
System.out.println("------------方法:addAll--------------");
//向一个集合中添加另一个集合
Collection<String> list2 = new ArrayList<>();
list2.add("蔡徐坤");
list2.add("吴亦凡");
list2.addAll(list1);
System.out.println(list2);
}
}
执行效果:
3.1.2 删除集合中的元素
代码演示:
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
System.out.println("------------方法:add--------------");
//添加
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("dddd");
System.out.println(list);
System.out.println("------------方法:remove--------------");
//删除指定的元素
boolean result1 = list.remove("aaa");
System.out.println(result1);
boolean result2 = list.remove("eee");
System.out.println(result2);
System.out.println(list);
System.out.println("------------方法:removeif--------------");
/*
//普通写法
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() == 4;
}
});*/
//lambda表达式写法
//根据条件删除元素
list.removeIf(s -> s.length() == 4);
System.out.println(list);
System.out.println("------------方法:clear--------------");
//清空集合
list.clear();
System.out.println(list);
}
}
执行效果:
3.1.3 集合中的其他方法
contains,isEmpty,size,toArray
代码演示:
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
System.out.println("------------方法:isEmpty + size--------------");
int size1 = list.size();
System.out.println(size1);
boolean result1 = list.isEmpty();
System.out.println(result1);
System.out.println("------------方法:add--------------");
//添加
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("dddd");
System.out.println(list);
System.out.println("------------方法:isEmpty + size--------------");
//查看集合的大小
int size2 = list.size();
System.out.println(size2);
//判断集合是否为空
boolean result2 = list.isEmpty();
System.out.println(result2);
System.out.println("------------方法:contains--------------");
//判断是否包含某个元素
boolean result3 = list.contains("eee");
System.out.println(result3);
boolean result4 = list.contains("aaa");
System.out.println(result4);
System.out.println("------------方法:toArray--------------");
//把集合转为一个数组
Object[] objects = list.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]);
}
}
}
执行效果:
3.1.4集合的遍历
-
普通for循环遍历
-
迭代器遍历
-
迭代器介绍:
本身是一个Iterator接口
-
作用:遍历集合的特有方式
-
方法:
boolean hasNext()–>判断有没有下一个元素
E next()–>获取下一个元素 -
怎么获取Iterator接口
Iterator<E.> iterator() -->也是Iterator的实现类对象 -
迭代器中删除的方法
void remove(): 删除迭代器对象当前指向的元素
-
-
增强for循环遍历
- 介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
- 使用:遍历数组,还可以遍历集合
- 格式:
for(容器中元素的类型 变量名:要遍历的容器名称){
System.out.println(变量名)
} - 增强for在遍历集合的时候,底层原理是迭代器,所以,不能在遍历集合的同时,改变集合长度
- 增强for在遍历数组的时候,底层原理不是迭代器
- 介绍
代码演示:
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("蔡徐坤");
list.add("吴亦凡");
list.add("乔碧萝");
list.add("giao哥");
list.add("奥利给");
System.out.println("----------迭代器-------------");
//利用迭代器遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("----------普通for-------------");
//利用普通for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("-----------增强for------------");
//利用增强for
for (String s : list) {
System.out.println(s);
}
}
}
执行效果:
3.1.5迭代器遍历注意事项
-
使用迭代器遍历集合的时候不能修改集合的长度
-
如遍历的时候向集合中添加元素,改变了集合的长度
会出现并发修改异常ConcurrentModificationException
-
解决方案,使用listIterator获取迭代器对象操作数据
代码演示(并发修改异常):
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("蔡徐坤");
list.add("吴亦凡");
list.add("乔碧萝");
list.add("giao哥");
list.add("奥利给");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
//不能修改集合的长度否则会有并发修改异常ConcurrentModificationException
if (s.equals("乔碧萝")) {
list.add("坦克");
}
}
System.out.println(list);
}
}
执行效果:
解决方案:
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("蔡徐坤");
list.add("吴亦凡");
list.add("乔碧萝");
list.add("giao哥");
list.add("奥利给");
System.out.println(list);
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("乔碧萝")) {
iterator.add("坦克");
}
}
System.out.println(list);
}
}
执行效果:
3.1.6迭代器中的删除方法
void remove(): 删除迭代器对象当前指向的元素
代码演示:
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("bbb");
list.add("ccc");
list.add("vvv");
//直接删除
// list.remove("bbb");
System.out.println(list);
//普通for循环遍历删除 -->删除时会有漏删的情况
//原因:集合中的一个元素被删除时,后面的元素会自动向前补齐空缺的索引
for (int i = 0; i < list.size(); i++) {
if ("bbb".equals(list.get(i))){
list.remove(list.get(i));
}
}
System.out.println(list);
//迭代器遍历删除
/* Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if ("bbb".equals(next)){
iterator.remove();
}
}
System.out.println(list);*/
}
}
执行效果:
普通for循环删除效果
迭代器删除效果:
3.1.7 迭代器并发修改异常源码分析
Iterator<String> iterator = list.iterator();
/* 循环迭代元素,在迭代的过程中,判断获取出来的元素是不是"乔碧萝"
如果是,调用add,往集合中添加"坦克"*/
while(iterator.hasNext()){
String element = iterator.next();
if (element.equals("乔碧萝")){
list.add("坦克");
}
}
=======================================================
modCount:实际操作元素的次数
expectedModCount:预期操作的次数
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
}
public E next() {
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
==============调用add方法的源码=============
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
}
3.2 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) 返回指定索引处的元素 -
数据结构
-
数据结构之栈和队列
-
栈结构
先进后出
-
队列结构
先进先出
-
-
数据结构之数组和链表
-
数组结构
查询快、增删慢
-
队列结构
查询慢、增删快
-
-
-
数据结构之栈的图解
- 数据结构之对列图解
3.2.1 ArrayList集合
-
数据结构:
底层是数组结构实现,查询快、增删慢
-
构造方法
方法名 | 说明 |
---|---|
public ArrayList() | 创建一个空的集合对象 |
- 常用成员方法
方法名 | 说明 |
---|---|
public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
public boolean add(E e) | 将指定的元素追加到此集合的末尾 |
public void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
ArrayList <E> :
可调整大小的数组实现
<E> : 是一种特殊的数据类型,泛型。
怎么用呢 ?
在出现E的地方我们使用引用数据类型替换即可
举例:ArrayList<String>, ArrayList<Student>
集合中添加元素(add)
代码演示:
public class ArrayList_add {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//默认添加是在集合的末尾
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
System.out.println(list);//[111, 222, 333, 444, 555]
//插入到指定索引的位置,后面的数据向后移一位
list.add(2,"aaa");
System.out.println(list);//[111, 222, aaa, 333, 444, 555]
}
}
执行效果:
删除集合中的元素(remove)
代码演示:
public class ArrayList_Remove {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//默认添加是在集合的末尾
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
list.add("111");
System.out.println(list);
//删除之后后面的元素会自动向前补充空缺位置
//根据元素进行删除 只删除集合中第一次出现的元素
boolean b1 = list.remove("111");
boolean b2 = list.remove("asfd");
System.out.println(b1);// 存在返回true
System.out.println(b2);// 不存在返回false
System.out.println(list);
//根据索引进行删除
String s = list.remove(0); // 返回被删除的元素
System.out.println(s);// 222
System.out.println(list);
}
}
执行效果:
修改集合中的元素(set)
代码演示:
public class ArrayList_Set {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//默认添加是在集合的末尾
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
//修改指定的元素
String s = list.set(0, "abc"); // 返回值是被修改的元素
System.out.println(s); // 111
System.out.println(list);//[abc, 222, 333, 444, 555]
}
}
执行效果:
获取集合中的元素(get)/大小(size)
代码演示:
public class ArrayList_Get {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//默认添加是在集合的末尾
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
//根据索引获取
String s1 = list.get(0);
String s2 = list.get(1);
String s3 = list.get(2);
System.out.println(s1);//111
System.out.println(s2);//222
System.out.println(s3);//333
//获取集合的大小
int size = list.size();
System.out.println(size);//5
}
}
执行效果:
遍历集合
代码演示:
public class ArrayList_For {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//默认添加是在集合的末尾
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
//遍历集合普通for
System.out.println("---------普通for遍历----------");
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
//增强for遍历
System.out.println("---------增强for遍历---------");
for (String s : list) {
System.out.println(s);
}
//迭代器遍历
System.out.println("---------迭代器遍历---------");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
}
}
执行效果:
ArrayList第一次添加元素源码分析
3.2.2 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 Test {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("aaa");
linkedList.add("vvv");
linkedList.add("bbb");
linkedList.add("ddd");
linkedList.add("fff");
System.out.println(linkedList);
System.out.println("----------------------");
//获取集合中的第一个元素
String first = linkedList.getFirst();
System.out.println("获取集合第一个元素:"+first);
System.out.println("---------------------");
//获取集合中的最后一个元素
String last = linkedList.getLast();
System.out.println("获取集合最后一个元素:"+last);
System.out.println("--------------------");
//在集合0索引位置添加
addFirst(linkedList);
System.out.println("----------------------");
//在集合末尾添加
addLast(linkedList);
System.out.println("----------------------");
//删除集合中第一个和最后一个元素
removeFisrtAndLast(linkedList);
}
private static void removeFisrtAndLast(LinkedList<String> linkedList) {
linkedList.removeFirst();
System.out.println("删除集合第一个元素:"+linkedList);
linkedList.removeLast();
System.out.println("删除集合最后一个元素:"+linkedList);
}
private static void addLast(LinkedList<String> linkedList) {
linkedList.addLast("www");
System.out.println("在集合末尾添加:"+linkedList);
}
private static void addFirst(LinkedList<String> linkedList) {
linkedList.addFirst("qqq");
System.out.println("在集合第一个位置添加:"+linkedList);
}
}
执行效果:
LinkedList添加元素源码分析
3.2.3 ArrayList集合与LinkedList集合的比较
-
ArrayList集合
底层是数组结构实现,查询快、增删慢
-
LinkedList集合
底层是链表结构实现,查询慢、增删快
3.3 Set集合
1.Set概述:接口-->没有对Collection的功能进行扩充
2.使用:根据实现类对象去创建
3.特点:
a.不允许出现重复元素
b.无序(LinkedHashSet除外)
c.没有索引-->增强for,迭代器遍历
4.实现类
HashSet
a.不允许出现重复元素
b.无序
c.没有索引-->增强for,和迭代器遍历集合
TreeSet
a.不允许出现重复元素
b.根据其元素的自然排序进行排序
c.根据指定的比较器进行排序
3.3.1 HashSet集合
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
- HashSet本身没有功能,依靠底层的HashMap
HashSet如何保证数据唯一?
- 先获取元素的哈希值,如果哈希值不一样,直接存
- 如果哈希值一样,再调用equals方法,比较内容,内容不一样,也可以存
- 如果哈希值一样,调用的equals方法内容也一样,HashSet直接去重复
代码演示:
public class Test {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
hashSet.add("java");
hashSet.add("java");
hashSet.add("java");
hashSet.add("java");
hashSet.add("通话");
hashSet.add("重地");
System.out.println(hashSet);
//哈希值一样,再调用equals方法,比较内容,内容不一样,也可以存
System.out.println("通话".hashCode());//1179395
System.out.println("重地".hashCode());//1179395
System.out.println("----------迭代器遍历----------");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("----------增强for遍历-----------");
for (String s : hashSet) {
System.out.println(s);
}
}
}
执行效果:
哈希表
-
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
-
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值
-
哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
-
JDK1.8以前
数组 + 链表
-
JDK1.8以后
-
节点个数少于等于8个
数组 + 链表
-
节点个数多于8个
数组 + 红黑树
-
LinkedHashSet
- LinkedHashSet extends HashSet
- 数据结构:链表+哈希表==>双链表
- 特点:
a.有序
b.不允许元素重复
c.没有索引
代码演示:
public class Test {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("吴亦凡");
linkedHashSet.add("蔡徐坤");
linkedHashSet.add("乔碧萝");
linkedHashSet.add("giao哥");
linkedHashSet.add("坦克");
linkedHashSet.add("坦克");
linkedHashSet.add("坦克");
System.out.println(linkedHashSet);
//遍历,没有索引,不能使用普通遍历
for (String s : linkedHashSet) {
System.out.println(s);
}
}
}
执行效果:
3.3.2 TreeSet集合
-
底层是一个红黑树结构,基于Set接口的红黑树的实现.
-
不可以存储重复元素
-
没有索引
-
可以将元素按照规则进行排序
-
TreeSet():根据其元素的自然排序进行排序。
使用元素的自然顺序对元素进行排序(内部会使用Comparator比较器对元素进行默认的升序排序),
-
TreeSet(Comparator comparator) :根据指定的比较器进行排序
-
自然排序
代码演示:
public class Test {
public static void main(String[] args) {
//会自动去重复和自动排序
TreeSet<Integer> treeSet1 = new TreeSet<>();
treeSet1.add(1);
treeSet1.add(5);
treeSet1.add(4);
treeSet1.add(4);
treeSet1.add(6);
treeSet1.add(3);
System.out.println(treeSet1);
System.out.println("--------------------");
TreeSet<String> treeSet2 = new TreeSet<>();
treeSet2.add("a");
treeSet2.add("c");
treeSet2.add("c");
treeSet2.add("b");
treeSet2.add("d");
System.out.println(treeSet2);
}
}
执行效果:
字符串的比较:
- 比较的是字符的ASCII码值
- 从字符串的第一个字符开始比较,如果相同就往后移一位接着比较
代码演示:
public class Test {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "aaa";
System.out.println(s1.compareTo(s2));//1
System.out.println("---------------------------");
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
treeSet.add("aba");
treeSet.add("aaa");
treeSet.add("vsag");
treeSet.add("biqwojh");
System.out.println(treeSet); //[aaa, aba, biqwojh, vsag]
}
}
执行效果:
比较器排序
- 自定义类实现Comparable接口
- 重写接口中的compareTo方法
代码演示:
public class TreeSetTest4 {
public static void main(String[] args) {
/*
内部类设置比较器规则
TreeSet<Teacher> treeSet = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
int result = o1.getAge() - o2.getAge();
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
*/
//λ表达式设置比较器规则按照年龄的升序排序
//如果年龄相同就再按名字自然排序的升序排序
TreeSet<Teacher> treeSet = new TreeSet<>(
(o1,o2) -> {
int result = o1.getAge() - o2.getAge();
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
);
treeSet.add(new Teacher("aaa",16));
treeSet.add(new Teacher("bbb",18));
treeSet.add(new Teacher("ccc",14));
// System.out.println(treeSet);
for (Teacher teacher : treeSet) {
System.out.println(teacher);
}
}
}
class Teacher {
private String name;
private int age;
public Teacher() {
}
public Teacher(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 String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
执行效果:
4. 双列集合
4.1 Map集合
- 概述:是一个双列集合的接口
- interface Map<K,V> K:键的类型;V:值的类型
- 存储元素的特点:键值对的形式存储
- 特点:
- 键唯一,不能重复,一个键对应一个值
- 无序
- 没有索引
代码演示:
public class Test {
public static void main(String[] args) {
//map集合中存储都是键值对。每次添加两个元素
/*
键不可重复,值可以重复
键值对是一一对应的,每个键都能找到相应的值
*/
Map<String,String> map = new HashMap<>();
map.put("A001","小红");
map.put("A002","小兰");
map.put("A003","小黑");
//如果键重复,后面的值会覆盖前面数据的值
map.put("A003","小黄");
System.out.println(map);
}
}
执行效果:
4.1.1 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 Test {
public static void main(String[] args) {
Map<String , String> map = new HashMap<>();
//如果添加数据时键已经存在,就会覆盖原来的值
map.put("A001","小红");
map.put("A002","小兰");
map.put("A003","小黑");
map.put("A004","小黄");
map.put("A005","小白");
System.out.println(map);
System.out.println("------------向集合中添加元素(键已存在)------------");
//向集合中添加元素
putMethod(map);
System.out.println("-----------删除集合中的元素(根据键删除)-------------");
//删除集合中的元素
removeMethod(map);
System.out.println("---------判断键是否存在---------------");
//判断键在集合中是否存在
conKeyMethod(map);
System.out.println("----------判断值是否存在--------------");
//判断值在集合中是否存在
conValMethod(map);
System.out.println("-----------获取集合的大小-------------");
//获取集合的大小
mapSize(map);
System.out.println("-----------判断集合是否为空-------------");
//判断集合是否为空
isEmpty(map);
System.out.println("------------清空集合------------");
clearMethod(map);
}
private static void mapSize(Map<String, String> map) {
int size = map.size();
System.out.println(map);
System.out.println(size);
}
private static void isEmpty(Map<String, String> map) {
boolean empty1 = map.isEmpty();
System.out.println(empty1);
map.clear();
boolean empty2 = map.isEmpty();
System.out.println(empty2);
}
private static void conValMethod(Map<String, String> map) {
boolean value1 = map.containsValue("aaa");
boolean value2 = map.containsValue("小黑");
System.out.println(value1);
System.out.println(value2);
}
private static void conKeyMethod(Map<String, String> map) {
boolean key1 = map.containsKey("A001");
System.out.println(key1);
boolean key2 = map.containsKey("A002");
System.out.println(key2);
}
private static void clearMethod(Map<String, String> map) {
map.clear();
System.out.println(map);
}
private static void removeMethod(Map<String, String> map) {
map.remove("A001");
System.out.println(map);
}
private static void putMethod(Map<String, String> map) {
map.put("A001","小紫");
System.out.println(map);
}
}
执行效果:
4.1.2 Map集合的获取功能
方法介绍
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V> values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
代码演示:
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
//如果添加数据时键已经存在,就会覆盖原来的值
map.put("A001", "小红");
map.put("A002", "小兰");
map.put("A003", "小黑");
map.put("A004", "小黄");
map.put("A005", "小白");
System.out.println(map);
//根据键获取值
System.out.println("===========根据键获取值==========");
String s = map.get("A001");
System.out.println(s);
//获取所有的键的集合
System.out.println("======获取键的集合=========");
Set<String> strings = map.keySet();
for (String string : strings) {
System.out.println(string);
}
//获取所有值的集合
System.out.println("========获取值的集合======");
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
//获取所有键值对集合
System.out.println("=========获取所有键值对集合========");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
//从键值对中获取键
String key = entry.getKey();
//从键值对中获取值
String value = entry.getValue();
System.out.println(entry);
System.out.println("键:" + key + ",值:" + value);
}
System.out.println("========foreach遍历===========");
map.forEach(
(String key, String value) ->
System.out.println("键:" + key + ",值:" + value)
);
//λ表达式简写
/*map.forEach(
(key,value)
->
System.out.println("键:" + key + ",值:" + value));*/
}
}
执行效果:
4.1.3 Map集合的遍历方式
- 方式1(通过键获取值遍历集合数据)
- 遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 把所有的丈夫给集中起来
- 遍历丈夫的集合,获取到每一个丈夫
- 根据丈夫去找对应的妻子
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 步骤分析
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用get(Object key)方法实现
- 遍历思路
- 代码演示:
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键的集合。用keySet()方法实现
Set<String> keySet = map.keySet();
//遍历键的集合,获取到每一个键。用增强for实现
for (String key : keySet) {
//根据键去找值。用get(Object key)方法实现
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
- 执行效果:
- 方式2(通过键值对获取集合的数据)
- 遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 获取所有结婚证的集合
- 遍历结婚证的集合,得到每一个结婚证
- 根据结婚证获取丈夫和妻子
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 步骤分析
- 获取所有键值对对象的集合
- Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象
- 用增强for实现,得到每一个Map.Entry
- 根据键值对对象获取键和值
- 用getKey()得到键
- 用getValue()得到值
- 获取所有键值对对象的集合
- 遍历思路
- 代码演示:
public class Test {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
- 执行效果:
4.2 HashMap集合
- HashMap底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法
4.2.1存储基本数据类型
代码演示:
public class Test {
public static void main(String[] args) {
//创建集合对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素
map.put("蔡徐坤", "乔碧萝");
map.put("吴亦凡", "凤姐");
map.put("Myself", "苍老师");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
执行效果:
4.2.2 存储自定义数据类型
- 要重写equals和hashCode方法保证数据的唯一性
- 如果不重写比较的是对象的地址值,两个对象的地址值不同就可以存,不能保证map集合的键唯一
未重写equals和hashCode方法,存储数据
代码演示:
public class Test {
public static void main(String[] args) {
HashMap<Student, String> hashMap = new HashMap<>();
hashMap.put(new Student("小黑",18),"河南");
hashMap.put(new Student("小红",16),"东北");
hashMap.put(new Student("小蓝",19),"北京");
//未重写equals和hashCode方法,默认比较地址值
hashMap.put(new Student("小黑",18),"广东");
System.out.println(hashMap);
System.out.println("----------------------------");
hashMap.forEach((student, str) -> System.out.println(student + "..." + str));
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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;
}
public Student() {
}
}
执行效果:
重写equals和hashCode方法,存储数据
代码演示:
public class Test {
public static void main(String[] args) {
HashMap<Student, String> hashMap = new HashMap<>();
hashMap.put(new Student("小黑",18),"河南");
hashMap.put(new Student("小红",16),"东北");
hashMap.put(new Student("小蓝",19),"北京");
//添加的自定义数据类型一致时,会覆盖
hashMap.put(new Student("小黑",18),"广东");
System.out.println(hashMap);
System.out.println("----------------------------");
hashMap.forEach((student,str)-> System.out.println(student+"..."+str));
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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);
}
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;
}
public Student() {
}
}
执行效果:
LinkedHashMap
-
特点:
- 存取有序
- key不能重复
- 没有索引
-
使用:
和HashMap一样,遍历也一样 -
代码演示:
public class Test { public static void main(String[] args) { LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put("1","老王"); linkedHashMap.put("2","老李"); linkedHashMap.put("3","老马"); linkedHashMap.put("4","老黄"); System.out.println(linkedHashMap); for (String key : linkedHashMap.keySet()) { String value = linkedHashMap.get(key); System.out.println("键:"+key+",值:"+value); } } }
执行效果:
4.3 TreeMap集合
-
TreeMap底层是红黑树结构
基于红黑树(Red-Black tree)的 NavigableMap 实现。
-
内部包含了一个比较器,会对key进行排序
依赖自然排序或者比较器排序,对键进行排序
-
如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
代码演示:
public class Test {
public static void main(String[] args) {
//以键的降序排序
TreeMap<Integer, String> treeMap = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
/*
λ表达式
TreeMap<Integer, String> treeMap = new TreeMap<>((o1, o2) -> o2-o1);
*/
treeMap.put(1, "坤坤");
treeMap.put(5, "吴亦凡");
treeMap.put(3, "Myself");
System.out.println(treeMap);
for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
System.out.println("键:"+entry.getKey()+",值:"+entry.getValue());
}
}
}
执行效果:
5.线程安全常用集合
- 单列集合,为保证线程安全使用vector集合
- 双列集合,为保证线程安全使用Hashtable和ConcurrentHashMap工具类
- Hashtable出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
- ConcurrentHashMap出现的原因 : Hashtable的效率低下,为了提高效率我们通常使用ConcurrentHashMap。
基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。
- HashMap是线程不安全的。多线程环境下会有数据安全问题
- Hashtable是线程安全的,但是会将整张表锁起来,效率低下
- ConcurrentHashMap也是线程安全的,效率较高。 在JDK7和JDK8中,底层原理不一样。
5.1 Vector
- 用法 : Vector<数据类型> 集合名 = new Vector<>();
- 特点:
- 有序
- 底层是数组
- 是同步的,线程安全,但是效率低
- 方法
- 与普通单列集合方法基本一致
5.2 HashTable
- 底层数据结构是哈希表
- HashTable采用的是悲观锁synchronized的形式保证数据的安全性
- 只要有线程访问就会把整张表全部锁按起来,所以HashTable的效率底下。
5.3 ConcurrentHashMap
- 既可以保证效率,又可以保证安全
5.4 并发工具类-ConcurrentHashMap1.7原理
5.5 并发工具类-ConcurrentHashMap1.8原理
- 如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做。
在第一次添加元素的时候创建哈希表 - 计算当前元素应存入的索引。
- 如果该索引位置为null,则利用cas算法,将本结点添加到数组中。
- 如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表。
- 当链表的长度大于等于8时,自动转换成红黑树
- 以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程操作集合时数据的安全性。