目录
集合框架
简单理解,就是包含了算法与数据结构课程中的一些数据结构,可以直接调用,不需要手动构造这些数据结构
使用集合框架需要import对应的包
- java.util.ArrayList
- java.util.LinkedList
- java.util.HashMap
- java.util.HashSet
- java.util.Hashtable
- java.util.Vector
集合框架概览
Collection接口:泛指广义上集合,主要表示List和Set两种存储方式。
List接口:表示列表,规定了允许记录添加顺序,允许元素重复的规范。元素有序且可重复,可以按照数组理解。
Set接口:表示狭义上集合,规定了不记录添加顺序,不允许元素重复的规范。元素无序且唯一,可以按照哈希表理解。
Map接口:表示映射关系,规定了两个集合映射关系的规范。每个元素对应一个键值,可以按照Python字典结构理解。
注意:都是接口,定义的是一些规范,是靠例如ArrayList等来具体实现的。
泛型
泛型可以理解为未知数据类型标志,当一个数据、返回值等数据类型不确定、可以是多种数据类型时,在编写类代码时,可以指定一个字母作为泛型代表未知数据类型,在创建对象、调用方法时再指定具体数据类型。
以下代码创建了一个数据类型不定的类与返回值不确定的方法:
public class MyArrayList<E> {
E e;
public E method(E e) {
return e;
}
}
当创建这个类的实例时,可以指定具体数据类型:
MyArrayList<String> stringMyArrayList = new MyArrayList<>();
List接口
表示列表,规定了允许记录添加顺序,允许元素重复的规范。元素有序且可重复,可以按照数组理解。
该接口常用的实现类有:
ArrayList类:数组列表,表示数组结构,底层采用数组实现,使用最多,重点。
LinkedList类:链表,表示双向列表和双向队列结构,采用链表实现,使用不多。
Stack类:栈,表示栈结构,采用数组实现,使用不多。
Vector类:向量,其实就是古老的ArrayList,采用数组实现,使用不多。
ArrayList类
public class ArrayListDemo1 {
public static void main(String[] args) {
// 创建一个默认长度的列表对象
List list = new ArrayList();
// 打印集合中元素的个数
System.out.println("元素数量:"+list.size());//0
// 添加操作:向列表中添加4个元素
list.add("Will");
list.add(100);
list.add(true);
list.add("Lucy");
// 查询操作:
System.out.println("列表中所有元素:"+list);//输出:[Will, 100, true, Lucy]
System.out.println("元素数量:"+list.size());//4
System.out.println("第一个元素:"+list.get(0));//Will
// 修改操作:把索引为2的元素,替换为wolfcode
list.set(2, "wolfcode");
System.out.println("修改后:"+list);//输出:[Will, 100, wolfcode, Lucy]
// 删除操作:删除索引为1的元素
list.remove(1);
System.out.println("删除后:"+list);//输出:[Will, wolfcode, Lucy]
}
}
LinkedList类
LinkedList类,底层采用链表算法,实现了链表,队列,栈的数据结构。无论是链表还是队列主要操作的都是头和尾的元素,因此在LinkedList类中除了List接口的方法,还有很多操作头尾的方法。
方法:
boolean offerFirst(Object e) 在此列表的开头插入指定的元素。
boolean offerLast(Object e) 在此列表末尾插入指定的元素。
Object peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
Object peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
Object pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
Object pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
void push(Object e) 将元素推入此列表所表示的栈。
Object pop() 从此列表所表示的栈处弹出一个元素。
Object peek() 获取但不移除此列表的头(第一个元素)。
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList list = new LinkedList();
//添加元素
list.addFirst("A");
list.addFirst("B");
System.out.println(list);//[B, A]
list.addFirst("C");
System.out.println(list);//[C, B, A]
list.addLast("D");
System.out.println(list);//[C, B, A, D]
//获取元素
System.out.println("获取第一个元素:" + list.getFirst());//C
System.out.println("获取最后一个元素:" + list.getLast());//D
//删除元素
list.removeFirst();
System.out.println("删除第一个元素后:" + list);//[B, A, D]
list.removeLast();
System.out.println("删除最后一个元素后:" + list);//[B, A]
}
}
集合遍历
一个一个手动获取
List<String> list = new ArrayList<>();
//以手动添加为例 获取同理
list.add("西施");
list.add("王昭君");
list.add("貂蝉");
list.add("杨玉环");
使用for循环遍历
for (int index = 0; index < list.size(); index++) {
String ele = list.get(index);
System.out.println(ele);
}
使用迭代器遍历(需要学习)
Iterator表示迭代器对象,迭代器中拥有一个指针,默认指向第一个元素之前
·boolean hasNext():判断指针后是否存在下一个元素
·Object next():获取指针位置下一个元素,获取后指针向后移动一位
·void remove():从迭代器指向的集合中移除迭代器返回的最后一个元素
Iterator<String> it = list.iterator();
//首先创建迭代器对象 map方法与此不同
while(it.hasNext()) {
String ele = it.next();
System.out.println(ele);
}
使用for-each遍历(简单推荐)
底层仍是Iterator实现
for (String ele : list) {
System.out.println(ele);
}
Set接口
表示狭义上集合,规定了不记录添加顺序,不允许元素重复的规范。元素无序且唯一,可以按照哈希表理解。
Set只包含从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素。当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
Set接口常用的实现类有:
- HashSet类:底层采用哈希表实现,使用最多,重点。
- TreeSet类:底层采用红黑树实现,可以对集合中元素排序,使用不多。
HashSet和TreeSet操作方法一样如下:
HashSet类
HashSet 是Set接口的实现类,底层数据结构是哈希表,集合容器不记录元素的添加顺序,也不允许元素重复。通常我们也说HashSet中的元素是无序的、唯一的。
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//添加操作:向列表中添加4个元素
set.add("Will");
set.add("wolf");
set.add("code");
set.add("Lucy");
//查询操作:
System.out.println("集合中所有元素:" + set);//[code, wolf, Will, Lucy]
System.out.println("元素数量:" + set.size());//4
System.out.println("是否存在某个元素:" + set.contains("code"));//true
System.out.println("是否存在某个元素:" + set.contains("code2"));//false
//删除操作:删除code元素
set.remove("code");
System.out.println("删除后:" + set);//[wolf, Will, Lucy]
//使用for-each遍历
for (String ele : set) {
System.out.println(ele);
}
//使用迭代器遍历
Iterator<String> it = set.iterator();
while (it.hasNext()) {
Object ele = it.next();
System.out.println(ele);
}
}
}
TreeSet类
TreeSet会对存入的元素默认进行从小到大排序,要保证TreeSet中存储的元素是相同的数据类型,否则会报错。
public class TreeSetDemo{
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("wolf");
set.add("will");
set.add("sfef");
set.add("allen");
System.out.println(set);// [allen, sfef, will, wolf]
//自动按照字母大小顺序从a~z排序
}
}
Map接口
Map可以按照Python字典的结构来理解,也可以按照函数的概念来理解,如图
从定义和结构图上,可以看出Map并不是集合,而表示两个集合之间的一种关系,故Map没有实现Collection接口。
在Map中,要求A集合中的每一个元素都可以在B集合中找到唯一的一个值与之对应。这句话可以解读为一个A集合元素只能对应一个B集合元素,也就说A集合中的元素是不允许重复的,B集合中的元素可以重复,也可不重复。那么不难推断出A集合应该是一个Set集合,B集合应该是List集合。举例理解:A集合是身份证号,是唯一的。B集合是姓名,是可以重复的。
A集合中的元素称为key,B集合中的元素称为value。每一对key-value表示为Entry。
Map常用操作方法
以下标红的是重点方法。
添加操作
boolean put(Object key,Object value):存储一个键值对到Map中
boolean putAll(Map m):把m中的所有键值对添加到当前Map中
删除操作
Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value
修改操作
修改操作无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。
查询操作
int size():返回当前Map中键值对个数
boolean isEmpty():判断当前Map中键值对个数是否为0.
Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null
boolean containsKey(Object key):判断Map中是否包含指定key
boolean containsValue(Object value):判断Map中是否包含指定value
Set keySet():返回Map中所有key所组成的Set集合
Collection values():返回Map中所有value所组成的Collection集合
Set entrySet():返回Map中所有键值对所组成的Set集合
HashMap
HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了在哈希表中的存储位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复。
HashMap key的底层数据结构是哈希表
补充:Hashtable 是一个古老的 Map 实现类,不建议使用
示例: 统计一个字符串中每个字符出现次数
public class HashMapDemo2{
public static void main(String[] args) {
String str = "ABCDEFABCDEABCDABCABA";
//把字符串转换为char数组
char[] charArray = str.toCharArray();
//Map的key存储字符,value存储出现的次数
Map<Character, Integer> map = new HashMap<>();
//迭代每一个字符
for (char ch : charArray) {
//判断Map中是否已经存储该字符
if (map.containsKey(ch)) {
Integer count = map.get(ch);
//如果已经存储该字符,则把出现次数加上1
map.put(ch, count+1);
}else {
//如果没有存储该字符,则把设置次数为1
map.put(ch, 1);
}
}
System.out.println(map);
}
}
TreeMap
TreeMap key底层基于红黑树算法,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复,但是Map中存储的key会默认使用自然排序(从小到大),和TreeSet一样,除了可以使用自然排序也可以自定义排序。
需求:测试HashMap和TreeMap中key的顺序
public class App {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("girl4", "杨玉环");
map.put("girl2", "王昭君");
map.put("key1", "西施");
map.put("key3", "貂蝉");
System.out.println(map);
//-------------------------------------------
map = new TreeMap<>(map);
System.out.println(map);
}
}
运行结果:
{key1=西施, girl4=杨玉环, key3=貂蝉, girl2=王昭君}
{girl2=王昭君, girl4=杨玉环, key1=西施, key3=貂蝉}
LinkedHashMap
Queue队列
队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。 Queue 接口扩展自 Collection,并提供插入、提取、检验等操作。
package com.jah.collection12;
import java.util.*;
public class TestQueue {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.offer("abc");
queue.offer("cde");
queue.offer("123");
queue.offer("abc");
System.out.println(queue);
System.out.println("queue.element():"+queue.element());
System.out.println(queue);
System.out.println("queue.peek():"+queue.peek());
System.out.println(queue);
System.out.println("queue.poll():"+queue.poll());
System.out.println(queue);
System.out.println("queue.remove():"+queue.remove());
System.out.println(queue); }
}
输出结果:
[abc, cde, 123, abc]
queue.element():abc
[abc, cde, 123, abc]
queue.peek():abc
[abc, cde, 123, abc]
queue.poll():abc
[cde, 123, abc]
queue.remove():cde
[123, abc]
Deque队列
接口 Deque,是一个扩展自 Queue 的双端队列,它支持在两端插入和删除元素,因为 LinkedList 类实现 了 Deque 接口,所以通常我们可以使用 LinkedList 来创建一个队列。
package com.jah.collection12;
import java.util.*;
public class TestDeque {
public static void main(String[] args) {
Deque<String> queue = new LinkedList<String>();
queue.offer("abc");
queue.offer("cde");
queue.offer("123");
queue.offer("abc");
queue.offerFirst("head");
queue.offerLast("tail");
System.out.println(queue);
System.out.println("queue.removeFirst():"+queue.removeFirst());
System.out.println(queue);
System.out.println("queue.removeLast():"+queue.removeLast());
System.out.println(queue);
} }
运行结果
[head, abc, cde, 123, abc, tail]
queue.removeFirst():head
[abc, cde, 123, abc, tail]
queue.removeLast():tail
[abc, cde, 123, abc]
补充:PriorityQueue 类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除
Stack栈
Stack 继承自 Vector,实现一个后进先出的堆栈
Stack 提供 5 个额外的方法使得 Vector 得以被当作堆栈使用。基本的 push 和 pop 方法,还有 peek 方法得到栈 顶的元素,empty 方法测试堆栈是否为空,search 方法检测一个元素在堆栈中的位置。
package com.jah.collection12;
import java.util.*;
public class TestDeque {
public static void main(String[] args) {
Stack<String> queue = new Stack<String>();
queue.add("abc");
queue.add("cde");
queue.push("123");
queue.push("xyz");
System.out.println(queue);
System.out.println("queue.pop():"+queue.pop());
System.out.println(queue);
}
}
运行结果:
[abc, cde, 123, xyz]
queue.pop():xyz
[abc, cde, 123]
Collections工具类
是一个操作 Set、List 和 Map 等集合的工具类,提供了大量方法,对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
多线程
主要内容略过
辅助/补充内容
多线程各类状态通俗理解
假设你正在烹饪一道菜,这个菜需要经过多个步骤才能完成。新建状态可以类比为你去超市购买食材和厨具,就绪状态可以类比为你把食材和厨具准备好并开始烹饪,运行状态可以类比为你在烹饪菜肴的过程中,阻塞状态可以类比为你在等待烤箱完成烘烤的过程中,锁池状态可以类比为你在等待其他客人离开餐桌的过程中,死亡状态可以类比为菜肴已经烹饪完毕并被端上餐桌。
继承Thread实现多线程通俗讲解
1.创建一个新的类并继承Thread类:首先,你需要创建一个新的类,这个类会继承Thread类。在Java中,任何继承Thread类的类都被称为线程类。
public class MyThread extends Thread {
// 线程的执行逻辑将会在这里定义
}
2.重写run()方法:在创建的线程类中,你需要重写run()方法。run()方法包含了线程的执行逻辑。当你调用start()方法启动线程时,线程会执行run()方法里的代码。
public class MyThread extends Thread {
@Override
public void run() {
// 这里是线程的执行逻辑
System.out.println("My thread is running!");
}
}
3.启动线程:当你完成了线程类的定义,你就可以使用start()方法来启动线程了。注意,你不能直接调用run()方法,因为这只会执行run()方法里的代码,而不会启动一个新的线程。
MyThread myThread = new MyThread();
myThread.start();
4.线程的状态:当线程启动后,它会进入就绪状态(Runnable)。当线程获得了CPU资源并开始执行时,它就进入了运行状态(Running)。当线程需要等待某个事件(如I/O操作)发生时,它就进入了阻塞状态(Blocked)。当线程正在等待某个锁释放时,它就进入了锁池状态(Waiting)。当线程执行完毕或被强制终止时,它就进入了死亡状态(Dead)。
5.注意事项:在Java中,我们不推荐直接继承Thread类来创建线程,因为这样做会导致我们的代码和Java的类层次结构紧密耦合。更好的做法是实现Runnable接口,并将实现放到实现类中。这样,我们可以更灵活地在不同的线程类中使用同一个Runnable实现。此外,Java还提供了其他高级的并发工具,如Executor框架、Fork/Join框架等,这些工具提供了更强大和灵活的并发处理能力。