java基础_day15

一 Map集合(会)

import java.util.*;
/**
* @Author 千锋⼤数据教学团队
* @Company 千锋好程序员⼤数据
* @Description Map API
*/
public class MapUsage {
 public static void main(String[] args) {
 // 1. 实例化⼀个Map集合的实现类对象,并向上转型为接⼝类型。
    Map<String, String> map = new HashMap<>();
 // 2. 向集合中插⼊数据
    String value = map.put("name", "xiaoming");
    System.out.println(value); // 由于第⼀次添加这个键值
对,集合中没有被覆盖的值,因此返回null
 String value2 = map.put("name", "xiaobai");
 System.out.println(value2); // 这⾥是第⼆次设置name的
值,会⽤xiaobai覆盖掉xiaoming,因此返回xiaoming
 // 3. 向集合中插⼊数据
 String value3 = map.putIfAbsent("name", "xiaohong");
 System.out.println(value3); // 这⾥返回的是集合中已经存
在的这个键对应的值
 String value4 = map.putIfAbsent("age", "20");
 System.out.println(value4); // 由于这个集合中原来是没有
age键存在的,所以返回的是null
 // 4. 将⼀个Map集合中所有的键值对添加到当前的集合中
 Map<String, String> tmp = new HashMap<>();
 tmp.put("height", "177");
 tmp.put("weight", "65");
 tmp.put("age", "30");
 map.putAll(tmp);
 // 5. 删除:通过键,删除⼀个键值对,并返回这个被删除的键值对中的
值。
 String value5 = map.remove("weight");
 System.out.println(value5);
 // 6. 删除
 boolean value6 = map.remove("age", "30");
 System.out.println(value6);
 // 7. 清空集合
 // map.clear();
 // 8. 修改集合中的某⼀个键值对(通过键,修改值)
 System.out.println(value7); // 返回被覆盖的值
 String value8 = map.replace("age", "30");
 System.out.println(value8); // 由于map中没有age键,因此这个返回null
     // 9. 修改: 只有当key和oldValue是匹配的情况下,才会将值修改成
newValue。
 boolean value9 = map.replace("name", "xiaohei",
"xiaohong");
 System.out.println(value9);
 // 10. 对集合中的元素进⾏批量的替换
 // 将集合中的每⼀个键值对,带⼊到BiFunction的⽅法中,使⽤接
⼝⽅法的返回值替换集合中原来的值。
 map.replaceAll((k, v) -> {
 if (k.equals("height")) {
 return v + "cm";
 }
 return v;
 });
 // 11. 通过键获取值。
 String value10 = map.get("name1");
 System.out.println(value10);
 // 12. 通过键获取值,如果这个键不存在,则返回默认的值。
 String value11 =                               map.getOrDefault("name1","aaa");
     System.out.println(value11);
 // 13. 判断是否包含某⼀个键
    boolean value12 = map.containsKey("height");
    System.out.println(value12);
    boolean value13 = map.containsValue("177");
    System.out.println(value13);
 // 14. 获取由所有的键组成的Set集合
    Set<String> keys = map.keySet();
 // 获取由所有的值组成的Collection集合
    Collection<String> values = map.values();
     System.out.println(map);
 }
}

1.1 Map集合的遍历(会)

.使⽤keySet进⾏遍历
1. 可以使⽤keySet()⽅法获取到集合中所有的键。
2. 遍历存储了所有的键的集合,依次通过键获取值。
​
/**
 * 1. 使⽤keySet进⾏遍历
 * @param map 需要遍历的集合
 */
private static void keyset(Map<String, String> map) {
 // 1. 获取存储了所有的键的集合
 Set<String> keys = map.keySet();
 // 2. 遍历这个Set集合
 for (String key : keys) {
 // 2.1. 通过键获取值
 String value = map.get(key);
 // 2.2. 展示⼀下键和值
 System.out.println("key = " + key + ", value = " +
value);
     }
 }
