分布式NoSQL列存储数据库Hbase Java API(三)

简介: 分布式NoSQL列存储数据库Hbase Java API(三)

分布式NoSQL列存储数据库Hbase(三)

知识点01:课程回顾

  1. Hbase使用场景
  • 集群管理:定时调度管理的脚本
  • 定时调度:Azkaban、Oozie、Linux Crontab
  • Hbase脚本
  • 将Hbase命令写入一个文件
  • hbase shell运行文件即可
  • 测试开发:Hbase命令
  • DDL
  • list_namespace/create_namespace
  • create/list/desc
  • DML
  • put:插入、更新
  • 插入:每次是为每行插入一列
  • 更新:通过插入数据来代替了更新,用于提高性能
  • 语法
put 表 rowkey    列族:列    值
  • delete:删除数据
  • delete:删除列
  • deleteall:删除行
  • truncate:清空表
  • get:获取某个rowkey的数据
  • 优点:查询数据最快的方式,根据rowkey进行查询
  • rowkey是Hbase中的唯一索引
  • 缺点
  • get最多返回一个rowkey的所有列
  • 语法
get 表名    rowkey    【列族:列】
  • scan:查询符合条件的数据
  • 优点:返回需要的多条Hbase的数据
  • 缺点:很多情况下是不走索引【不能根据rowkey进行检索】
  • 语法
scan 表名
scan 表名 + Filter
  • 生产开发:JavaAPI
  • Connection:连接对象
  • Configuration:配置管理对象
  • HbaseAdmin:Hbase管理员对象:用于实现DDL操作

知识点02:课程目标

  1. Hbase JavaAPI实现DML操作【重点内容】
  • put:数据的写入或者更新
  • get:查询数据
  • delete:删除数据
  • scan:扫描数据
  • Filter:过滤器
  1. Hbase存储设计【掌握原理】
  • 表、RegionServer关系?
  • 表的分布式如何实现的?
  • Region的划分规则?
  • 数据如何决定进入哪个分区哪?
  • 分区内部是如何存储数据的?

知识点03:Java API:DML:Table

  • 需求:通过JavaAPI实现对Hbase的DML操作
  • 分析
  • step1:构建连接对象
  • step2:构建Table对象
  • 所有的DML操作,必须构建一个表的对象
  • 实现
//todo:1-构建一个连接对象
Configuration conf = HBaseConfiguration.create();
//指定Hbase集群的服务端地址:给定ZK的地址
conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
Connection conn = ConnectionFactory.createConnection(conf);
//todo:2-所有的DML必须构建一个Table表的对象
//构建一个表的对象
TableName tbname = TableName.valueOf("itcast:t1");
Table table = conn.getTable(tbname);
  • 总结
  • step1:先构建一个连接对象:Connection
  • step2:根据所做的操作决定构建哪种对象
  • DDL:HbaseAdmin
  • DML:Table

知识点04:Java API:DML:Put

  • 需求:JavaAPI实现往Hbase表中写入数据
put   表   rowkey    列族:列    值
  • 分析
  • step1:Hbase中实现put操作,需要构建Put对象
  • step2:为Put对象添加列族、列名、值
  • step3:对表执行put操作
  • 实现
/**
     * 用于实现使用Put插入数据到表中:put   表   rowkey    列族:列    值
     * @param table
     */
    private void putData(Table table) throws IOException {
        //step1:构建Put对象,必须指定插入的rowkey的数据
        Put put = new Put(Bytes.toBytes("20201001_888"));
        //step2:初始化put,添加列族、列、值
        put.addColumn(
                Bytes.toBytes("basic"),//指定列族
                Bytes.toBytes("name"),//指定列的名称
                Bytes.toBytes("laoba")//指定列的值
        );
        //step3:让表执行put操作
        table.put(put);     
    }
  • 总结[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a57OCTc7-1616631335573)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318084424081.png)]
  • Put对象:专门用于往Hbase写入数据的对象
  • 指定Rowkey,添加列族、列、值信息
  • table.put

