2022黑马Redis跟学笔记.实战篇(七)

news2024/9/23 23:23:49

2022黑马Redis跟学笔记.实战篇 七

    • 4.11.附近的店铺功能
      • 4.11.1. GEO数据结构的基本用法
        • 1. 附近商户-导入店铺数据到GEO
      • 4.11.2. 获取附近的店铺
        • 1. 附近商户-实现附近商户功能
    • 4.9. 签到功能
      • 4.9.1.BitMap原理
        • 1. 用户签到-BitMap功能演示
      • 4.9.2.实现签到功能
      • 4.9.3.实现补签功能
      • 4.9.4.统计连续签到天数
        • 1. 用户签到-签到统计
        • 2. 额外加餐-关于使用bitmap来解决缓存穿透的方案
    • 4.10.UV统计
      • 4.10.1.UV统计的基本思路
      • 4.10.2.HypeLogLog实现统计

在这里插入图片描述
这里视频中先讲了4.11

4.11.附近的店铺功能

4.11.1. GEO数据结构的基本用法

GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

  • GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
  • GEODIST:计算指定的两个点之间的距离并返回
  • GEOHASH:将指定member的坐标转为hash字符串形式并返回
  • GEOPOS:返回指定member的坐标
  • GEORADIUS:指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.以后已废弃
  • GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2.新功能
  • GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key。 6.2.新功能。

在这里插入图片描述

GEOADD g1 116.378248 39.865275 beijingnan
GEOADD g1 116.42803 39.903738 beijingzhan 116.322287 39.893729 beijingxizhan

在这里插入图片描述
看一下图形界面,底层是sortedset
在这里插入图片描述
计算距离,以km为单位

GEODIST g1 beijingnan beijingxizhan km

在这里插入图片描述

GEODIST g1 beijingxizhan beijingzhan km

在这里插入图片描述
天安门附近火车站

GEOSEARCH g1 FROMLONLAT 116.397904 39.909005 BYRADIUS 10 km WITHDIST

在这里插入图片描述

再看一下其它指令

  • GEOPOS 返回指定memeber的坐标
GEOPOS g1 beijingzhan

在这里插入图片描述

  • GEOHASH 将指定member的坐标转为hash字符串形式并返回
GEOHASH g1 beijingzhan

在这里插入图片描述

1. 附近商户-导入店铺数据到GEO

具体场景说明:
在这里插入图片描述
当我们点击美食之后,会出现一系列的商家,商家中可以按照多种排序方式,我们此时关注的是距离,这个地方就需要使用到我们的GEO,向后台传入当前app收集的地址(我们此处是写死的) ,以当前坐标作为圆心,同时绑定相同的店家类型type,以及分页信息,把这几个条件传入后台,后台查询出对应的数据再返回。

在这里插入图片描述

我们要做的事情是:将数据库表中的数据导入到redis中去,redis中的GEO,GEO在redis中就一个menber和一个经纬度,我们把x和y轴传入到redis做的经纬度位置去,但我们不能把所有的数据都放入到menber中去,毕竟作为redis是一个内存级数据库,如果存海量数据,redis还是力不从心,所以我们在这个地方存储他的id即可。

但是这个时候还有一个问题,就是在redis中并没有存储type,所以我们无法根据type来对数据进行筛选,所以我们可以按照商户类型做分组,类型相同的商户作为同一组,以typeId为key存入同一个GEO集合中即可

代码

HmDianPingApplicationTests

 @Test
    void loadShopData() {
        // 1.查询店铺信息
        List<Shop> list = shopService.list();
        // 2.把店铺按照typeId分组,typeId一样的分一组
        Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));

        // 3.分批完成写入Redis
        Set<Map.Entry<Long, List<Shop>>> entries = map.entrySet();
        for (Map.Entry<Long, List<Shop>> entry : entries) {
            // 3.1获取typeId
            Long typeId = entry.getKey();

            // 3.2获取同类型的店铺的集合
            List<Shop> value = entry.getValue();

            String key = RedisConstants.SHOP_GEO_KEY + typeId;
            // 3.3 写入Redis

            // 方法一:打开shop实体类集合,一条店铺一条店铺添加(比较慢)
            /*for (Shop shop : value) {
                stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getY()), shop.getId().toString());
            }*/

            // 方法二:locations
            List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());
            for (Shop shop : value) {
                locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),
                        new Point(shop.getX(), shop.getY())));
            }
            stringRedisTemplate.opsForGeo().add(key, locations);
        }
    }