​
.使⽤forEach⽅法
这个forEach⽅法, 并不是Iterable接⼝中的⽅法。 是Map接⼝中定义的⼀个⽅法。
从功能上讲, 与Iterable中的⽅法差不多。 只是在参数部分有区别。
default void forEach(BiConsumer<? super K, ? super V> action)
    
    /**
 * 2. 使⽤forEach进⾏遍历
 * @param map 需要遍历的集合
 */
private static void forEach(Map<String, String> map) {
 map.forEach((k, v) -> {
 // k: 遍历到的每⼀个键
 // v: 遍历到的每⼀个值
 System.out.println("key = " + k + ", value = " + v);
    });
}
    
​
.使⽤EntrySet进⾏遍历
    Entry<K, V>:
是Map中的内部静态接⼝, ⼀个Entry对象我们称为⼀个实体,⽤来描述集合中的每⼀个键值对。
​
/**
 * 3. 使⽤entrySet进⾏遍历
 * @param map 需要遍历的集合
 */
private static void entrySet(Map<String, String> map) {
 // 1. 获取⼀个存储有所有的Entry的⼀个Set集合
 Set<Map.Entry<String, String>> entries = map.entrySet();
 // 2. 遍历Set集合
 for (Map.Entry<String, String> entry : entries) {
 // 2.1. 获取键
 String key = entry.getKey();
 // 2.2. 获取值
 String value = entry.getValue();
 // 2.3. 展示
 System.out.println("key = " + key + ", value = " +
value);
 
 //通过setValue可以去修改原始map的值
 //映射项(键-值对)。Map.entrySet ⽅法返回映射的 collection
视图,其中的元素属于此类。
//获得映射项引⽤的唯⼀ ⽅法是通过此 collection 视图的迭代器来实
现。这些 Map.Entry 对象仅
 //在迭代期间有效;更正式地说,如果在迭代器返回项之后修改了底层映
射,则
 //某些映射项的⾏为是不确定的,除了通过 setValue 在映射项上执⾏
操作之外。
 //entry.setValue("hello");
 }
}

二 线程

1 为什么要使⽤线程?

在程序中完成某⼀个功能的时候,我们会将他描述成任务,这个任务需要在线程中完成.

2 串⾏与并发

如果在程序中,有多个任务需要被处理,此时的处理⽅式可以有串⾏和并发:
串⾏(同步):所有的任务,按照⼀定的顺序,依次执⾏。如果前⾯的任务没有
执⾏结束,后⾯的任务等待。
并发(异步):将多个任务同时执⾏,在⼀个时间段内,同时处理多个任务。

3 进程和线程

进程, 是对⼀个程序在运⾏过程中, 占⽤的各种资源的描述。
线程, 是进程中的⼀个最⼩的执⾏单元。 其实, 在操作系统中, 最⼩的任务执⾏单元并不是线程, ⽽是句柄。 只不过句柄过⼩, 操作起来⾮常的麻烦, 因此线程就是我们可控的最⼩的任务执⾏单元。
​
进程和线程的异同:
相同点: 进程和线程都是为了处理多个任务并发⽽存在的。
不同点: 进程之间是资源不共享的, ⼀个进程中不能访问另外⼀个进程中的数据。 ⽽线程之间是资源共享的, 多个线程可以共享同⼀个数据。 也正因为线程之间是资源共享的, 所以会出现临界资源的问题。
​
进程和线程的关系:
⼀个进程, 在开辟的时候, 会⾃动的创建⼀个线程, 来处理这个进程中的任务。 这个线程被称为是主线程。 在程序运⾏的过程中, 还可以开辟其他线程, 这些被开辟
出来的其他线程, 都是⼦线程。也就是说, ⼀个进程中, 是可以包含多个线程。 ⼀个进程中的某⼀个线程崩溃了,
只要还有其他线程存在, 就不会影响整个进程的执⾏。 但是如果⼀个进程中, 所有的线程都执⾏结束了, 那么这个进程也就终⽌了。
​
​
总结
程序:⼀个可执⾏的⽂件
进程:⼀个正在运⾏的程序.也可以理解成在内存中开辟了⼀块⼉空间
线程:负责程序的运⾏,可以看做⼀条执⾏的通道或执⾏单元,所以我们通常将进程
的⼯作理解成线程的⼯作
进程中可不可以没有线程? 必须有线程,⾄少有⼀个.
当有⼀个线程的时候我们称为单线程(唯⼀的线程就是主线程).
当有⼀个以上的线程同时存在的时候我们称为多线程.
​