知识点05:Java API:DML:Get

  • 需求:JavaAPI实现从Hbase表中读取某个Rowkey的数据
get 表名  rowkey  【列族,列】
  • 分析
  • step1:Hbase中实现get操作,需要构建Get对象
  • step2:为Get对象添加列族、列名
  • step3:对表执行get操作,获取返回值,打印输出
table.get(Get)
  • 实现
/**
     * 使用Get查询Hbase表中的数据:get 表名 rowkey  【列族,列】
     * @param table
     */
    private void getData(Table table) throws IOException {
        //step1:构建Get对象,必须指定Rowkey
        Get get = new Get(Bytes.toBytes("20201001_888"));
        //step2:可选的,配置Get需要的参数:列族、列
//        get.addColumn()//指定获取某一列的数据
//        get.addFamily()//指定获取某个列族的数据
        //step3:表执行get操作
        Result result = table.get(get);//Get操作的返回值为Result类型对象
        /**
         * 迭代输出Result对应的rowkey中的每一列的数据,一个Rowkey包含很多列,循环迭代输出每一列的数据
         *      Result:一个Result对象就是一个Rowkey的数据
         *      Cell:一个Cell代表一个rowkey中一列的数据
         *      一个Result对象中会包含多个Cell
         */
        for(Cell cell : result.rawCells()){
            //输出每一列Cell对象中的数据:20201001_888  column=basic:age, timestamp=1616029665232, value=20
            System.out.println(
                    Bytes.toString(CellUtil.cloneRow(cell)) //获取这一列的rowkey,转换为字符串类型
                    +"\t"+
                    Bytes.toString(CellUtil.cloneFamily(cell)) //获取这一列的列族,转换为字符串类型
                    +"\t"+
                    Bytes.toString(CellUtil.cloneQualifier(cell)) //获取这一列的名称,转换为字符串类型
                    +"\t"+
                    Bytes.toString(CellUtil.cloneValue(cell)) //获取这一列的值,转换为字符串类型
                    +"\t"+
                    cell.getTimestamp() //获取时间戳
            );
        }
    }
  • 总结
  • step1:先构建Get
  • step2:执行Get,得到返回值
  • Result:一个Result就是一个Rowkey的数据,包含一个Cell数组
  • Cell:一个Cell就是一列的数据

知识点06:Java API:DML:Delete

  • 需求:JavaAPI实现从Hbase表删除某个Rowkey的某列的数据
  • 分析
  • step1:Hbase中实现delete操作,需要构建Delete对象
  • step2:为Delete对象添加列族、列名
  • step3:对表执行delete操作
table.delete(Delete)
  • 实现
/**
     * 删除某个Rowkey的某一列的数据:delete  表名     rowkey      列族:列
     * @param table
     */
    private void deleteData(Table table) throws IOException {
        //step1:构建删除对象
        Delete delete = new Delete(Bytes.toBytes("20201001_888"));
        //step2:添加删除配置
//        delete.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name")); //只删除最新版本
        delete.addColumns(Bytes.toBytes("basic"),Bytes.toBytes("name")); //删除所有版本
        //step3:执行删除
        table.delete(delete);
    }
  • 总结
  • step1:构建Delete对象
  • step2:根据需求调用配置方法:添加列族、列
  • step3:表对象指定delete对象即可

知识点07:Java API:DML:Scan

  • 需求:JavaAPI实现从Hbase表中读取所有数据
scan 表
scan 表 + filter
  • 分析
  • step1:Hbase中实现scan操作,需要构建Scan对象
  • step2:对表执行scan操作
  • step3:获取返回值,取出每个Rowkey的数据,打印输出
  • 实现
