Java编程的利器:Pair和Triple无缝解决多值返回问题,助力编写高效代码

news2024/10/2 22:20:17

在实际编码中,经常会遇到一个方法需要返回多个值的情况,你编写一个方法,需要同时返回某个操作的结果和一些相关的附加信息。使用传统的方式,你可能需要创建一个包含这些信息的自定义类或者使用集合(如 Map)来存储这些值。然而,这往往使得代码变得臃肿,而且对于调用方来说,理解和提取这些值可能会显得有些繁琐。

这时使用org.apache.commons.lang3.tuple下的PairTriple 及其子类是一种非常便捷的解决方案。这些类提供了一种清晰、简单的方式来组织和传递多个相关联的值,使得代码更加直观和易于理解。

使用 PairTriple 就能轻松解决这个问题。你可以在一个方法中返回一个 PairTriple 对象,其中包含你想要传递的多个值。这样,你可以清晰地表示这些值之间的关系,而且调用方可以轻松地访问和使用这些值,而无需繁琐的解包过程。

在接下来的部分,我们将深入研究如何在这类场景中使用 PairTriple 及其子类,以及它们如何简化我们在编码中常遇到的多值返回问题。

引入依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

Pair 类介绍

Pair 类是org.apache.commons.lang3库提供的一个简单的键值对容器,用于表示两个相关联的值。其主要作用是将两个值组织在一起,提供一种便捷的方式进行传递和处理。

首先我们来看一下Pair的源码:

/**
 * 抽象类,表示简单的键值对。
 * 实现了 Map.Entry 接口,支持在各种集合中使用。
 * 实现了 Comparable 接口,用于比较两个 Pair 对象的大小。
 * 可序列化,支持对象的序列化和反序列化。
 *
 * @param <L> 左值的类型
 * @param <R> 右值的类型
 */
public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {

    private static final long serialVersionUID = 4954918890077093841L;

    // 空数组,用于表示空的 Pair 对象数组
    public static final Pair<?, ?>[] EMPTY_ARRAY = new PairAdapter[0];

    /**
     * 返回一个空的 Pair 数组。
     *
     * @return 空的 Pair 数组
     */
    public static <L, R> Pair<L, R>[] emptyArray() {
        return (Pair[]) EMPTY_ARRAY;
    }

    /**
     * 静态工厂方法,创建一个新的 ImmutablePair 对象,表示给定的左右值的键值对。
     *
     * @param left  左值
     * @param right 右值
     * @param <L>   左值的类型
     * @param <R>   右值的类型
     * @return ImmutablePair 对象
     */
    public static <L, R> Pair<L, R> of(L left, R right) {
        return ImmutablePair.of(left, right);
    }

    /**
     * 静态工厂方法,创建一个新的 ImmutablePair 对象,表示给定 Map.Entry 对象的键值对。
     *
     * @param pair Map.Entry 对象
     * @param <L>  左值的类型
     * @param <R>  右值的类型
     * @return ImmutablePair 对象
     */
    public static <L, R> Pair<L, R> of(Map.Entry<L, R> pair) {
        return ImmutablePair.of(pair);
    }

    /**
     * 返回左值,实现了 Map.Entry 接口。
     *
     * @return 左值
     */
    public final L getKey() {
        return this.getLeft();
    }

    /**
     * 抽象方法,由子类实现,用于获取左值。
     *
     * @return 左值
     */
    public abstract L getLeft();

    /**
     * 抽象方法,由子类实现,用于获取右值。
     *
     * @return 右值
     */
    public abstract R getRight();

    /**
     * 返回右值,实现了 Map.Entry 接口。
     *
     * @return 右值
     */
    public R getValue() {
        return this.getRight();
    }
}

Pair类是一个抽象类,它有两个子类ImmutablePairMutablePair。接下来我们介绍一下这两个子类,也是我们要使用的两个类。

MutablePair

MutablePair是一个可变的。它允许在创建后动态修改键和值,提供了更大的灵活性。但是它是线程不安全的。
我们可以根据它提供的几个静态方法或者它的构造器去构造一个MutablePair:

// 静态工厂方法,返回一个空的 MutablePair
public static <L, R> MutablePair<L, R>[] emptyArray();

// 静态工厂方法,传入给定的左右值的键值对,创建并返回一个MutablePair 对象
public static <L, R> MutablePair<L, R> of(L left, R right);