运行单元测试
在这里插入图片描述

4.11.2. 获取附近的店铺

1. 附近商户-实现附近商户功能

先安装插件Maven Helper,管理依赖
在这里插入图片描述
管理Maven依赖
在这里插入图片描述
移除老版本
在这里插入图片描述

SpringDataRedis的2.3.9版本并不支持Redis 6.2提供的GEOSEARCH命令,因此我们需要提示其版本,修改自己的POM

第一步:导入pom.xml

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.1.6.RELEASE</version>
</dependency>

第二步:

修改ShopController

@GetMapping("/of/type")
public Result queryShopByType(
        @RequestParam("typeId") Integer typeId,
        @RequestParam(value = "current", defaultValue = "1") Integer current,
        @RequestParam(value = "x", required = false) Double x,
        @RequestParam(value = "y", required = false) Double y
) {
   return shopService.queryShopByType(typeId, current, x, y);
}

修改接口IShopService.java

 /**
     * @param
     * @return void
     * @description //根据商铺类型分页查询商铺信息(加入坐标)
     * @param: typeId
     * @param: current
     * @param: x
     * @param: y
     * @date 2023/2/19 1:00
     * @author wty
     **/
    Result queryShopByType(Integer typeId, Integer current, Double x, Double y);

修改实现类ShopServiceImpl

    /**
     * @param
     * @return void
     * @description //根据商铺类型分页查询商铺信息(加入坐标)
     * @param: typeId
     * @param: current
     * @param: x
     * @param: y
     * @date 2023/2/19 1:00
     * @author wty
     **/
    @Override
    public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
        // 1.判断是否需要根据坐标查询,如果需要再按照坐标
        if (null == x || null == y) {
            // 根据类型分页查询
            Page<Shop> page = query()
                    .eq("type_id", typeId)
                    .page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
            // 返回数据
            return Result.ok(page.getRecords());
        }

        // 2.分页参数的计算
        /**
         * 当前页的起始数据是第几条
         */
        int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
        /**
         * 当前页的结束数据是第几条
         */
        int end = current * SystemConstants.DEFAULT_PAGE_SIZE;

        // 3.查询redis,按照距离排序和分页 结果 shopId ,distance
        // GEOSEARCH g1 FROMLONLAT 116.397904 39.909005 BYRADIUS 10 km WITHDIST
        String key = RedisConstants.SHOP_GEO_KEY + typeId;
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo()
                .search(key,
                        GeoReference.fromCoordinate(x, y),
                        new Distance(RedisConstants.GEO_DISTANT),
                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(end)
                );

        // 4.解析shopId
        if (null == results) {
            return Result.ok(Collections.emptyList());
        }
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
        ArrayList<Long> shopIds = new ArrayList<>(list.size());
        Map<String, Distance> map = new HashMap<>(list.size());
        // 4.1 截取从from 到 end
        list.stream().skip(from).forEach(result -> {
            // 4.2获取店铺id
            String shopIdStr = result.getContent().getName();
            shopIds.add(Long.valueOf(shopIdStr));

            // 4.3获取距离
            Distance distance = result.getDistance();

            map.put(shopIdStr, distance);
        });

        // 5.根据id查询shop
        String joinStr = StrUtil.join(",", shopIds);
        List<Shop> shops = query().in("id", shopIds).last("order by field(id," + joinStr + ")").list();
        for (Shop shop : shops) {
            shop.setDistance(map.get(shop.getId().toString()).getValue());
        }

        // 6.返回
        return Result.ok(shops);
    }

重启应用,发现按照距离由近到远排序了
在这里插入图片描述
此时滚动往下拉发现报错了
在这里插入图片描述
发现IDEA控制台报错了
在这里插入图片描述
在ShopServiceImpl.java中增加逻辑

        if (list.size() <= from) {
            return Result.ok(Collections.emptyList());
        }

在这里插入图片描述
再次重启,查看下拉就正常了
在这里插入图片描述
在这里插入图片描述

4.9. 签到功能

4.9.1.BitMap原理

1. 用户签到-BitMap功能演示

我们针对签到功能完全可以通过mysql来完成,比如说以下这张表

在这里插入图片描述

用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条

每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则最多需要600多字节