4 线程的⽣命周期(会)

4.1 线程的状态:

线程的⽣命周期, 指的是⼀个线程对象, 从最开始的创建, 到最后的销毁, 中间所经历的过程。 在这个过程中, 线程对象处于不同的状态。
New: 新⽣态, ⼀个线程对象刚被实例化完成的时候, 就处于这个状态。
Runnable: 就绪态, 处于这个状态的线程, 可以参与CPU时间⽚的争抢。
Run: 运⾏态, 某⼀个线程抢到了CPU时间⽚, 可以执⾏这个线程中的逻辑
Block: 阻塞态, 线程由于种种原因, 暂时挂起, 处于阻塞(暂停)状态。 这个状态的线程, 不参与CPU时间⽚的争抢。
Dead: 死亡态, 线程即将被销毁。

4.2 . 线程的⽣命周期图

 

5 理解多线程(会)

简单理解(cpu单核):从宏观上看,线程有并发执⾏,从微观上看,并没有,在线程完成任务时,实际⼯作的是cpu,我们将cpu⼯作描述为时间⽚(单次获取cpu的时间,⼀般在⼏⼗毫秒).cpu只有⼀个,本质上同⼀时间只能做⼀件事,因为cpu单次时间⽚很短,短到⾁眼⽆法区分,所以当cpu在多个线程之间快速切换时,宏观上给我们的感觉是多件事同时在执⾏.
​
注意:
1.cpu是随机的,线程之间本质上默认是抢cpu的状态,谁抢到了谁就获得了时间⽚,就⼯作,所以多个线程的⼯作也是默认随机的.
2.在使⽤多线程时,并不是线程数越多越好,本质上⼤家共同使⽤⼀个cpu,完成任务的时间并没有减少.要根据实际情况创建线程,多线程是为了实现同⼀时间完成多件事情的⽬的.⽐如我们⽤⼿机打开⼀个app时,需要滑动界⾯浏览,同时界⾯的图⽚需要下载,对于这两个功能最好同时进⾏,这时可以使⽤多线程.
​
示例代码:
public class Demo4 {
 //就是主线程的任务区
 public static void main(String[] args) {
    new Test();
 /*
 * ⼿动运⾏垃圾回收器
 */
 System.gc();
 System.out.println("main");
    }
}
class Test{
 @Override
 /*
 * 重写finalize⽅法
 */
 protected void finalize() throws Throwable {
 System.out.println("finalize");
 }
}

6 创建线程(会)

6.1原因分析

默认情况下,主线程和垃圾回收线程都是由系统创建的,但是我们需要完成⾃⼰的功能,所以需要创建⾃⼰的线程
java将线程⾯向对象了,形成的类就是Thread,在Thread类内部执⾏任务的⽅法叫run()

6.2 . 继承Thread类