// 静态工厂方法,传入给定 Map.Entry 对象的键值对,创建并返回一个新的MutablePair 对象
public static <L, R> MutablePair<L, R> of(Map.Entry<L, R> pair);

// 无参构造器
public MutablePair()

// 指定左右值的键值对的构造器
public MutablePair(L left, R right)

我们可以根据它的几个方法修改键和值:

// 修改左值
public void setLeft(L left);

// 修改右值
public void setRight(R right);

// 修改新的右值,并返回之前的右值。
public R setValue(R value);

我们可以根据它的几个方法获取键和值:

// 获取左值
public L getLeft();

// Pair中的方法  获取左值
public final L getKey();

// 获取右值
public R getRight();

// Pair中的方法  获取右值 
public R getValue();

当然我们看到它的left以及right都是public的。所以我们也可以直接取值,不用使用它的get方法。

ImmutablePair

ImmutablePairPair的一个不可变的子类。它在创建完成之后,不允许改变键和值。它是线程安全的。

我们可以看一下它如何进行构造的:

// 静态工厂方法,返回一个空的 ImmutablePair 数组。
public static <L, R> ImmutablePair<L, R>[] emptyArray();

// 静态工厂方法,返回一个包含 null 左值和 null 右值的 ImmutablePair 对象,表示空值。
public static <L, R> ImmutablePair<L, R> nullPair();

// 静态工厂方法,返回一个包含指定左值和 null 右值的 ImmutablePair 对象。
public static <L, R> Pair<L, R> left(L left);

// 静态工厂方法,返回一个包含 null 左值和指定右值的 ImmutablePair 对象。
public static <L, R> Pair<L, R> right(R right);

// 静态工厂方法,创建并返回一个新的 ImmutablePair 对象,表示给定的左右值的键值对。
public static <L, R> ImmutablePair<L, R> of(L left, R right);

// 静态工厂方法,创建并返回一个新的 ImmutablePair 对象,表示给定 Map.Entry 对象的键值对。
public static <L, R> ImmutablePair<L, R> of(Map.Entry<L, R> pair);

// 有参构造器 传入给定的左右值的键值对。
public ImmutablePair(L left, R right);

我们可以根据它的几个方法获取键和值:

// 获取左值
public L getLeft();

// Pair中的方法  获取左值
public final L getKey();

// 获取右值
public R getRight();

// Pair中的方法  获取右值 
public R getValue();

当然我们看到它的left以及right都是public的。所以我们也可以直接取值,不用使用它的get方法。

那我们再看一下为什么ImmutablePair是不可变的,并且是线程安全的。
首先我们看一下它的左值以及右值都是final的,不可更改的。并且调用它的setValue会抛出UnsupportedOperationException

public final L left;  
public final R right;

public R setValue(R value) {  
    throw new UnsupportedOperationException();  
}

类中的 leftright 成员变量被声明为 final,这意味着它们在对象创建后不能被修改,确保了线程安全性。ImmutablePair 被设计为不可变的键值对类,即一旦创建,其内容不可更改。这确保了在多线程环境中,不会有并发修改的问题。

使用示例
/**  
 * 返回MutablePair  
 * @param userDO  
 * @return  
 */  
private static MutablePair<String, Integer> handleUserInfo1(UserDO userDO){  
    return MutablePair.of(userDO.getUserId(), userDO.getAge());  
}  
  
/**  
 * 返回ImmutablePair  
 * @param userDO  
 * @return  
 */  
private static ImmutablePair<String, Integer> handleUserInfo2(UserDO userDO){  
    return ImmutablePair.of(userDO.getUserId(), userDO.getAge());  
}  
  
public static void main(String[] args) {  
    UserDO userDO = new UserDO();  
    userDO.setUserId("coderacademy");  
    userDO.setAge(35);  
  
    MutablePair<String, Integer> mutablePair = handleUserInfo1(userDO);  
    System.out.println(mutablePair.getLeft()+" MutablePair修改前:"+ mutablePair.right);  
    mutablePair.setRight(40);  
    System.out.println(mutablePair.getLeft()+" MutablePair修改后:"+ mutablePair.right);  
  
    ImmutablePair<String, Integer> immutablePair = handleUserInfo2(userDO);  
    System.out.println(mutablePair.getLeft()+" ImmutablePair修改前:"+ mutablePair.right);  
    immutablePair.setValue(50);  
    System.out.println(mutablePair.getLeft()+" ImmutablePair修改后:"+ mutablePair.right);  
}