/**
     * 通过Scan,进行Hbase表的数据查询:scan 表
     * @param table
     */
    private void scanData(Table table) throws IOException {
        //step1:构建Scan对象
        Scan scan = new Scan();
        //step2:执行scan:返回值是ResultScanner,包含了多个Rowkey的数据
        ResultScanner rsScan = table.getScanner(scan);
        /**
         *      ResultScanner:包含多个Rowkey的数据,包含了多个Result对象:Iterator<Result>
         *      Result:一个Rowkey的数据,包含了这个Rowkey多列的数据:Cell[]
         *      Cell :一列的数据
         */
        //step3:打印数据
        //先获取每个rowkey
        for(Result rs:rsScan){
            //直接输出当前rowkey的值
            System.out.println(Bytes.toString(rs.getRow()));
            //再获取每个rowkey中的每一列
            for(Cell cell : rs.rawCells()){
                //输出每一列Cell对象中的数据:20201001_888  column=basic:age, timestamp=1616029665232, value=20
                System.out.println(
                        Bytes.toString(CellUtil.cloneRow(cell)) //获取这一列的rowkey,转换为字符串类型
                                +"\t"+
                                Bytes.toString(CellUtil.cloneFamily(cell)) //获取这一列的列族,转换为字符串类型
                                +"\t"+
                                Bytes.toString(CellUtil.cloneQualifier(cell)) //获取这一列的名称,转换为字符串类型
                                +"\t"+
                                Bytes.toString(CellUtil.cloneValue(cell)) //获取这一列的值,转换为字符串类型
                                +"\t"+
                                cell.getTimestamp() //获取时间戳
                );
            }
            System.out.println("----------------------------------------------------------------------");
        }
    }
  • 总结
  • step1:先构建Scan对象
  • step2:执行Scan操作
  • step3:返回值
  • ResultScanner:包含多个Rowkey的数据的集合
  • Iter《Result》
  • Result:一个Rowkey的数据
  • Cell[]
  • Cell:一列的数据

知识点08:Java API:DML:Filter

  • 需求:JavaAPI实现从Hbase表中根据条件读取部分
  • 需求1:查询2021年1月和2月的数据
  • 需求2:查询2021年的所有数据
  • 需求3:查询所有age = 20的数据
  • 需求4:查询所有数据的name和age这两列
  • 需求5:查询所有年age = 20的人的name和age
  • 分析
  • Rowkey的设计:时间【年月日】+id
  • Rowkey整体有序
  • 前缀匹配
  • 需求1
  • StartRow = 》202101
  • 包含
  • StopRow = 》202103
  • 不包含
  • 需求2
  • 方案一:使用startrow = 2021,stoprow = 2022
  • 方案二:使用Filter:PrefixFilter,Rowkey的前缀过滤器[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MfZFeVn-1616631335575)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318103200422.png)]
  • 构建过滤器,指定Rowkey前缀
  • Scan加载过滤器,返回所有符合rowkey前缀的过滤器
  • 需求三:查询所有age = 20的数据
where age = 20
  • Filter:列值过滤器
  • SingleColumnValueFilter:对某一列的值进行过滤
  • 指定列值过滤器:哪个列族下的哪一列的值等于什么值
  • scan加载过滤器
  • 需求4:查询所有数据的name和age这两列
select name ,age from table
  • Filter:多列前缀过滤器
  • MutipleColumnPrefixFIlter:用于将每条数据中指定的列过滤出来
  • 指定需要过滤哪些列
  • scan加载过滤器即可
  • 需求5:查询所有年age = 20的人的name和age
select name,age from table where age = 20
  • 支持组合过滤器:FilterList
  • 列的过滤:MutipleColumnPrefixFIlter
  • 列值过滤:SingleColumnValueFilter
  • 构建两个过滤器,将两个过滤器组合放入过滤器集合
  • Scan加载过滤器集合
  • 实现
  • 批量写入数据到Hbase