我们如何能够简化一点呢?其实可以考虑小时候一个挺常见的方案,就是小时候,咱们准备一张小小的卡片,你只要签到就打上一个勾,我最后判断你是否签到,其实只需要到小卡片上看一看就知道了。
在这里插入图片描述
我们可以采用类似这样的方案来实现我们的签到需求。

我们按月来统计用户签到信息,签到记录为1,未签到则记录为0.

把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。这样我们就用极小的空间,来实现了大量数据的表示

Redis中是利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是 232个bit位。

在这里插入图片描述
BitMap的操作命令有:

  • SETBIT:向指定位置(offset)存入一个0或1,从0开始
  • GETBIT :获取指定位置(offset)的bit值
  • BITCOUNT :统计BitMap中值为1的bit位的数量
  • BITFIELD :操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
  • BITFIELD_RO :获取BitMap中bit数组,并以十进制形式返回
  • BITOP :将多个BitMap的结果做位运算(与 、或、异或)
  • BITPOS :查找bit数组中指定范围内第一个0或1出现的位置
    需求:实现签到接口,将当前用户当天签到信息保存到Redis中

示例:

SETBIT bm1 0 1

在这里插入图片描述

GETBIT bm1 0

在这里插入图片描述

SETBIT bm1 1 1
SETBIT bm1 2 1
SETBIT bm1 5 1
SETBIT bm1 6 1

在这里插入图片描述
查看redis图形界面中存取的数据,注意勾选二进制
在这里插入图片描述

BITCOUNT bm1

在这里插入图片描述
从0开始2个bit位,刚好是11,二进制11转换为十进制就是3

BITFIELD bm1 GET u2 0

在这里插入图片描述
同理取3位,就是111,转换为10进制就是7

BITFIELD bm1 GET u3 0

在这里插入图片描述
查找bit数组中指定范围内第一个0出现的位置

BITPOS bm1 0

在这里插入图片描述
在这里插入图片描述

思路:我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到。

我们通过接口文档发现,此接口并没有传递任何的参数,没有参数怎么确实是哪一天签到呢?这个很容易,可以通过后台代码直接获取即可,然后到对应的地址上去修改bitMap。

在这里插入图片描述

4.9.2.实现签到功能

代码

修改UserController

 @PostMapping("/sign")
 public Result sign(){
    return userService.sign();
 }

修改接口IUserService.java

   /**
     * @param
     * @return com.hmdp.dto.Result
     * @description //签到
     * @date 2023/2/19 12:21
     * @author wty
     **/
    Result sign();

修改UserServiceImpl

    /**
     * @param
     * @return com.hmdp.dto.Result
     * @description //签到
     * @date 2023/2/19 12:22
     * @author wty
     **/
    @Override
    public Result sign() {
        // 1.获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));

        // 3.拼接key
        String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;

        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();

        // 5.写入Redis SETBIT key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);

        return Result.ok();
    }

重启应用测试
打开PostMan,配置路径

http://localhost:8080/api/user/sign

配置token
在这里插入图片描述
点击send后的结果
在这里插入图片描述
保存在redis中的信息如下:比如今天19号,那就在19位是1
在这里插入图片描述

4.9.3.实现补签功能

用命令行给前三天补签

SETBIT sign:1010:202302 0 1
SETBIT sign:1010:202302 1 1
SETBIT sign:1010:202302 2 1

在这里插入图片描述
前3天签到
在这里插入图片描述

4.9.4.统计连续签到天数

1. 用户签到-签到统计

**问题1:**什么叫做连续签到天数?
从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。
在这里插入图片描述
Java逻辑代码:获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了

**问题2:**如何得到本月到今天为止的所有签到数据?

BITFIELD key GET u[dayOfMonth] 0

假设今天是10号,那么我们就可以从当前月的第一天开始,获得到当前这一天的位数,是10号,那么就是10位,去拿这段时间的数据,就能拿到所有的数据了,那么这10天里边签到了多少次呢?统计有多少个1即可。

问题3:如何从后向前遍历每个bit位?

注意:bitMap返回的数据是10进制,哪假如说返回一个数字8,那么我哪儿知道到底哪些是0,哪些是1呢?我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1 才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次,就把签到结果向右移动一位,依次内推,我们就能完成逐个遍历的效果了。

需求:实现下面接口,统计当前用户截止当前时间在本月的连续签到天数

有用户有时间我们就可以组织出对应的key,此时就能找到这个用户截止这天的所有签到记录,再根据这套算法,就能统计出来他连续签到的次数了。

在这里插入图片描述
代码