执行结果,我们发现ImmutablePair在修改value时报错:
image.png

Pair 类及其子类 ImmutablePairMutablePair 是用于表示键值对的实用工具类。ImmutablePair 是不可变的、线程安全的,适用于安全共享;MutablePair 允许动态修改值,但不具备线程安全性,适用于单线程环境。它们在方法返回多个值时提供了简便的解决方案,提高了代码的灵活性。

Triple介绍

Triple 是一个用于表示三元组的抽象类。三元组是由三个元素组成的有序集合,其中每个元素都有特定的位置,分别称为左值(Left)、中间值(Middle)和右值(Right)。Triple 类提供了一种便捷的方式来组织和处理这种具有固定顺序的数据。可以在不创建专门类的情况下轻松返回三个值。通过 Triple,开发者可以更方便地处理包含三个元素的数据,减少了创建和维护多个变量的复杂性,使代码更加简洁。

我们来看一下Triple的源码:

/**
 * 表示包含三个元素的三元组的抽象类 Triple。
 *
 * 该类是一个抽象实现,定义了基本的 API,将元素分别称为 'left'、'middle' 和 'right'。
 *
 * 子类的实现可以是可变的或不可变的。对存储的对象类型没有限制。
 * Triple 对象的可变性取决于其中存储的对象是否是可变的。如果存储的是可变对象,那么 Triple 本身也就变得可变,因为存储的对象状态可以被修改。
 * 如果存储的是不可变对象,那么Triple 对象在创建后就保持不可变。
 *
 */
public abstract class Triple<L, M, R> implements Comparable<Triple<L, M, R>>, Serializable {

    /**
     * 一个空数组。
     */
    public static final Triple<?, ?, ?>[] EMPTY_ARRAY = new TripleAdapter[0];

    /**
     * 返回可分配而无需编译器警告的空数组单例。
     *
     */
    @SuppressWarnings("unchecked")
    public static <L, M, R> Triple<L, M, R>[] emptyArray() {
        return (Triple<L, M, R>[]) EMPTY_ARRAY;
    }

    /**
     * 获取由三个对象组成的不可变三元组,推断出泛型类型。
     *
     * 此工厂方法允许使用推断类型来创建三元组以获取泛型类型。
     *
     * @param left   左元素,可以为 null
     * @param middle 中间元素,可以为 null
     * @param right  右元素,可以为 null
     * @return 由三个参数形成的三元组,非 null
     */
    public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) {
        return new ImmutableTriple<>(left, middle, right);
    }

    /**
     * 获取此三元组的左元素。
     *
     * @return 左元素,可以为 null
     */
    public abstract L getLeft();

    /**
     * 获取此三元组的中间元素。
     *
     * @return 中间元素,可以为 null
     */
    public abstract M getMiddle();

    /**
     * 获取此三元组的右元素。
     *
     * @return 右元素,可以为 null
     */
    public abstract R getRight();
}

Triple是一个抽象类,它有两个子类:可变MutableTriple 以及不可变 ImmutableTriple

MutableTriple

MutableTriple 是可变的,原因在于它提供了公共的设置(set)方法,允许在创建后修改其内部值。具体来说,MutableTriple 提供了 setLeftsetMiddlesetRight 方法,使得在对象创建后可以修改左、中、右元素的值。

/**
 * 表示由三个 {@code Object} 元素组成的可变三元组。
 *
 * 非线程安全
 *
 */
public class MutableTriple<L, M, R> extends Triple<L, M, R> {


    /**
     * 通过推断泛型类型获取三个对象的可变三元组。
     *
     * 该工厂允许通过推断泛型类型创建三元组。
     *
     */
    public static <L, M, R> MutableTriple<L, M, R> of(final L left, final M middle, final R right) {
        return new MutableTriple<>(left, middle, right);
    }

    /** 左对象 */
    public L left;
    /** 中间对象 */
    public M middle;
    /** 右对象 */
    public R right;

    /**
     * 创建一个新的三元组实例,包含三个 null 值。
     */
    public MutableTriple() {
    }

