Java基础(22)之集合进阶

本文深入探讨了Java集合框架,包括Collection、List、Set和Map四大接口及其子类的使用和特性。详细介绍了集合的遍历、迭代器、并发修改异常、泛型以及常用方法。此外,还讲解了数据结构如栈、队列、数组和链表,并分析了HashSet、LinkedHashSet和TreeSet的元素唯一性和排序机制。最后,讨论了泛型的概念、类型通配符、可变参数和Collections工具类的应用。
摘要由CSDN通过智能技术生成

在这里插入图片描述

目录

在这里插入图片描述

1. Collection

Collection常用方法详解

1.1 集合基础知识回顾

在这里插入图片描述

1.2 集合类体系结构

集合存储数据的方式有单列和双列
在这里插入图片描述
Clooection中还有List(可以存储可重复的数据)和Set(可以存储不可重复的数据)。
有重复的数存储到Set中,重复的数据就会被合一,变成一个。
在这里插入图片描述
List,Set和Map接口中还有实现类
下面是几个主要使用的实现类
在这里插入图片描述

1.3 Clooection集合概述和使用

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
输出的是集合元素,所以ArrayList集合已经重写了toString方法。
在这里插入图片描述

1.4 Clooection集合常用方法

在这里插入图片描述

1.5 Clooection集合的遍历 (Iterator)迭代器

代码示例:

创建Iterator迭代器:
在这里插入图片描述
使用next()方法:
在这里插入图片描述
运行结果:
返回了第一个集合元素,想要返回集合中多个元素就返回多次
在这里插入图片描述
如果这里next元素的次数超过了集合元素的数量那?
在这里插入图片描述
运行结果:
运行报错,报了,NoSuchEL ementException:表示被请求的元素不存在,
那怎么解决这样的问题那?向下看
在这里插入图片描述
为了防止发生这样的事情,所以要加判断,
hasNext()方法:
如果迭代具有更多的元素则(true)执行,没有则(false)不执行

在这里插入图片描述
运行结果:
第四个next方法没有执行,程序也没有报错。
在这里插入图片描述
如果集合中有多个元素这样的方式就显的非常的麻烦,但是使用while方法就会非常的便捷
while判断是否为true是则继续循环,不是则退出循环。
在这里插入图片描述
运行结果:
在这里插入图片描述

1.6 集合的使用步骤

在这里插入图片描述

2. List

List集合的常用方法
ArrayList的使用
ArrayList
LinkedList常用方法

2.1 List集合的概述和特点

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
输出了重复的元素
在这里插入图片描述
也可以使用迭代器器的方式进行遍历
在这里插入图片描述
运行结果:
在这里插入图片描述

2.2 List集合特有方法

List特有的方法Collection是没有的,但是List的实现类是有的
在这里插入图片描述

2.3 并发修改异常

ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常
ConcurrentModificationException异常原因和解决方法
Java并发修改异常
在这里插入图片描述
引发并发修改异常的原因是,在迭代器遍历的过程中有两个变量,实际修改值和预期修改中。
如果在迭代的过程中添加或删除一个元素,就会导致实际修改中的变化,在底层源码中如果这两个变量不相等的话就会有引发并发修改异常。

2.4 Listlterator

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
在这里插入图片描述
ListLIterator中最常用的方法就是add了
在这里插入图片描述
运行结果:
在迭代的过程中,使用ListLIterator的add添加元素不会报错并发修改异常,
因为,我们使用的是列表迭代器在添加元素,在Listlterator底层代码中,已经将实际修改值赋值给了预期修改值。
在这里插入图片描述

2.5 增强for循环

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
和使用while和for循环没有区别
在这里插入图片描述
其内部原理是一个Iterator迭代器,怎么证明那?
Iterator迭代器,如果在迭代的过程中修改集合就会引发并发修改异常
在这里插入图片描述
运行结果:
代码证明增强for循环内不的确是一个Iterator迭代器
在这里插入图片描述

2.6 数据结构

在这里插入图片描述

2.7 常见数据结构之栈

这一个容器(栈),一端是开口(栈顶)一段是封闭(栈底)
还有数据A B C D
在这里插入图片描述
演示数据存入这个栈和取出的过程:

数据进入栈模型的过程:
从栈顶进去:压/进栈
从ABCD的顺序进栈
在这里插入图片描述
数据全部从栈顶进去
在这里插入图片描述
栈中数据最顶端的是栈顶元素,最低端的是栈底元素
在这里插入图片描述
数据离开栈模型的过程:
数据离开栈模型的顺序是从栈顶元素开始:弹/出栈
从DBCA的吮吸出栈
在这里插入图片描述
一直到栈底元素
在这里插入图片描述

  • 总结:
    进栈是从ABCD这样的顺序进入的,出栈是从DCBA这样的顺序离开的。
    可见栈是一种先进后处的模型,因为它只有一段是开口的,它只能从开口进,从开口处。
    在这里插入图片描述

2.8 常见数据结构之队列

这一个容器(队列),一段是开口(后端),一段是开头(前端),还有数据ABCD
在这里插入图片描述
演示数据ABCD进队列和出队列的过程

进入队列过程:
数据从后端进入:入队列
从ABCD的顺序入队列
在这里插入图片描述
数据全部都是从后端进入队列
在这里插入图片描述
数据从后端进入队列的过程,叫做入列方向
在这里插入图片描述
数据离开
从前端离开:出队列
在这里插入图片描述
按照ABCD的顺序从前端出列
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • **总结:**队列的入队列和出队列都是从ABCD开始的,所以可以得出队列是一种先进先出的模型
    在这里插入图片描述

2.9 常见数据结构之数组

