史上最全讲解:JAVA中的Map与Thread
文章目录
Map
- Map :存储的每一个数据都是一个键值对形式存在的 k,v
- key 无序的**,唯一的** -->就是一个set集合
- value 无序的,可重复的
- 一个key对象一个value,两者之间存在映射关系
- 如果存储数据的时候,key相同,value的值会覆盖
- map中,HashMap的去重,根据Map的key进行计算
- TreeMap的去重+排序,根据Map的key进行计算
- Map中的去重都是根据key来进行计算的
- 如果一个key,想要对象多个value值,可以把多个value存在一个容器中,而容器对象是一个
- 如果使用HashMap存储数据,key是自定义的引用数据类型 Student对象,value存储学生的身高
- 要求:实现去重,如果key相同,value不许覆盖
Map的实现类接口有两个 HashMap和TreeMap
HashMap
- HashMap 的key值如果是自定义引用数据类型,如果要达达到去重的目的,需要重写hashcode 和 equals方法,如果要想相同的key情况下的value值不被覆盖,那么需要在获取值(put)之前进行判断当前的key是否存在(containsKey)
package com.shsxt.map02;
import java.util.HashMap;
public class HashMapDemo03 {
static HashMap<Student,Double> map=new HashMap();
public static void main(String[] args) {
/*map.put(new Student("1001","于泳洋",18), 180.00);
map.put(new Student("1001","于泳洋",18), 190.00);*/
putValue(new Student("1001","于泳洋",18), 180.0);
putValue(new Student("1001","于泳洋",18), 190.0);
System.out.println(map);
}
public static void putValue(Student s ,Double d){
if(!map.containsKey(s)){
map.put(s, d);
}else{
System.out.println("已经存在指定的key");
}
}
}
class Student implements Comparable<Student>{//根据键值student来进行去重
//内部比较器用Comparable
private String id;
private String name;
private int height;
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String id, String name, int height) {
super();
this.id = id;
this.name = name;
this.height = height;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", height=" + height + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + height;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (height != other.height)
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Student o) {//对身高进行排序
// TODO Auto-generated method stub
//if()
return Double.compare(o.height,this.height );
}
}
TreeMap
- TreeMap 去存储自定义的引用数据类型的数据key是自定义的引用数据类型 Student对象,根据学生的身高进行排序
- TreeaMap的特点是:其中数据会自动升序排序,根据学生的身高进行排序
- 实现的步骤:
-
1.去重 实现内部|外部比较器 比较的原则根据key,不能根据value
-
2.根据需求排序
-
key的内容决定排序和去重的原则,这是TreeMap默认的比较过程
package com.shsxt.map02;
import java.util.TreeMap;
public class TreeMapDemo04 {
static TreeMap<Student,String> tree=new TreeMap((s1,s2)-> ((Student)s2).getHeight() -((Student)s1).getHeight());
public static void main(String[] args) {
tree.put(new Student("100","张三",180), "java");
tree.put(new Student("101","李四",170), "大数据");
tree.put(new Student("100","张三",180), "python");
System.out.println(tree);
}
}
package com.shsxt.map02;
//Student类
public class Student implements Comparable<Student>{
private String name;
private double height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student(String name, double height) {
super();
this.name = name;
this.height = height;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [name=" + name + ", height=" + height + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(height);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (Double.doubleToLongBits(height) != Double.doubleToLongBits(other.height))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Student o) {//对身高进行排序
// TODO Auto-generated method stub
//if()
return Double.compare(o.height,this.height );
}
}
Hash(HashSet/HashMap)与Tree(TreeSet/TreeMap)比较:
都是不可重复的 无序的
在去重的时候自定义数据类型都需要重写hashcode和equals方法
Tree在排序的时候,自定义类型需要实现Compatator接口(compare方法外部比较器)或者继承Comporatable(compareTo)内部比较器
Properties
Properities是Hashtable的子类的子类,要求键与值只能为字符串 ,不能为 null ,常与 配置文件与外界交互 的信息 ) 即内存与存储介质 文件、数据库、网络、服务器内存等 交互。
- Properties Properties 可保存在流中或从流中加载。 key与value都是字符串类型的数据
- 配置文件使用:
- 1.项目右键->source floder->file->添加键值对类型的数据
- 2.通过Properties 的load方法,从资源文件中获取资源流,读入数据
package com.shsxt.properties;
import java.io.IOException;
import java.util.Properties;
public class Properties01 {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
System.out.println(pro.getProperty("name"));
}
}
HashMap\HashTable\Properties三者的比较:
HashMap: 线程不安全,效率高 . 允许 key 或 value 为 null
HashTable: 线程安全,效率低 . 不允许 key 或 value 为 null
Properties: Hashtable 的子类, key 和 value 都是 string
Thread
多线程:
多任务同时执行,多条路径执行,如果没有多任务,不需要使用多线程
线程和进程之间的区别:
1.进程包含1~n个线程
2.每个进程有自己的代码和运行空间
3.一系列线程共享继承的资源
4.进程是资源分配的最小单位,线程是cpu调度的最小单位
5.线程之间切换开销小,进程之间开销大.
多线程的开启方式****
1.继承Thread类,重写run()方法,在方法中定义多线程的线程体
2.实现Runnable接口,重写run()方法,在方法中定义多线程的线程体 —推荐
3.实现Callable接口,重写call()方法,在方法中定义多线程的线程体
多线程的状态问题
多线程的线程安全问题 ****
线程通信问题
开启多线程方法1
继承Thread类,重写run()方法,在方法中定义多线程的线程体
public class ThreadDemo01 extends Thread {
/*
* 定义线程体
*/
@Override
public void run() {
for(int i=0;i<=20;i++){
System.out.println("一遍敲代码...");
}
}
public static void main(String[] args) {
//线程创建
ThreadDemo01 th=new ThreadDemo01();
//th.run(); //方法的调用
th.start(); //线程的开启
for(int i=0;i<=20;i++){
System.out.println("一遍聊微信...");
}
}
}
多线程开启方法2
- 实现Runnable接口,重写run()方法
1.接口可以多实现,类只能单继承
2.实现资源共享
package com.shsxt.thread05;
public class ThreadDemo02 implements Runnable{
public static void main(String[] args) {
ThreadDemo02 th = new ThreadDemo02();
Thread t = new Thread(th);//使用静态代理 将th对象座位参数传进来
//使用Thread的start方法 降低耦合
t.start();
for(int i=0;i<50;i++){
System.out.println("123");
}
}
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("456");
}
}
}
多线程开启方法3
实现callable接口,重写call()方法
特点:异常可以捕获(runnable接口run方法异常只能抛出),效率高
12306买票问题
package com.shsxt.thread05;
/*
* 100张票,3个人买完
* 共享的资源:100张票
*
*/
public class Web12306_03 implements Runnable{
int tickets=100;
/*
* 买票
*/
@Override
public void run() {
while(true){
//A B C
if(tickets<=0){
break;
}
try {
Thread.sleep(100); //ms数
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
}
}
public static void main(String[] args) {
Web12306_03 web=new Web12306_03();
Thread t1=new Thread(web,"凌希");//Thread的重载方法
Thread t2=new Thread(web,"张苏蒙");
Thread t3=new Thread(web,"刘易舟");
t1.start();
t2.start();
t3.start();
}
}
龟兔赛跑
龟兔赛跑: 谁先跑完100步就赢了,但是兔子每跑十步休息2ms,只要有人赢了,游戏就结束
package com.shsxt.thread05;
/*
* 比赛类:
*/
public class Racer04 implements Runnable{
String winner = null; //存储胜利者的名字
@Override
public void run() {
for(int i=1;i<=100;i++){
if("兔子".equals(Thread.currentThread().getName()) && i%10==0){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(checkOver(i)){
break;
}
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
if(checkOver(i)){
break;
}
}
}
/*
* 如果返回true,代表游戏结束
* 如果返回false,证明游戏继续
*/
public boolean checkOver(int i){
if(winner==null){
if(i==100){
winner= Thread.currentThread().getName();
}else{
return false;
}
}
return true;
}
public static void main(String[] args) {
Racer04 race=new Racer04();
Thread th1=new Thread(race,"兔子");
Thread th2=new Thread(race,"乌龟");
th1.start();
th2.start();
}
}
线程状态
-
新生状态new :new的时候,线程处于新生状态
-
就绪状态runnable:start()方法后线程进入到就绪状态,等待cpu的调度,进入到就绪队列
-
运行状态running:cpu把时间片资源分配给某一个线程,线程才可以进入到运行状态
-
阻塞状态block:sleep()非能够正常运行完毕,直到阻塞解除
-
终止状态dead:线程结束
-
一个线程如何进入就绪状态:
1.start()
2.yield() 礼让线程
yield() 礼让线程,高风亮节
package com.shsxt.threadstate;
public class ThreadYield02 implements Runnable{
public static void main(String[] args) {
new Thread(new ThreadYield02(),"A").start();
new Thread(new ThreadYield02(),"B").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开启了");
Thread.yield();//礼让出来之后 CPU再次分配
System.out.println(Thread.currentThread().getName()+"结束了");
}
}
3.阻塞解除
4.线程切换,被切换的线程直接进入就绪状态
一个线程如何进入阻塞状态:
1.sleep()
sleep(): 抱着资源睡觉
对象锁不会释放,但是会让出cpu的资源
放大问题的可能性
模拟网络延迟
2.wait() 线程等待
3.join() 插队线程(儿子买烟例子)
爸爸让儿子买烟,先发出买烟的指令,儿子接收到以后再去买烟,然后爸爸抽烟,并不是爸爸一说让儿子买烟自己就能抽到,此时需要开启儿子买烟这个线程,买到烟之后(即买烟线程结束)爸爸再抽烟(即执行抽烟的代码)
package com.shsxt.threadstate;
public class ThreadJoin01 {
public static void main(String[] args) {
new Thread(new Father()).start();//自己没有start方法 借用Thread的
}
}
class Father implements Runnable {
@Override
public void run() {
System.out.println("买烟");
Thread th = new Thread(new Son());
th.start();
try {
th.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("抽烟");
}
}
class Son implements Runnable {
@Override
public void run() {
System.out.println("去买烟");
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i+"秒过去了");
}
}
}
Thread.State getState() 返回该线程的状态。
线程的优先级: 1~10,1最小 10最大,默认5
提高优先执行可能性,但是不是一定的
void setPriority(int newPriority) 更改线程的优先级
int getPriority() 返回线程的优先级。
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
package com.shsxt.threadstate;
import java.lang.Thread.State;
public class ThreadState03 implements Runnable{
public static void main(String[] args) {
Thread th = new Thread(()->{
for(int i=1;i<10;i++){
if(i==5){
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
}
});
System.out.println(th.getState());
th.start();
System.out.println(th.getState());
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(th.getState());
if(th.getState()==State.TERMINATED){//状态是枚举类型 需要用类名点的形式使用
System.out.println(Thread.currentThread().getName()+"当前线程的状态是"+th.getState());
break;
}
}
Thread th01 = new Thread(new ThreadState03(),"A");
Thread th02 = new Thread(new ThreadState03(),"B");
Thread th03 = new Thread(new ThreadState03(),"C");
th01.setPriority(Thread.MIN_PRIORITY);
th02.setPriority(Thread.MAX_PRIORITY);
th03.setPriority(Thread.NORM_PRIORITY);
th01.start();
th02.start();
th03.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
}
}
同步锁
synchronized
- 使用方法
- 1.使用在代码块上
- 2.使用在方法上
可以锁的内容:
**this(调用当前这个方法的对象,**this关键字必须在方法里使用,因为方法只有被调对象调用才会执行,只有在调用方法对象才能是已经生成了的.而static修饰的方法是类的,在类加载的时候就存在于静态方法区不需要对象,所以不能再static方法里面使用this,但是可以创建对象来调用该对象的成员方法,比如:new Object().method())
类名.class(当前这个类的class对象,锁上后该类的所有对象都不能使用)
资源(引用数据类型的成员属性(某个类的成员属性是一个另一个类))
wait()执行后会释放出对象 其他线程可以使用,并不是自己不用别人也不能使用
人和车过斑马线例子
package com.shsxt.threadstate;
public class Threadwait04 {
public static void main(String[] args) {
Street street = new Street();
new Thread(new Person(street)).start();
new Thread(new Car(street)).start();
}
}
class Street {
boolean flag = false;
//人走
public synchronized void ns() throws InterruptedException{
if(flag==false){
this.wait();
}else{
System.out.println("人走");
flag = false;
this.notify();
}
}
//车走
public synchronized void we() throws InterruptedException{//IllegalMonitorStateException
if(flag==true){
this.wait();
}else{
System.out.println("车走");
flag = true;
this.notify();
}
}
}
class Person implements Runnable{
Street street;
//要用带street的方法
@Override
public void run() {
while(true){
try {
street.ns();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Person() {
super();
}
public Person(Street street) {
super();
this.street = street;
}
}
class Car implements Runnable{
Street street;
public Car() {
super();
}
public Car(Street street) {
super();
this.street = street;
}
@Override
public void run() {
while(true){
try {
street.we();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}