    /**
     * 创建一个新的三元组实例。
     *
     * @param left   左值,可以为 null
     * @param middle 中间值,可以为 null
     * @param right  右值,可以为 null
     */
    public MutableTriple(final L left, final M middle, final R right) {
        this.left = left;
        this.middle = middle;
        this.right = right;
    }

    /**
     * 设置三元组的左元素。
     */
    public void setLeft(final L left) {
        this.left = left;
    }

    /**
     * 设置三元组的中间元素。
     */
    public void setMiddle(final M middle) {
        this.middle = middle;
    }

    /**
     * 设置三元组的右元素。
     */
    public void setRight(final R right) {
        this.right = right;
    }
}

MutableTriple 被明确标记为非线程安全。

ImmutableTriple

ImmutableTriple 是一个不可变的三元组类,由三个泛型元素(left、middle、right)组成。不可变意味着一旦创建,其状态无法修改。该类被设计为线程安全的,但需要注意,如果存储在三元组中的对象是可变的,那么三元组本身实际上就不再是不可变的。

/**
 * 一个由三个元素组成的不可变三元组。
 *
 * ImmutableTriple 是一个最终类,被设计成不可变的,即在实例化后其状态不可更改。
 * 如果存储在三元组中的三个对象都是线程安全的,则该类是线程安全的。类的最终性防止了子类化,确保不会添加不希望的行为。
 *
 * 线程安全的 如果三个对象都是线程安全的
 *
 */
public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {

    /**
     * 返回可以在不触发编译器警告的情况下分配的空数组单例。
     * @return 可以在不触发编译器警告的情况下分配的空数组单例。
     */
    @SuppressWarnings("unchecked")
    public static <L, M, R> ImmutableTriple<L, M, R>[] emptyArray() {
        return (ImmutableTriple<L, M, R>[]) EMPTY_ARRAY;
    }

    /**
     * 返回一个由 null 组成的不可变三元组。
     *
     * @return 一个由 null 组成的不可变三元组。
     */
    public static <L, M, R> ImmutableTriple<L, M, R> nullTriple() {
        return NULL;
    }

    /**
     * 通过推断泛型类型获得由三个对象组成的不可变三元组。
     *
     * 此工厂允许使用推断创建三元组以获得泛型类型。
     *
     * @return 由三个参数形成的不可变三元组,不为 null
     */
    public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
        return new ImmutableTriple<>(left, middle, right);
    }

    /** 左对象 */
    public final L left;
    /** 中间对象 */
    public final M middle;
    /** 右对象 */
    public final R right;

    /**
     * 构造方法 创建一个新的三元组实例。
     *
     */
    public ImmutableTriple(final L left, final M middle, final R right) {
        this.left = left;
        this.middle = middle;
        this.right = right;
    }
}

ImmutableTriple 被声明为 final,表示不可继承,确保不可变性。确保不会有子类添加或修改行为。然后类中的属性 leftmiddleright 被声明为 final,表示它们在实例化后无法被修改。类中没有提供修改元素的公共方法。ImmutableTriple 主张不可变性,不提供修改实例状态的方法。当然如果存储在三元组中的对象是可变的,则整个三元组就变得可变。这是因为虽然 ImmutableTriple 本身是不可变的,但如果存储的对象是可变的,它们的状态可能会发生变化。

类声明中使用 #ThreadSafe# 标记,表示在存储的三个对象都是线程安全的情况下,ImmutableTriple 是线程安全的。

示例
/**  
* 返回可变Truple  
* @param userDO  
* @return  
*/  
private static MutableTriple<String, Integer, UserDO> handleUserInfo1(UserDO userDO){  
	return MutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);  
}  
  
/**  
* 返回不可变Triple  
* @param userDO  
* @return  
*/  
private static ImmutableTriple<String, Integer, UserDO> handleUserInfo2(UserDO userDO){  
	return ImmutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);  
}  
  
  
public static void main(String[] args) {  
	UserDO userDO = new UserDO();  
	userDO.setUserId("coderacademy");  
	userDO.setUserName("码农Academy");  
	userDO.setSex(1);  
	  
	MutableTriple<String, Integer, UserDO> mutableTriple = handleUserInfo1(userDO);  
	System.out.println("mutableTriple改变前的值:" + mutableTriple);  
	mutableTriple.setMiddle(2);  
	System.out.println("mutableTriple改变后的值:" + mutableTriple);  
	  
	ImmutableTriple<String, Integer, UserDO> immutableTriple = handleUserInfo2(userDO);  
	System.out.println("ImmutableTriple改变前的值:" + immutableTriple);  
	UserDO userFromTriple = immutableTriple.right;  
	userFromTriple.setSex(2);  
	System.out.println("ImmutableTriple改Right键值对象的值:" + immutableTriple);  
	// 因ImmutableTriple 不可变,无法通过set方法修改键值。
}