UserController

@GetMapping("/sign/count")
public Result signCount(){
    return userService.signCount();
}

IUserService.java

/**
     * @param
     * @return com.hmdp.dto.Result
     * @description //合计签到总数
     * @date 2023/2/19 14:33
     * @author wty
     **/
    Result signCount();

UserServiceImpl

    /**
     * @param
     * @return com.hmdp.dto.Result
     * @description //合计签到总数
     * @date 2023/2/19 14:33
     * @author wty
     **/
    @Override
    public Result signCount() {
        // 1.获取当前登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));

        // 3.拼接key
        String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;

        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();

        // 5.获取本月截至今天为止所有的签到记录,返回的是一个十进制的数字
        // BITFIELD bm1 GET u2 0
        List<Long> results = stringRedisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                        .valueAt(0)
        );

        if (null == results || results.isEmpty()) {
            // 没有任何签到结果
            return Result.ok(0);
        }
        Long num = results.get(0);
        if (null == num || 0 == num) {
            return Result.ok(0);
        }

        // 6.循环遍历
        // 计数器
        int calCount = 0;
        while (true) {
            // 7.让这个数字与1做与运算,得到数字的最后一个bit位
            if ((num & 1) == 0) {
                // 判断这个bit位是否为0,如果为0说明未签到,结束
                break;

            } else {
                // 如果不为0,说明已签到,计数器 + 1
                calCount++;
            }


            // 把数字(无符号)右移一位,抛弃最后一个bit位,继续下一个bit位
            num = (num >>> 1);

        }


        return Result.ok(calCount);
    }

重新启动应用
用PosrMan测试,注意这里实现的是截至今天为止的连续签到次数,昨天签到,今天没签,连续签到次数也是0
在这里插入图片描述
在这里插入图片描述

2. 额外加餐-关于使用bitmap来解决缓存穿透的方案

回顾缓存穿透

发起了一个数据库不存在的,redis里边也不存在的数据,通常你可以把他看成一个攻击

解决方案:

  • 判断id<0

  • 如果数据库是空,那么就可以直接往redis里边把这个空数据缓存起来

第一种解决方案:遇到的问题是如果用户访问的是id不存在的数据,则此时就无法生效

第二种解决方案:遇到的问题是:如果是不同的id那就可以防止下次过来直击数据

所以我们如何解决呢?

我们可以将数据库的数据,所对应的id写入到一个list集合中,当用户过来访问的时候,我们直接去判断list中是否包含当前的要查询的数据,如果说用户要查询的id数据并不在list集合中,则直接返回,如果list中包含对应查询的id数据,则说明不是一次缓存穿透数据,则直接放行。

在这里插入图片描述

现在的问题是这个主键其实并没有那么短,而是很长的一个 主键

哪怕你单独去提取这个主键,但是在11年左右,淘宝的商品总量就已经超过10亿个

所以如果采用以上方案,这个list也会很大,所以我们可以使用bitmap来减少list的存储空间

我们可以把list数据抽象成一个非常大的bitmap,我们不再使用list,而是将db中的id数据利用哈希思想,比如:

id % bitmap.size = 算出当前这个id对应应该落在bitmap的哪个索引上,然后将这个值从0变成1,然后当用户来查询数据时,此时已经没有了list,让用户用他查询的id去用相同的哈希算法, 算出来当前这个id应当落在bitmap的哪一位,然后判断这一位是0,还是1,如果是0则表明这一位上的数据一定不存在, 采用这种方式来处理,需要重点考虑一个事情,就是误差率,所谓的误差率就是指当发生哈希冲突的时候,产生的误差。

在这里插入图片描述

在这里插入图片描述

4.10.UV统计

4.10.1.UV统计的基本思路

首先我们搞懂两个概念:

  • UV:全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。
  • PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。

通常来说UV会比PV大很多,所以衡量同一个网站的访问量,我们需要综合考虑很多因素,所以我们只是单纯的把这两个值作为一个参考值。

UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到Redis中,数据量会非常恐怖,那怎么处理呢?

Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考:Hyperloglog算法

Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。

在这里插入图片描述

示例:
添加5个元素

PFADD hl e1 e2 e3 e4 e5

在这里插入图片描述
查询元素的个数

PFCOUNT  hl

在这里插入图片描述
UV统计,对于相同用户不能统计2次,那么我们插入相同元素看一下。发现计算的个数还是5
在这里插入图片描述