/**
     * 批量写入数据到Hbase
     * @param table
     */
    private void putListData(Table table) throws IOException {
        //step1:构建Put对象,一个Put对象用于表示一个Rowkey写入的数据
        Put put1 = new Put(Bytes.toBytes("20210101_001"));
        Put put2 = new Put(Bytes.toBytes("20210201_002"));
        Put put3 = new Put(Bytes.toBytes("20210301_003"));
        //step2:为Put添加这个Rowkey的每一列
        put1.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoda"));
        put1.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("18"));
        put1.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("110"));
        put1.addColumn(Bytes.toBytes("other"),Bytes.toBytes("addr"),Bytes.toBytes("shanghai"));
        put2.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoer"));
        put2.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("20"));
        put2.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("120"));
        put3.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laosan"));
        put3.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("22"));
        put3.addColumn(Bytes.toBytes("other"),Bytes.toBytes("addr"),Bytes.toBytes("beijing"));
        //step3:将多个Put封装到List中
        List<Put> puts = new ArrayList<Put>();
        puts.add(put1);
        puts.add(put2);
        puts.add(put3);
        //step4:执行PutList
        table.put(puts);
    }
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ijNYbrbY-1616631335576)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318101928785.png)]
  • 需求一:实现范围过滤
//需求一:查询2021年1月和2月的数据
scan.withStartRow(Bytes.toBytes("202101"));
scan.withStopRow(Bytes.toBytes("202103"));
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUtwEsEy-1616631335577)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318102604990.png)]
  • 需求二实现
//需求2:查询2021年的所有数据
Filter prefixFiter = new PrefixFilter(Bytes.toBytes("2021"));
//将过滤器加载到scan中
scan.setFilter(prefixFiter);
  • 需求三实现
//需求三:查询所有age = 20的数据
        /**
         *    * @param family name of column family
         *    * @param qualifier name of column qualifier
         *    * @param op operator
         *    * @param value value to compare column values against
         */
        Filter valueFilter = new SingleColumnValueFilter(
                Bytes.toBytes("basic"),//指定列族
                Bytes.toBytes("age"),//指定列
                CompareOperator.EQUAL,//指定比较器类型
                Bytes.toBytes("20")  //比较的值
        );
        //将过滤器加载到scan中
        scan.setFilter(valueFilter);
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sP5MLfRe-1616631335579)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318104213375.png)]
  • 需求四实现
//需求4:查询所有数据的name和age这两列
        //构建所有需要过滤的列
        byte[][] prefixes = {
            Bytes.toBytes("name"),
            Bytes.toBytes("age")
        };
        Filter columnFilter = new MultipleColumnPrefixFilter(prefixes);
        //将过滤器加载到scan中
        scan.setFilter(columnFilter);
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vkQbO9w9-1616631335580)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318104930496.png)]
  • 需求五实现
Filter valueFilter = new SingleColumnValueFilter(
                Bytes.toBytes("basic"),//指定列族
                Bytes.toBytes("age"),//指定列
                CompareOperator.EQUAL,//指定比较器类型
                Bytes.toBytes("20")  //比较的值
        );
        //需求4:查询所有数据的name和age这两列
        //构建所有需要过滤的列
        byte[][] prefixes = {
            Bytes.toBytes("name"),
            Bytes.toBytes("age")
        };
        Filter columnFilter = new MultipleColumnPrefixFilter(prefixes);
        //需求5:查询所有age = 20的人的name和age
        //构建FIlterList
        FilterList lists = new FilterList();//MUST_PASS_ALL:and,MUST_PASS_ONE:or
        //添加过滤器
        lists.addFilter(valueFilter);
        lists.addFilter(columnFilter);
        //将过滤器加载到scan中
        scan.setFilter(lists);
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GaXWQJwf-1616631335581)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210318110732471.png)]
  • 总结
  • Rowkey范围过滤:StartRow和StopRow
  • Rowkey前缀过滤:PrefixFilter
  • Rowkey中列的值的过滤:SingleColumnValueFilter
  • Rowkey中列的过滤:MultipleColumnPrefixFilter
  • 多种条件的组合过滤:FilterList