总结

使用 Pair 和 Triple 类可以简化代码、提高可读性,使关联数据更清晰,保持类型安全,增强代码清晰度,提高扩展性,并提供丰富的功能,从而使开发人员更高效地处理相关数据,编写更简洁可读的代码,提升代码质量和开发效率。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、SpringBoot、SpringCloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1412752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统引导程序 Boot Loader——学习笔记

基于嵌入式Linux 的完整系统软件由三个部分组成&#xff1a;系统引导程序、Linux 操作系统内核和文件系统。 系统引导程序 Boot Loader 是系统加电后运行的第一段软件代码&#xff0c;它的作用是加载操作系统或者其他程序到内存中&#xff0c;并将控制权交给它们。 Boot Load…

代码随想录算法训练营第16天 | 104.二叉树的最大深度, 111.二叉树的最小深度 ,222.完全二叉树的节点个数

二叉树理论基础&#xff1a; https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE 104.二叉树的最大深度 题目链接&#xff1a;https://leetcode.cn/problems/maximum-depth-…

Mediasoup Demo-v3笔记(三)——Mediasoup库介绍

Mediasoup基本概念 Worker &#xff1a; 每一个worker就是一个进程&#xff08;节点&#xff09;&#xff0c;进程和进程之间可以通信Router&#xff1a;每一个Router就是一个房间的概念Producer&#xff1a;每一个发出声音和视频的流都是一个ProducerConsumer&#xff1a;每一…

Confluence 的文章导入到 YouTrack KB 中

YouTrack 是有一个 KB 的&#xff0c;我们可以吧 Confluence 的文章全部导入到 YouTrack 的 KB 中。 首先&#xff0c;你需要具有管理员权限&#xff0c;然后选择导入。 然后可以在打开的界面中新增一个导入。 在新增导入中输入 Confluence 在随后的界面中输入你 Confluence …

浅谈ICMP协议

ICMP(Internet Control Message Protocol) 网络控制消息协议是网络层的协议&#xff0c;所谓控制&#xff0c;指的是通过下发指令来判断是否当前主机可达目标主机及不可达时的错误报告。通常使用ping命令和traceroute命令来使用。 ping命令:检测到目标主机是否可达、 tracerout…

SQL语句创建一个简单的银行数据库

目录 一、银行业务E-R图 二、数据库模型图 转换关系模型后&#xff1a; 三、创建数据库 3.1 创建银行业务数据库 四、创建表 4.1 创建客户信息表 4.2 创建银行卡信息表 4.3 创建交易信息表 4.4 创建存款类型表 结果如下&#xff1a; ​编辑 五、插入适量数据 5.1…

Linux中LVM实验

LVM实验&#xff1a; 1、分区 -L是大小的意思-n名称的意思 从vg0&#xff08;卷组&#xff09;分出来 2、格式化LV逻辑卷 LVM扩容 如果icdir空间不够了&#xff0c; 扩展空间lvextend -L 5G /dev/vg0/lv1 /dev/vg0/lv1(pp,vg,lv) 刷新文件系统xfs_growfs /lvdir VG扩容 …

选现货白银投资划不划算?

可以肯定的是选择现货白银投资是划算的&#xff0c;但投资者需要有足够的知识和经验&#xff0c;以及对市场的敏锐观察力。只有这样&#xff0c;投资者才能在现货白银投资中获取收益。在投资市场上&#xff0c;白银作为一种特殊的投资品种&#xff0c;一直以来都备受投资者们的…

01:云计算底层技术奥秘|虚拟化管理|公有云概述

云计算底层技术奥秘&#xff5c;虚拟化管理&#xff5c;公有云概述 虚拟化平台安装验证虚拟化支持 Linux虚拟机创建虚拟机磁盘虚拟机配置文件创建虚拟机 公有云简介 虚拟化平台安装 虚拟化&#xff1a;是一种技术 就是将不可拆分的实体资源变成可以自由划分的逻辑资源&#xf…