4.10.2.HypeLogLog实现统计

查看当前redis内存占用情况:

info memory

在这里插入图片描述
当前内存占用情况:2139256

测试思路:我们直接利用单元测试,向HyperLogLog中添加100万条数据,看看内存占用和统计效果如何。

在这里插入图片描述
代码如下:

@Test
    public void testHyperLog() {
        String[] values = new String[1000];
        int j = 0;
        for (int i = 0; i < 1000000; i++) {
            j = i % 1000;
            values[j] = "user_" + i;
            if (j == 999) {
                // 发送到Redis
                stringRedisTemplate.opsForHyperLogLog().add("hl", values);

            }
        }

        // 统计数量
        Long count = stringRedisTemplate.opsForHyperLogLog().size("hl");
        System.out.println("统计的总数是:" + count);

    }

测试结果:
在这里插入图片描述
看一下误差

在这里插入图片描述
再看一下内存占用:

在这里插入图片描述
目前是2175992
与之前的差值是36736bit
在这里插入图片描述
换算成kb是35kb
在这里插入图片描述

1百万数据只占了36kb

经过测试:我们会发现它的误差是在允许范围内,并且内存占用极小。

黑马实战篇结束了!

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

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

相关文章

.net开发安卓入门-自动升级(配合.net6 webapi 作为服务端)

文章目录思路客户端权限清单&#xff08;AndroidManifest.xml&#xff09;权限列表(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;打开外部应用的权限(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;添加文件如下…

网络数据包接收流程

1. 网络数据包接收流程简述 典型的以太网卡网络包接收流程如下&#xff1a; 1.网络包通过物理介质传到接收端的phy芯片&#xff1b; 2.phy芯片通过RGMII协议传到MAC芯片rx queue fifo中&#xff1b; 3.MAC芯片通过专用DMA将网络包搬运到网卡驱动程序预先分配好的rx ringbuffer中…

【多线程与高并发】- synchronized锁的认知

synchronized锁的认知 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 一个有梦有戏的人 怒放吧德德 &#x1f31d;分享学习心得&#xff0c;欢迎指正&#xf…

言简意赅+图解 函数传参问题(传值、传地址 500字解决战斗)

1、传值 2、传地址 不论是传值&#xff0c;还是传地址&#xff0c;形参都是对于实参的一份拷贝 下图为按值传递进行交换&#xff1a; 形参left拷贝一块新空间&#xff0c;形参right拷贝一块新空间 下图为按指针传递进行交换 形参left拷贝一块新的空间&#xff0c;形参right…

研究生薪资管理系统-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-5】研究生薪资管理系统 【案例介绍】 案例描述 在学校中&#xff0c;学生每个月需要交相应的生活费&#xff0c;老师每个月有相应的工资&#xff0c;而在职研究生既是老师又是学生&#xff0c;所以在职研究生既需要交学费又会有工资。下面要求编写一个程序来统计在职…

最完整的小红书带货笔记——垂直模式

最完整的小红书带货笔记——垂直模式&#xff0c;小红书直播复盘怎么做&#xff1f;#直播带货笔记 第1篇&#xff0c;带你解锁直播复盘5大要点&#xff01; #小红书店铺#小红书运营 小红书怎么发带货笔记&#xff1f; 做小红书带货的同学注意了&#xff0c;我们带货一定要发笔…

看见统计——第三章 概率分布

看见统计——第三章 概率分布 参考 https://github.com/seeingtheory/Seeing-Theory中心极限定理 概率分布描述了随机变量取值的规律。 随机变量Random Variables &#x1f525; 定义&#xff1a;将样本空间中的结果映射到实数的函数 XXX 称为随机变量(random variable)&a…

【算法基础】链表

一、单链表例题&#xff1a;实现一个单链表&#xff0c;链表初始为空&#xff0c;支持三种操作&#xff1a;向链表头插入一个数&#xff1b;删除第 k个插入的数后面的数&#xff1b;在第 k&#xfffd; 个插入的数后插入一个数。现在要对该链表进行 M次操作&#xff0c;进行完所…

单片机——显示方式

数码LED 一、静态显示方式 1、连接 所有LED的位选均共同连接到VCC或GND&#xff0c;每个LED的8根段选线分别连接一个8位并行I/O口&#xff0c;从该I/O口送出相应的字型码显示字型。 2、这种连接方式的缺点就是需要的数据线太多&#xff1a;我们可以计算一下&#xff1a;8*4133根…