在这里插入图片描述

2.10 常见数据结构之链表

头结点
在这里插入图片描述
现在有一个头结点,没有连接上地址,它是空的。
在这里插入图片描述
在地址11的位置上,存储数据A
在这里插入图片描述
如果想将头结点和数据A连接起来,就需要将头结点的地址改为数据A的地址,这样就将两个结点连接了起来。
在这里插入图片描述
再连接数据C和D也是一样的,将地址值改为下一个数据的地址值。
在这里插入图片描述
如果想在结点AC之间添加一个数据B
在这里插入图片描述
删除数据BD之间的数据C
在这里插入图片描述
总结:
链表查询数据必须从头开始查起,所以它是一种查询慢的模型。
但是是一种增删快的模型。
在这里插入图片描述

2.11 List集合子类特点

不同需求使用的List集合子类也是不一样的。
ArrayList和LinkedList的使用是一样的,因为它们都是List集合的子类。
在这里插入图片描述

2.12 LinkedList集合的特有功能

在这里插入图片描述

2.13 总结

ArrayList和LinkedList总结
ArrayList源码分析
LinkedList源码分析

3. Set

Set总结
HashSet简单讲解
TreeSet

3.1 Set集合的概述和特点

没有索引,所以输出的顺序和添加的顺序是不一样的。
在这里插入图片描述
代码示例:
存储Set元素并遍历,
不支持普通for循环遍历,但是可以用增强for循环和迭代器的方法。
在这里插入图片描述
运行结果:
输出的顺序和我们add的顺序是不一样的,是因为,HashSet对集合的迭代顺序不作任何保证,
而且并没有输出添加的第二个“word”,是因为Set集合不支持重复元素。

在这里插入图片描述

3.2 哈希值

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
在这里插入图片描述
默认情况下,不同对象的哈希值是不同的,但是重写hashCode方法就不一样了
在对象中重写hashCode方法
在这里插入图片描述
运行测试类
在这里插入图片描述
运行结果:
都是0
在这里插入图片描述
字母字符串的哈希值是不一样的,但是一样的字母字符串的哈希值是一样的。
汉字的字符串的哈希值是一样的,因为它重写了hashCode方法。
在这里插入图片描述
运行结果:
在这里插入图片描述

3.3 HashSet集合概述和特点

在这里插入图片描述
代码示例:
在这里插入图片描述
运行结果:
存储的顺序不是这样的,可见HashSet对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序-致
在这里插入图片描述
再添加一次world
在这里插入图片描述
运行结果:
world只出现了一次,可见HashSet由于是Set集合,所以是不包含重复元素的集合
在这里插入图片描述

3.4 HashSet集合保证元素唯一性源码分析

HashSet添加一个元素的过程
在这里插入图片描述
结论:HashSet是更具hashCode()和equals()方法来判断元素是否重复的
在这里插入图片描述
代码分析:

public class HashSetDemo {
   

	public static void main(String[] args) {
   
		// 创建集合对象
		HashSet<String> hs = new HashSet<String>();
		// 添加元素
		hs.add("hello");
		hs.add("world");
		hs.add("java");
		
		hs.add("world");
		// 遍历
		for (String s : hs) {
   
			System.out.println(s);
		}
	}

}

运行结果:
没有输出添加的第二个world
在这里插入图片描述
HashSet保证了元素的唯一性,导致我们添加第二个world的时候没有执行,
所以查看源码是怎么判断的。

	// 创建HashSet集合对象
	HashSet<String> hs = new HashSet<String>();
	
	// 调用add方法,添加元素
	hs.add("hello");
	hs.add("world");
	hs.add("java");
	hs.add("world");
--------------------------------------------------------
add方法的源码:
	public boolean add(E e) {
    //e就是添加的元素
	    return map.put(e, PRESENT)==null; //将e作为第一个元素
	    //返回put方法
	}
	
put方法:
    public V put(K key, V value) {
    //key就是e,也就是添加的元素
        return putVal(hash(key), key, value, false, true); 
        //返回putVal方法之前,先调用了hash方法
    }
    
hash方法:
    static final int hash(Object key) {
    //key是添加的元素
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //根据添加元素的hashCode方法得到哈希值
        //返回得到添加元素的哈希值
    }
    
putVal方法:
	//hash值和元素的hashCode方法相关
	//元素的哈希值作为第一个参数hash,添加的元素作为第二个元素key
	final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
   
	        Node<K,V>[] tab; Node<K,V> p; int n, i; //哈希表其实是一个数组,Node表示为节点,所以表示元素为节点的数组
	        //如果哈希表未初始化,就对其进行初始化。
	        if ((tab = table) == null || (n = tab.length) == 0)
	            n = (tab = resize()).length;
			///根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储元素。
	        if ((p = tab[i = (n - 1) & hash]) == null)
	            tab[i] = newNode(hash, key, value, null);
	        else {
   
	            Node<K,V> e; K k;
	            /*
	            	存入的元素和以前的元素比较哈希值
						如果哈希值不同,会继续向下执行,把元素添加到集合
						如果哈希值相同,会调用对象的equals()方法比较
							如果返回false,会继续向下执行,把元素添加到集合
							如果返回true,说明元素重复,不存储
	            */
	            if (p.hash == hash &&
	                ((k = p.key) == key || (key != null && key.equals(k))))
	                e = p;
	            else if (p instanceof TreeNode)
	                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
	            else {
   
	                for (int binCount = 0; ; ++binCount) {
   
	                    if ((e = p.next) == null) {
   
	                        p.next = newNode(hash, key, value, null);
	                        if (binCount >= TREEIFY_THRESHOLD - <
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值