知识点09:存储设计:存储架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QFiNIbeF-1616631335582)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210317190105892.png)]

  • 问题:Hbase整体如何实现数据的存储?
  • 分析

知识点10:存储设计:Table、Region、RegionServer的关系


  • 问题:客户端操作的是表,数据最终存在RegionServer中,表和RegionServer的关系是什么?
  • 分析

知识点11:存储设计:Region的划分规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xGxSZiF6-1616631335582)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210317191202582.png)]

  • 问题:一张表划分为多个Region,划分的规则是什么?写一条数据到表中,这条数据会写入哪个Region,分配规则是什么?
  • 分析

知识点12:存储设计:Region内部存储结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KRgBXSLA-1616631335583)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210317191716413.png)]

  • 问题:数据在Region的内部是如何存储的?
  • 分析

知识点13:存储设计:HDFS中的存储结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-10viiw3z-1616631335583)(20210318_分布式NoSQL列存储数据库Hbase(三).assets/image-20210317191754182.png)]

  • 问题:Hbase的数据在HDFS中是如何存储的?
  • 分析

egionServer的关系


  • 问题:客户端操作的是表,数据最终存在RegionServer中,表和RegionServer的关系是什么?
  • 分析

知识点11:存储设计:Region的划分规则

[外链图片转存中…(img-xGxSZiF6-1616631335582)]

  • 问题:一张表划分为多个Region,划分的规则是什么?写一条数据到表中,这条数据会写入哪个Region,分配规则是什么?
  • 分析

知识点12:存储设计:Region内部存储结构

[外链图片转存中…(img-KRgBXSLA-1616631335583)]

  • 问题:数据在Region的内部是如何存储的?
  • 分析

知识点13:存储设计:HDFS中的存储结构

[外链图片转存中…(img-10viiw3z-1616631335583)]

  • 问题:Hbase的数据在HDFS中是如何存储的?
  • 分析


相关实践学习
lindorm多模间数据无缝流转
展现了Lindorm多模融合能力——用kafka API写入,无缝流转在各引擎内进行数据存储和计算的实验。
云数据库HBase版使用教程
&nbsp; 相关的阿里云产品:云数据库 HBase 版 面向大数据领域的一站式NoSQL服务,100%兼容开源HBase并深度扩展,支持海量数据下的实时存储、高并发吞吐、轻SQL分析、全文检索、时序时空查询等能力,是风控、推荐、广告、物联网、车联网、Feeds流、数据大屏等场景首选数据库,是为淘宝、支付宝、菜鸟等众多阿里核心业务提供关键支撑的数据库。 了解产品详情:&nbsp;https://cn.aliyun.com/product/hbase &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
1月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
1月前
|
Java API Maven
如何使用Java开发抖音API接口?
在数字化时代,社交媒体平台如抖音成为生活的重要部分。本文详细介绍了如何用Java开发抖音API接口,从创建开发者账号、申请API权限、准备开发环境,到编写代码、测试运行及注意事项,全面覆盖了整个开发流程。
157 10
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
77 2
|
4天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
1月前
|
JSON Java 关系型数据库
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
在Java中,使用mybatis-plus更新实体类对象到mysql,其中一个字段对应数据库中json数据类型,更新时报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
82 4
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
|
19天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
52 10
|
27天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
44 6
|
27天前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
1月前
|
安全 Java API
告别SimpleDateFormat:Java 8日期时间API的最佳实践
在Java开发中,处理日期和时间是一个基本而重要的任务。传统的`SimpleDateFormat`类因其简单易用而被广泛采用,但它存在一些潜在的问题,尤其是在多线程环境下。本文将探讨`SimpleDateFormat`的局限性,并介绍Java 8引入的新的日期时间API,以及如何使用这些新工具来避免潜在的风险。
39 5