搭建Vite和Vue环境

​ 第一步&#xff1a;创建一个文件夹&#xff08;此处为新建文件夹&#xff09;&#xff0c;并通过vscode打开 第二步&#xff1a;鼠标右键新建终端&#xff0c;并在终端处输入代码npm create vuelatest ​第三步&#xff1a;输入该项目名称&#xff08;该项目名称并不是第一…

面向对象编程(进阶)(上)

文章目录 一. 关键字&#xff1a;this1.1 this是什么&#xff1f;1.2 什么时候使用this1.2.1 实例方法或构造器中使用当前对象的成员1.2.2 同一个类中构造器互相调用 1.3 练习 二. 面向对象特征二&#xff1a;继承(Inheritance)2.1 继承的概述2.1.1 生活中的继承2.1.2 Java中的…

乖乖,咱不用BeanUtil.copy了,咱试试这款神级工具(超详细)

引言 在现代Java应用程序开发中&#xff0c;处理对象之间的映射是一个常见而且必不可少的任务。随着项目规模的增长&#xff0c;手动编写繁琐的映射代码不仅耗时且容易出错&#xff0c;因此开发者们一直在寻找更高效的解决方案。比如基于Dozer封装的或者Spring自带的BeanUtil.…

C语言指针进阶之一字符指针

目录 1.指针知识回顾 2.字符指针 2.1字符指针的一般使用 2.2字符指针的另外一种使用 1.指针知识回顾 ①.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识了一片空间。 内存会划分成一个个的内存单元&#xff0c;每个内存单元都有一个独立的编号&#xff0…

php怎么输入一个变量,http常用的两种请求方式getpost(ctf基础)

php是网页脚本语言&#xff0c;网页一般支持两种提交变量的方式&#xff0c;即get和post get方式传参 直接在网页URL的后面写上【?a1027】&#xff0c;如果有多个参数则用&符号连接&#xff0c; 如【?a10&b27】 post方式传参 需要借助插件&#xff0c;ctfer必备插…

腾讯云服务器一键部署幻兽帕鲁联机服务器详细教程(Linux系统)

腾讯云作为国内领先的云计算服务商&#xff0c;为广大用户提供了稳定、高效的云计算服务。本文将详细介绍如何在腾讯云服务器&#xff08;Linux系统&#xff09;实现一键部署幻兽帕鲁联机服务器&#xff0c;帮助大家快速搭建自己的游戏联机服务器。 第一步&#xff1a;购买服务…

C#winform上位机开发学习笔记13-串口助手显示系统时间功能添加

1.功能描述 在上位机中显示系统的实时时间 2.代码部分 步骤1&#xff1a;添加文本框控件并设置参数 #此处注意将BackColor颜色修改为非Control&#xff0c;即可正常显示ForeColor颜色&#xff0c;否则该颜色不变&#xff0c;原因暂且不明。 步骤2&#xff1a;添加timer控件…

Docker深入解析:从基础到实践

Docker基础知识 Docker是什么&#xff1a;定义和核心概念解释 Docker是一个开源项目&#xff0c;它诞生于2013年&#xff0c;旨在自动化应用程序的部署过程&#xff0c; 让应用程序能够在轻量级的、可移植的、自给自足的容器中运行。这些容器可以在几乎任何机器上运行&#xf…

linux安装python3.11

yum -y install gcc-c zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite* readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel下载地址 https://www.python.org/ftp/python/3.11.7/Python-3.11.7.tar.xz 上传python文件&#xff…

互信息的简单理解

在介绍互信息之前&#xff0c;首先需要了解一下信息熵的概念&#xff1a;所谓信息熵&#xff0c;是指信息论中对一个随机变量不确定性的度量&#xff0c;对于随机变量x&#xff0c;信息熵的定义为&#xff1a; H ( x ) − ∑ x p ( x ) l o g p ( x ) H(x)-\sum_xp(x)logp(x) …

Programming Abstractions in C阅读笔记:p254-p257

《Programming Abstractions in C》学习第70天&#xff0c;p254-p257总结&#xff0c;总计4页。 一、技术总结 1.minimax strategy(极小化极大算法) p255, This idea–finding the position that leaves your opponent with the worst possible best move–is called the mi…