JAVASE15
一、TreeMap
TreeSet底层就是由TreeMap维护的
底层结构: 红黑树
特点: 有序(默认升序排序),存放存储与内部真实存储顺序是不一致的
注意: 以后什么功能|结构能够实现排序的,想到比较器:指定比较规则
去重排序: 根据key实现去重与排序
key中存放的数据的类型->1)实现内部比较器 2)实现外部比较器
我们先写一User类
public class User implements Comparable<User>{
private String username;
private int password;
public User() {
}
public User(String username, int password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getPassword() {
return password;
}
public void setPassword(int password) {
this.password = password;
}
@Override
public String toString() {
return "Person{" +
"username='" + username + '\'' +
", password=" + password +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return password == user.password &&
Objects.equals(username, user.username);
}
@Override
public int hashCode() {
return Objects.hash(username, password);
}
//内部比较方式
@Override
public int compareTo(User o) {
return this.getUsername().compareTo(o.getUsername());
}
}
然后开始存储
public class TreeMapDemo01 {
public static void main(String[] args) {
//默认根据User类的内部比较器提供的比较方式进行比较排序
//TreeMap<User,String> map = new TreeMap<>();
//指定使用某种外部比较规则
TreeMap<User,String> map = new TreeMap<>((o1, o2) -> o1.getPassword()-o2.getPassword());
//map.put(null,null); //treemap中存储键值对key不能为null
//存储数据,在存储的时候才会比较,排序
map.put(new User("阿浪",111),"新浪");
map.put(new User("阿汤",122),"腾讯");
map.put(new User("阿哥",133),"斗鱼");
map.put(new User("阿笙",144),"拼多多");
map.put(new User("阿烨",144),"点淘app");
System.out.println(map);
}
}
二、Properties
Properties为Hashtable的子类,要求键与值只能为字符串 ,不能为null,常与配置文件(与外界交互的信息) 即内存与存储介质(文件、数据库、网络、服务器内存等)交互
properties根式作为配置文件的格式存在,键值对 都是字符串,简单
程序从properties文件中读取数据:
void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
.properties文件
userName=庄周
passWord=12345678
public class PropertiesDemo02 {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
//通过properties对象从配置文件中读取数据
//指定从哪一个文件,通过哪个流加载
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
System.out.println(pro.getProperty("userName"));
System.out.println(pro.getProperty("passWord"));
}
}
注意:这里如果我们的userName输入的是中文,我们需要设置好编码格式不然在java中输出的就是类似于乱码的存在。
三、Collections
Collections 与 Collection 之间的区别:
Collection 集合体系的上层接口
Collections 操作集合数据的工具类 , 与 Array很像
查找的方法查找特定对象
使用前提: 升序排序
HashMap线程不安全问题:
1.Hashtable类型,线程安全的HashMap
Hashtable是同步的。 如果不需要线程安全实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替Hashtable 。
2.Collections -> static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
3.juc包(高级并发编程包) - > ConcurrentHashMap 线程安全的哈希表—>推荐,安全性好效率高
public class CollectionsDemo01 {
public static void main(String[] args) {
//sort
//List<Integer> list = List.of(5,1,2,6,4,3);
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(5);
list.add(1);
list.add(4);
list.add(2);
System.out.println(list);
//升序
Collections.sort(list);
System.out.println(list);
List<User> list2 = new ArrayList<>();
list2.add(new User("", 19));
list2.add(new User("", 18));
list2.add(new User("", 20));
Collections.sort(list2, (o1, o2) -> o2.getPassword() - o1.getPassword());
System.out.println(list2);
// void shuffle(List) //对List容器内的元素进行随机排列
// Collections.shuffle(list);
System.out.println(list);
//reverse
//Collections.reverse(list);
System.out.println(list);
//void fill(List, Object)
//Collections.fill(list,111);
System.out.println(list);
System.out.println(Collections.binarySearch(list, 6));//-插入点-1
}
}
四、Thread
线程(英语:thread)是操作系统**能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程优点: 资源利用率更好;程序设计在某些情况下更简单;程序响应更快
进程与线程的区别
区别 | 进程 | 线程 |
---|---|---|
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的的开销 | 线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程由独立运行栈和程序计数器(PC),线程切换的开销小 |
所处环境 | 在操作系统中能同时运行多个任务(程序) | 在同一应用程序中有多个顺序流通时执行 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 线程间共享进程的所有资源,每个线程只有自己的堆栈和局部变量,线程有CPU独立调度执行,在多CPU环境下就允许多个线程同时运行 |
包含关系 | 没有线程的进程可以看做单线程,如果一个进程拥有多个线程,则执行过程不是一条线的,而是多余线(线程)共同完成的 | 线程是进程的一部分,所以线程有的时候会被称为是轻量级进程或轻权进程 |
创建线程+开启线程:
1.继承Thread,重写run() + start()
2.实现Runnable,重写run()方法,方法的内部定义线程体 + start()
接口: 可以多实现,类只能单继承
可以资源共享
第一种方法
public class ThreadDemo01 extends Thread{
public static void main(String[] args) {
//创建线程
ThreadDemo01 th = new ThreadDemo01();
//开启线程
th.start();
//th.run(); 方法的调用
//主线程
for(int i=1;i<=20;i++){
System.out.println("一边讲课");
/*休息10ms*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
定义线程体代码
*/
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边喝水");
/*休息10ms*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
第二种方法
实现Runnable接口,重写run()方法 + start() 开启线程
接口可以多实现
可以实现资源共享
public class ThreadDemo02 implements Runnable{
public static void main(String[] args) {
//线程的创建
ThreadDemo02 th = new ThreadDemo02();
Thread thread = new Thread(th);
//开启
thread.start();
new Thread(new ThreadDemo02()).start();
for(int i=0;i<=50;i++){
System.out.println("一边说话...");
}
}
/*
线程体
*/
@Override
public void run() {
for(int i=0;i<=50;i++){
System.out.println("一边吃饭...");
}
}
}
这里我们可以使用内部类来简化代码
public class ThreadDemo03 {
//内部类
static class Inner1 implements Runnable{
@Override
public void run() {
for(int i=0;i<=50;i++){
System.out.println("一边打游戏...");
}
}
}
static class Inner2 extends Thread{
@Override
public void run() {
for(int i=0;i<=50;i++){
System.out.println("一边打上厕所...");
}
}
}
public static void main(String[] args) {
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<=50;i++){
System.out.println("一边陪女朋友...");
}
}
}).start();
//lambda
new Thread(()->{
for(int i=0;i<=50;i++){
System.out.println("一边陪直播...");
}
}).start();
//线程开启
new Thread(new Inner1()).start();
new Inner2().start();
}
}
这里我们写一个经典的例子
/*
龟兔赛跑
参赛者跑满一百步结束
乌龟 : 一直跑不休息
兔子 : 每20步休息2ms
如果一旦有人赢了游戏就结束
*/
public class ThreadDemo04 {
public static void main(String[] args) {
Match match = new Match();
new Thread(match,"乌龟").start();
new Thread(match,"兔子").start();
}
}
//比赛
class Match implements Runnable{
//结束标识
private String winner; //记录赢的人的名字
@Override
public void run() {
//i作为步数
for(int i=1;i<=100;i++){
//是否为兔子
System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步");
if("兔子".equals(Thread.currentThread().getName()) && i%20==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//结束判断
if(checkOver(i)){
break;
}
}
}
//结束的条件判定 返回值: true结束 false不结束
public boolean checkOver(int steps){
boolean flag = false;
//有人赢了
if(winner != null){
return true;
}
//自己赢了
if(steps==100){
winner = Thread.currentThread().getName();
return true;
}
return false;
}
}
第三种开启线程的方法(了解即可)
Callable 接口 call() ,定义线程体
优点:
可以抛出异常,可以定义返回值
public class ThreadDemo05 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Match2 match = new Match2();
//通过线程池 固定大小线程池2个 static ExecutorService newFixedThreadPool(int nThreads)
//1.创建执行服务
ExecutorService server = Executors.newFixedThreadPool(2);
//2.提交任务 <T> Future<T> submit(Callable<T> task) 提交值返回任务以执行并返回表示任务的挂起结果的Future。
Future<Integer> f1 = server.submit(match);
Future<Integer> f2 = server.submit(match);
//3.获取结果
//Integer result1 = f1.get();
//Integer result2 = f2.get();
//System.out.println(result1);
//System.out.println(result2);
//4.结束服务
server.shutdown();
}
}
//比赛
class Match2 implements Callable<Integer> {
//结束标识
private String winner; //记录赢的人的名字
/*
可以抛出异常
*/
@Override
public Integer call() throws InterruptedException {
//i作为步数
for(int i=1;i<=100;i++){
//是否为兔子
System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步");
if("兔子".equals(Thread.currentThread().getName()) && i%20==0){
Thread.sleep(20);
}
//结束判断
if(checkOver(i)){
return i;
}
}
return null;
}
//结束的条件判定 返回值: true结束 false不结束
public boolean checkOver(int steps){
boolean flag = false;
//有人赢了
if(winner != null){
return true;
}
//自己赢了
if(steps==100){
winner = Thread.currentThread().getName();
return true;
}
return false;
}
}