继承⾃Thread类, 做⼀个Thread的⼦类。 在⼦类中, 重写⽗类中的run⽅法,
在这个重写的⽅法中, 指定这个线程需要处理的任务。
Thread.currentThread() : 可以⽤在任意的位置, 获取当前的线程。
如果是Thread的⼦类, 可以在⼦类中, 使⽤this获取到当前的线程。
当我们⼿动调⽤run的时候,他失去了任务区的功能,变成了⼀个普通的⽅法. 当run作为⼀个普通⽅法时,内部对应的线程跟调⽤他的位置保持⼀致.
​
结果分析:
主线程和两个⼦线程之间是随机打印的,他们是抢cpu的关系.
通过创建Thread⼦类的⽅式实现功能,线程与任务绑定在了⼀起,操作不⽅法我们可以将任务从线程中分离出来,哪个线程需要⼯作,就将任务交给谁,操作⽅便,灵活-使⽤Runnable接⼝
​
示例代码:
public class Demo5 {
 public static void main(String[] args) {//为了⽅便研究,先暂时不
考虑垃圾回收线程.
 MyThread t1 = new MyThread("bing");
 MyThread t2 = new MyThread("ying");
 t1.start();
 t2.start();
 
 //⼿动调⽤run()⽅法
 t1.run();
 for (int i = 0; i <10; i++) {
 
System.out.println(Thread.currentThread().getName()+" 
i:"+i);
 }
 }
}
class MyThread extends Thread{
 String name1;
 public MyThread(String name1) {
 this.name1 = name1;
 }
 //任务区
 //重写run⽅法,实现我们的功能.run就是我们的任务区
 /*
 * Thread.currentThread():获取的是当前的线程
 * Thread.currentThread().getName():线程的名字
 */
 @Override
 public void run() {
 for (int i = 0; i <10; i++) {
 System.out.println(this.name1+" 
"+Thread.currentThread().getName()+" i:"+i);
 }
 }
}

6.3 使用Runnable接口

在Thread类的构造⽅法中, 有⼀个重载的构造⽅法, 参数是 Runnable 接⼝。
因此, 可以通过Runnable接⼝的实现类对象进⾏Thread对象的实例化。
这⾥Thread内部默认有⼀个run,⼜通过runnable传⼊⼀个run,为什么优先调⽤的是传⼊的run?
如果该线程是使⽤独⽴的 Runnable 运⾏对象构造的,则调⽤该 Runnable 对象的 run ⽅法;否则,该⽅法不执⾏任何操作并返回。
​
示例代码:
public class Program {
 public static void main(String[] args) {
 // Runnable接⼝的匿名实现类
 Runnable runnable = new Runnable() {
 @Override
 public void run() {
 System.out,println("⼦线程处理的逻辑");
 }
 };
 // 实例化线程对象
 Thread thread = new Thread(runnable);
 }
}

6.4 优缺点对比

继承的⽅式: 优点在于可读性⽐较强, 缺点在于不够灵活。 如果要定制⼀个线
​
public class Demo6 {
 public static void main(String[] args) {
 //创建4个线程代表4个售票员
//线程与任务不分离
// SubThread s1 = new SubThread();
// SubThread s2 = new SubThread();
// SubThread s3 = new SubThread();
// SubThread s4 = new SubThread();
//
// //开启线程
// s1.start();
// s2.start();
// s3.start();
// s4.start();
 //线程与任务分离测试
 Ticket ticket = new Ticket();
 Thread t1 = new Thread(ticket);
 Thread t2 = new Thread(ticket);程, 就必须要继承⾃Thread类, 可能会影响原有的继承体系。
接⼝的⽅式: 优点在于灵活, 并且不会影响⼀个类的继承体系。 缺点在于可读性较差。
​
public class Demo6 {
 public static void main(String[] args) {
 //创建4个线程代表4个售票员
//线程与任务不分离
// SubThread s1 = new SubThread();
// SubThread s2 = new SubThread();
// SubThread s3 = new SubThread();
// SubThread s4 = new SubThread();
//
// //开启线程
// s1.start();
// s2.start();
// s3.start();
// s4.start();
 //线程与任务分离测试
 Ticket ticket = new Ticket();
 Thread t1 = new Thread(ticket);
 Thread t2 = new Thread(ticket);
 Thread t3 = new Thread(ticket);
 Thread t4 = new Thread(ticket);
 //开启线程
 t1.start();
 t2.start();
 t3.start();
 t4.start();
 }
}
//线程与任务不分离
class SubThread extends Thread{
 static int num = 20;//想让⼤家共⽤这个num
 @Override
 public void run() {
 for (int i = 0; i < 5; i++) {
 
System.out.println(Thread.currentThread().getName()+" num:"+
--num);
 }
 }
}
//线程与任务分离
class Ticket implements Runnable{
 int num = 20;//想让⼤家共⽤这个num
 @Override
 public void run() {
 for (int i = 0; i < 5; i++) {
 
System.out.println(Thread.currentThread().getName()+" num:"+
--num);
 }
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值