[oeasy]python0088_字节_Byte_存储单位_KB_MB_GB_TB

编码进化 回忆上次内容 上次 回顾了 字符大战的结果 ibm 曾经的 EBCDIC 由于字符不连续的隐患 导致后续 出现 无数问题无法补救 7-bit 的 ASA X3.4-1963 字母序号连续 比较字符时 效率高判断字符 是否是字母 也很容易 获得了 IBM以外公司的 支持 为什么 ASA X3.4-1963 是 7…

PHP 页面静态化

前言随着网站的内容的增多和用户访问量的增多&#xff0c;网站加载会越来越慢&#xff0c;受限于带宽和服务器同一时间的请求次数的限制&#xff0c;&#xff0c;我们往往需要在此时对我们的网站进行代码优化和服务器配置的优化。一、页面静态化概念静态化定义静态化就是指把原…

VNCTF 2023 - Web 象棋王子|电子木鱼|BabyGo Writeups

象棋王子 签到题&#xff0c;jsfuck解密 丢到console得到flag 电子木鱼 后面两道都是代码审计&#xff0c;这题是rust&#xff0c;题目给出了源码&#xff0c;下载下来看 关键代码&#xff1a; 由于限制&#xff0c;quantity只能为正数 功德也只能是正数&#xff08;负数的…

如何下载JDK8源码并导入IDEA

目录一、下载JDK8源码二、将源码导入IDEA一、下载JDK8源码 JDK8源码下载地址&#xff1a; https://hg.openjdk.org/ 1.打开下载地址&#xff0c;JDK8的源码都是基于jdk8u之上&#xff0c;所以点击jdk8u 2.进入页面之后可以看到一些小版本&#xff0c;这里我们选择 jdk8u60&am…

Python 之 Pandas Series 数据结构

文章目录一、Series 结构二、数据结构 Series 创建1. 创建1.1 列表/数组作为数据源创建 Series1.2 字典作为数据源创建 Series1.3 通过标量创建2. 参数说明2.1 index 参数2.2 name 参数2.3 copy 参数三、Series 的索引/切片1. 下标索引2. 标签索引3. 切片四、Series 数据结构的…

WSL1和WSL2相互转换以及安装路径迁移相关问题

目录 1.从WSL 1如何切换到WSL 2&#xff1f; 2.从WSL 2如何切换回WSL 1&#xff1f; 3.WSL1转换为WSL2后&#xff0c;WSL1里面安装的程序和库需要重装吗&#xff1f; 4.WSL2转换为WSL1后&#xff0c;WSL2里面安装的程序和库需要重装吗&#xff1f; 5.如何备份WSL2&#xf…

【内网安全】——Windows权限维持

作者名&#xff1a;白昼安全主页面链接&#xff1a; 主页传送门创作初心&#xff1a; 以后赚大钱座右铭&#xff1a; 不要让时代的悲哀成为你的悲哀专研方向&#xff1a; web安全&#xff0c;后渗透技术每日鸡汤&#xff1a;20岁的年纪不该困在爱与不爱里&#xff0c;对吗在红队…

shiro CVE-2020-13933

0x00 前言 同CVE-2020-1957&#xff0c;补充一下笔记&#xff0c;在CVE-2020-1957的基础上进行了绕过。 影响版本&#xff1a;Apache Shiro < 1.6.0 环境搭建参考&#xff1a;shiro CVE-2020-1957 0x01 漏洞复现 CVE-2020-13933中使用%3b绕过了shiro /*的检测方式&…

使用BP神经网络和Elman Net预测航班价格(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

电脑如何录制屏幕视频?6个屏幕录制的好方法

我们每天消费的大部分媒体都是通过视频。研究表明&#xff0c;与阅读相比&#xff0c;我们通过观看视频保留的信息更多。因此&#xff0c;很容易看出视频在我们的个人生活和职业生活中的重要性。 让我们更深入地了解视频录制软件和市场上的一些最佳选择。 如何确定好的视频录制…

建议收藏,轻松搞懂区块链

未来已来&#xff0c;只是不均衡地分布在当下 大家好&#xff0c;我是菜农&#xff0c;欢迎来到我的频道。 本文共 5844字&#xff0c;预计阅读 30 分钟 区块链是近些年来最热门的前沿技术&#xff0c;被认为是未来十几年对金融、物联网、医疗等诸多领域产生最大影响的"…