精华内容
下载资源
问答
  • 为什么会这样呢:当使用POI处理excel的时候,遇到了比较长的数字,虽然excel里面设置该单元格是文本类型的,但是POI的cell的类型就会变成数字类型 原来cell.getCellType=0的时候 我是这样子写的 cellvalue=Strin

    1。首先我这边确认我excel中手机号码这个设置的类型是文本类型
    2。但是在运行的时候手机号码还是被当成了number,所以解决方案怎么弄
    为什么会这样呢:当使用POI处理excel的时候,遇到了比较长的数字,虽然excel里面设置该单元格是文本类型的,但是POI的cell的类型就会变成数字类型
    原来cell.getCellType=0的时候
    我是这样子写的
    cellvalue=String.valueOf((int)cell.getNumbericCellValue())
    无论数字是否小数,使用cell.getNumbericCellValue() 去获取值的时候,会得到一个double,而且当长度大一点的时候会变成科学计数法形式

    所以要获取表格的原始数据,就只要对这个进行格式转换
    使用format方法就可以获取原始数据
    DecimalFormat df=new DecimalFormat(“0”);
    cellvalue=df.format(cell.getNumericCellValue());

    展开全文
  • 前言Elasticsearch诞生的本意为了解决文本搜索太慢的问题,ES会默认将所有的输入内容当作字符串来理解,对于字段类型是keyword或者text的数据比较友好。但是如果输入的类型是数字,ES还会把数字当作字符串吗?排序...

    05721493bd2500c1cc1b3dd0807e4975.png

    前言

    Elasticsearch诞生的本意是为了解决文本搜索太慢的问题,ES会默认将所有的输入内容当作字符串来理解,对于字段类型是keyword或者text的数据比较友好。但是如果输入的类型是数字,ES还会把数字当作字符串吗?排序问题还有范围查询问题怎么解决呢?这篇文章就简单介绍了ES对于数字类型(numeric)数据的处理,能让你大涨姿势

    简介

    Elasticsearch专为字符串搜索而生,在建立索引的时候针对字符串进行了非常多的优化,在对字符串进行准确匹配或者前缀匹配等匹配的时候效率是很高的。ES底层把所有的数据都会当成字符串,其中就包括数字——所有的数字在ES底层都是会以字符串的形式保存。

    这就和我们通常理解的数字就不一样了,在MySQL或者各种编程语言比如Java、Python中,数字比如int类型就是4个字节呀!最高位符号位,后面的位数按照二进制进行存储,还能有其他的表示方式?

    出现这个现象的原因就是ES和MySQL或者Java中对数字的需求不一样了。在编程语言中,数字要经常参与计算比如加减乘除还有移位以及比较大小操作,这个时候用int这种原生的方式是简单直接效率高的;在MySQL中,索引是以B+树的形式存储,每次查询某一个数字都要在树中把整个数字和分隔节点进行比较操作,直到找到最后的目标数据节点。

    可以看到直接使用int的本身的结构来存储的优势就是直接比较大小的效率非常高(空间消耗小也是另外一个优势),但是如果进行范围查询,就会有问题了。比如在MySQL的组合查询中如果出现范围查询,那么很有可能出现范围查询后面的索引是不生效的(具体的MySQL的组合查询的原理可以在网上看看),也就是说范围查询可能会降低查询性能;在编程语言中的集合的范围查询就只能遍历所有的元素,一一比较大小,没有优化。

    ES的出现为解决范围查询提供了一个新的思路——为不同精度范围内的数据直接建立索引,把符合范围查询要求的数据聚合到一个索引上面,在搜索的时候把大的搜索范围拆分成很多小的范围索引,直接用term搜索就可以找到符合要求的所有文档。

    emmm...是不是有点抽象╮(╯_╰)╭

    比如想搜索在[300,500]内的文档并且事先已经把值在[300,400]之间的文档索引到了3[400,500]之间的文档索引到了4,那么就直接通过term查询取出34对应的文档id列表并且进行or操作就可以了,简单直接高效。

    2bd9461d728d0cb6eba46fb4cd917b04.png

    原理虽然简单,不过实现起来还是有些困难需要解决的。

    数字直接变成字符串的问题

    ES并没有直接把数字变成字符串,也没有对每个数字建立简单的索引,因为这两种做法可能会带来一些问题。

    字符串比较

    首先最大的问题是数字变成字符串之后如何进行比较,如果直接是把十进制的数字变成字符串,排序按照字典序(lexicographic)比较(默认所有的term都是按照字典序比较大小),会有不同位数比较的问题。比如搜索[423,642]内的文档,5也会被算在内,因为字典序"5""423"大,比"642"小。

    这个问题的一个解决方案是在每个数字变成字符串的时候在前面填充0,把5变成005,这样就能正确比较大小了,这也是旧版本的ES采用的解决方案。但是每次把int转化成string的时候要填充多少个0呢?太多了占空间,太少了又可能因为数字太长影响比较,比如最多只填充2个0,对于1000以下的数字没有问题,当数字大于1000了,个位数填充2个0就不够用了。

    获取的范围过多

    另一个问题是范围内的term过多带来的性能下降。比如现在有很多文档,其中索引的数字的列表为[421,423,445,446,448,521,522,632,633,634,641,642,644]一共13个term,如果我们想要查询[423,642]之间的所有的文档,需要取出一共11个term,然后用这些term去搜索对应的文档。

    046efe67f35f3f671efaf7d141227221.png

    当范围越来越大,需要的term的数量就越来越多,查询的性能就会不断下降。

    ES是怎么把数字变成字符串

    先来解决第一个问题,数字怎么变成字符串。

    十进制的数字有填充问题,如果变成了二进制,再进行词典序比较,不就没有问题了吗?Perfect,似乎问题完美的解决了。

    哥么你就没有考虑过负数的感受吗?

    59f92e6fbb5082e4509548733c0f6daa.png

    二进制的int保证是32位,对于正数和正数的比较或者负数和负数的比较是没有问题的,可是正数和负数的比较就不行了。正数的最高位是0,负数的最高位是1,直接比较,负数永远大于正数。这个时候ES采用的方法是把正数最高位变成1,负数最高位变成0,这样正数用于大于负数,问题就解决了。

    int类型解决了,float呢?由于浮点数在Java中的表示方法,最高位符号位,23~30位是指数位,0~22是尾数,如果直接把一个正的float当作int类型来比较好像也没有什么问题,指数位高的,当然大;指数相同,尾数大的也自然数字就大,所以正浮点型可以直接当成int转化。但是负数就不行了,指数越大,数字越小,尾数越大,数字也越小。ES给出的解决方案是直接对低31位每一位取反,1变成0,0变成1,这样负数的float就可以比较大小了。总结就是正float当int用,负float低31位取反后当成int用。

    对于long和double类型,也是同样的道理,只不过32位变成了64位。

    你以为就这样变成了二进制字符串了吗?不,还没有,没有这么简单。

    刚才是把int变成了二进制的字符串,一个字符只保存0和1不觉得浪费吗?一个int要用32个字符也就是32个字节保存,暴殄天物呀!

    Java的1个int占4个字节,1个char是2个字节,1个int用2个char不就行了。但是Java使用Unicode字符集来保存字符串,ES用UTF8编码保存Unicode字符,对于0~127使用1个字节,大于127一般2个字节,汉字通常3个字节,这样的话1个int用2个char表示,最多需要6个字节(这里int虽然不是汉字,但是在变成char之后有可能在Unicode字符集中表示某一个汉字)

    ES表示还能做的更好,上面不是说0~127只用一个字节吗?好,我就把int切分之后的大小限制在127以内(原来默认切分是4组8位的二进制数字)。127是7位二进制数字,int是32位的,那就把32位的int变成由4、7、7、7、7这5组二进制组成,最后这个字符串只需要5个字节就可以了,和上面的6个字节相比,空间利用率提高了17%

    f41084f000e734863d04d1c98044cd7b.png

    数字的索引是什么样子

    上面说到的另外一个问题是查询term数量太多的问题,解决方案就是用空间换时间,通过前缀聚合部分的term来达到。

    这里的聚合的实现方式是采用trie的数据结构,比如445、446和448这个三个term,可以聚合到44这个term的下面,节点44包含的文档的id列表应该是所有子节点的并集,这样原先需要的11个term就可以减少2个。同理对于其他的term也进行合并,合并之后[423,642]查询就只需要6个term,效率提高了一倍

    然而聚合也是要讲道理的,把445、446和448聚合到44以及把44聚合到4相当于是把数字除以10,精度就是10。但是并不是一直都希望这个精度是10,也可以设置为100(精度相对应的降低,节约索引空间)等等。ES提供了precisionStep来定制化这个精度,不过不是针对十进制,而是二进制的位数。比如precisionStep设置为4,那么在二进制位里面每隔4位(相当于十进制的16)就建立一个前缀聚合索引。

    41a8e0ff0ecdc6e3c8888dba9d012de7.png

    比如对于二进制数字0100 0011 0001 1010,当precisionStep为4的时候,会建立4个索引——0100 0011 0001 10100100 0011 00010100 0011以及0100(最高的4位),这四个索引相当于从trie的子节点一直到根节点

    精度越高(precisionStep越小)索引就越大,查询速度越快;精度越低,索引越小,相对查询速度比较慢。

    比如对于long类型的数据,precisionStep是4的时候,最多需要同时搜索465个term;precisionStep是2的时候,最多只要189个term。不过并不能绝对的说精度越高越好,因为查找这些term需要的时间也会相应增加。实际上最佳的precisionStep还是要根据业务情况测试得出。

    上面根据precisionStep建立索引的过程中有一个特殊的分词器来帮助拆分,比如把423拆成42342以及4。不过分词器会同样的把4拆分成4,那怎么区分423444呢?

    那就需要额外的空间来区分这两个4,ES给出的解决方案是在这两个数字前面加上一个前缀shift表示偏移量。比如4234shift242342shift1423shift0);而44shift0,所以前者的4比后者的要大。分词之后的term在每次比较之前都会先比较shiftshift越大,相应的term也越大,避免的重复的问题。

    总结上面建立索引的过程:当一个文档进来的时候,有一个数字423需要建立索引,于是先把这个int数据转化成字符串,再用一个特殊的分词器根据精度把423分成对应的三个term423424,并且附上对应的前缀shift,接下来在trie中找到这几个term,把稳定的id添加到这几个term的文档id列表里面(如果不存在就创建这个term)。

    查询原理

    清楚了数字类型的数据的索引机制之后,范围查询的原理就比较简单了。

    比如有一个范围[423, 642],要找到字段大于等于423并且小于等于642的文档。

    1. 先在索引的trie里面找到这两个term以及范围内的兄弟节点,分别是trie的两个叶节点423、641和642
    2. 从叶节点向上缩小范围,对两个数字分别除以10加一和减一之后查找范围为[43,63],此时的shift是1,得到这一层级的“叶子节点”以及范围内的兄弟节点是44和63
    3. 再从这一层向上,两个数字除以10,分别加一减一,得到范围[5,5]shift为2,这就是最后的节点了,term是5
    4. 上面三个步骤得到最后需要的term是423、44、5、63、641和642

    上面是用十进制举得一个例子,在二进制里面也是同样的道理,这里就不啰嗦了。实际上在ES实现里面用了很多位操作,效率相比于使用十进制要高很多,感兴趣的同学可以去看源代码,在LegacyNumericUtils类的splitRange方法里面。

    总结

    总的来说,Elasticsearch对于数字类型数据的索引和搜索不同于传统的MySQL或者Java等编程语言,采用了独特的字符串存储以及Trie数据结构保存索引的方式。

    ES先将输入的数字进行预处理,把float和double分别映射成int和long,原来是int和long类型的则保持不变

    然后把输入的整型数字切分成许多组由最长7位二进制数字组成的二进制串,每组二进制数字都是一个Unicode字符,整体连起来变成一个Unicode字符串

    接下来根据precisionStep把这个字符串数字分词成很多term,并附上前缀shift

    根据这些term建立索引词典,词典的结构类似于一个trie

    范围查询的时候根据trie把所谓的范围区间划分成离散的term字符串,这些term指向的文档的并集就是范围查询的结果

    One more thing

    谢谢各位大佬看完了这篇文章,在这里我很遗憾的通知您,以上提到的ES数字类型数据处理的方式已经被废弃了

    694781ec861e0e9e200d5e398e48ac16.png

    ES使用的搜索库Lucene在6.0版本以及以后为了解决多维空间位置搜索问题,改用新的数据结构——BKD树来实现位置搜索,带来了很大的性能提升。开发者发现这种实现也能用于一维数据搜索,于是用新的数据结构代替了现在的字符串的实现

    我会在以后专门写一篇文章介绍BKD树在ES中的应用

    参考

    The Evolution of Numeric Range Filters in Apache Lucene Numeric datatypes Class IntField Package org.apache.lucene.search Integer size vs Long size Lucene的数字范围搜索 (Numeric Range Query)原理 Lucene 4 和 Solr 4 学习笔记(3) Class LegacyNumericRangeQuery LegacyNumericUtils Class NumericRangeQuerys

    更多精彩内容请关注公众号:佛西先森

    展开全文
  • 更多精彩内容请看我的个人博客或者扫描二维码,关注微信公众号:...Elasticsearch诞生的本意为了解决文本搜索太慢的问题,ES会默认将所有的输入内容当作字符串来理解,对于字段类型是keyword或者text的数据比较友...

    更多精彩内容请看我的个人博客或者扫描二维码,关注微信公众号佛西先森
    WeChat

    前言

    Elasticsearch诞生的本意是为了解决文本搜索太慢的问题,ES会默认将所有的输入内容当作字符串来理解,对于字段类型是keyword或者text的数据比较友好。但是如果输入的类型是数字,ES还会把数字当作字符串吗?排序问题还有范围查询问题怎么解决呢?这篇文章就简单介绍了ES对于数字类型(numeric)数据的处理,能让你大涨姿势

    简介

    Elasticsearch专为字符串搜索而生,在建立索引的时候针对字符串进行了非常多的优化,在对字符串进行准确匹配或者前缀匹配等匹配的时候效率是很高的。ES底层把所有的数据都会当成字符串,其中就包括数字——所有的数字在ES底层都是会以字符串的形式保存。

    这就和我们通常理解的数字就不一样了,在MySQL或者各种编程语言比如Java、Python中,数字比如int类型就是4个字节呀!最高位符号位,后面的位数按照二进制进行存储,还能有其他的表示方式?

    出现这个现象的原因就是ES和MySQL或者Java中对数字的需求不一样了。在编程语言中,数字要经常参与计算比如加减乘除还有移位以及比较大小操作,这个时候用int这种原生的方式是简单直接效率高的;在MySQL中,索引是以B+树的形式存储,每次查询某一个数字都要在树中把整个数字和分隔节点进行比较操作,直到找到最后的目标数据节点。

    可以看到直接使用int的本身的结构来存储的优势就是直接比较大小的效率非常高(空间消耗小也是另外一个优势),但是如果进行范围查询,就会有问题了。比如在MySQL的组合查询中如果出现范围查询,那么很有可能出现范围查询后面的索引是不生效的(具体的MySQL的组合查询的原理可以在网上看看),也就是说范围查询可能会降低查询性能;在编程语言中的集合的范围查询就只能遍历所有的元素,一一比较大小,没有优化。

    ES的出现为解决范围查询提供了一个新的思路——为不同精度范围内的数据直接建立索引,把符合范围查询要求的数据聚合到一个索引上面,在搜索的时候把大的搜索范围拆分成很多小的范围索引,直接用term搜索就可以找到符合要求的所有文档。

    emmm…是不是有点抽象╮(╯_╰)╭

    比如想搜索在[300,500]内的文档并且事先已经把值在[300,400]之间的文档索引到了3[400,500]之间的文档索引到了4,那么就直接通过term查询取出34对应的文档id列表并且进行or操作就可以了,简单直接高效。

    range-query

    原理虽然简单,不过实现起来还是有些困难需要解决的。

    数字直接变成字符串的问题

    ES并没有直接把数字变成字符串,也没有对每个数字建立简单的索引,因为这两种做法可能会带来一些问题。

    字符串比较

    首先最大的问题是数字变成字符串之后如何进行比较,如果直接是把十进制的数字变成字符串,排序按照字典序(lexicographic)比较(默认所有的term都是按照字典序比较大小),会有不同位数比较的问题。比如搜索[423,642]内的文档,5也会被算在内,因为字典序"5""423"大,比"642"小。

    这个问题的一个解决方案是在每个数字变成字符串的时候在前面填充0,把5变成005,这样就能正确比较大小了,这也是旧版本的ES采用的解决方案。但是每次把int转化成string的时候要填充多少个0呢?太多了占空间,太少了又可能因为数字太长影响比较,比如最多只填充2个0,对于1000以下的数字没有问题,当数字大于1000了,个位数填充2个0就不够用了。

    获取的范围过多

    另一个问题是范围内的term过多带来的性能下降。比如现在有很多文档,其中索引的数字的列表为[421,423,445,446,448,521,522,632,633,634,641,642,644]一共13个term,如果我们想要查询[423,642]之间的所有的文档,需要取出一共11个term,然后用这些term去搜索对应的文档。

    es-docs

    当范围越来越大,需要的term的数量就越来越多,查询的性能就会不断下降。

    ES是怎么把数字变成字符串

    先来解决第一个问题,数字怎么变成字符串。

    十进制的数字有填充问题,如果变成了二进制,再进行词典序比较,不就没有问题了吗?Perfect,似乎问题完美的解决了。

    哥么你就没有考虑过负数的感受吗?

    sad

    二进制的int保证是32位,对于正数和正数的比较或者负数和负数的比较是没有问题的,可是正数和负数的比较就不行了。正数的最高位是0,负数的最高位是1,直接比较,负数永远大于正数。这个时候ES采用的方法是把正数最高位变成1,负数最高位变成0,这样正数用于大于负数,问题就解决了。

    int类型解决了,float呢?由于浮点数在Java中的表示方法,最高位符号位,2330位是指数位,022是尾数,如果直接把一个正的float当作int类型来比较好像也没有什么问题,指数位高的,当然大;指数相同,尾数大的也自然数字就大,所以正浮点型可以直接当成int转化。但是负数就不行了,指数越大,数字越小,尾数越大,数字也越小。ES给出的解决方案是直接对低31位每一位取反,1变成0,0变成1,这样负数的float就可以比较大小了。总结就是正float当int用,负float低31位取反后当成int用。

    对于long和double类型,也是同样的道理,只不过32位变成了64位。

    你以为就这样变成了二进制字符串了吗?不,还没有,没有这么简单。

    刚才是把int变成了二进制的字符串,一个字符只保存0和1不觉得浪费吗?一个int要用32个字符也就是32个字节保存,暴殄天物呀!

    Java的1个int占4个字节,1个char是2个字节,1个int用2个char不就行了。但是Java使用Unicode字符集来保存字符串,ES用UTF8编码保存Unicode字符,对于0~127使用1个字节,大于127一般2个字节,汉字通常3个字节,这样的话1个int用2个char表示,最多需要6个字节(这里int虽然不是汉字,但是在变成char之后有可能在Unicode字符集中表示某一个汉字)

    ES表示还能做的更好,上面不是说0~127只用一个字节吗?好,我就把int切分之后的大小限制在127以内(原来默认切分是4组8位的二进制数字)。127是7位二进制数字,int是32位的,那就把32位的int变成由4、7、7、7、7这5组二进制组成,最后这个字符串只需要5个字节就可以了,和上面的6个字节相比,空间利用率提高了17%

    transform

    数字的索引是什么样子

    上面说到的另外一个问题是查询term数量太多的问题,解决方案就是用空间换时间,通过前缀聚合部分的term来达到。

    这里的聚合的实现方式是采用trie的数据结构,比如445、446和448这个三个term,可以聚合到44这个term的下面,节点44包含的文档的id列表应该是所有子节点的并集,这样原先需要的11个term就可以减少2个。同理对于其他的term也进行合并,合并之后[423,642]查询就只需要6个term,效率提高了一倍

    然而聚合也是要讲道理的,把445、446和448聚合到44以及把44聚合到4相当于是把数字除以10,精度就是10。但是并不是一直都希望这个精度是10,也可以设置为100(精度相对应的降低,节约索引空间)等等。ES提供了precisionStep来定制化这个精度,不过不是针对十进制,而是二进制的位数。比如precisionStep设置为4,那么在二进制位里面每隔4位(相当于十进制的16)就建立一个前缀聚合索引。

    trie

    比如对于二进制数字0100 0011 0001 1010,当precisionStep为4的时候,会建立4个索引——0100 0011 0001 10100100 0011 00010100 0011以及0100(最高的4位),这四个索引相当于从trie的子节点一直到根节点

    精度越高(precisionStep越小)索引就越大,查询速度越快;精度越低,索引越小,相对查询速度比较慢。

    比如对于long类型的数据,precisionStep是4的时候,最多需要同时搜索465个term;precisionStep是2的时候,最多只要189个term。不过并不能绝对的说精度越高越好,因为查找这些term需要的时间也会相应增加。实际上最佳的precisionStep还是要根据业务情况测试得出。

    上面根据precisionStep建立索引的过程中有一个特殊的分词器来帮助拆分,比如把423拆成42342以及4。不过分词器会同样的把4拆分成4,那怎么区分423444呢?

    那就需要额外的空间来区分这两个4,ES给出的解决方案是在这两个数字前面加上一个前缀shift表示偏移量。比如4234shift242342shift1423shift0);而44shift0,所以前者的4比后者的要大。分词之后的term在每次比较之前都会先比较shiftshift越大,相应的term也越大,避免的重复的问题。

    总结上面建立索引的过程:当一个文档进来的时候,有一个数字423需要建立索引,于是先把这个int数据转化成字符串,再用一个特殊的分词器根据精度把423分成对应的三个term423424,并且附上对应的前缀shift,接下来在trie中找到这几个term,把稳定的id添加到这几个term的文档id列表里面(如果不存在就创建这个term)。

    查询原理

    清楚了数字类型的数据的索引机制之后,范围查询的原理就比较简单了。

    比如有一个范围[423, 642],要找到字段大于等于423并且小于等于642的文档。

    1. 先在索引的trie里面找到这两个term以及范围内的兄弟节点,分别是trie的两个叶节点423、641和642
    2. 从叶节点向上缩小范围,对两个数字分别除以10加一和减一之后查找范围为[43,63],此时的shift是1,得到这一层级的“叶子节点”以及范围内的兄弟节点是44和63
    3. 再从这一层向上,两个数字除以10,分别加一减一,得到范围[5,5]shift为2,这就是最后的节点了,term是5
    4. 上面三个步骤得到最后需要的term是423、44、5、63、641和642

    上面是用十进制举得一个例子,在二进制里面也是同样的道理,这里就不啰嗦了。实际上在ES实现里面用了很多位操作,效率相比于使用十进制要高很多,感兴趣的同学可以去看源代码,在LegacyNumericUtils类的splitRange方法里面。

    总结

    总的来说,Elasticsearch对于数字类型数据的索引和搜索不同于传统的MySQL或者Java等编程语言,采用了独特的字符串存储以及Trie数据结构保存索引的方式。

    ES先将输入的数字进行预处理,把float和double分别映射成int和long,原来是int和long类型的则保持不变

    然后把输入的整型数字切分成许多组由最长7位二进制数字组成的二进制串,每组二进制数字都是一个Unicode字符,整体连起来变成一个Unicode字符串

    接下来根据precisionStep把这个字符串数字分词成很多term,并附上前缀shift

    根据这些term建立索引词典,词典的结构类似于一个trie

    范围查询的时候根据trie把所谓的范围区间划分成离散的term字符串,这些term指向的文档的并集就是范围查询的结果

    One more thing

    谢谢各位大佬看完了这篇文章,在这里我很遗憾的通知您,以上提到的ES数字类型数据处理的方式已经被废弃了😓

    angry

    ES使用的搜索库Lucene在6.0版本以及以后为了解决多维空间位置搜索问题,改用新的数据结构——BKD树来实现位置搜索,带来了很大的性能提升。开发者发现这种实现也能用于一维数据搜索,于是用新的数据结构代替了现在的字符串的实现

    我会在以后专门写一篇文章介绍BKD树在ES中的应用

    参考

    The Evolution of Numeric Range Filters in Apache Lucene
    Numeric datatypes
    Class IntField
    Package org.apache.lucene.search
    Integer size vs Long size
    Lucene的数字范围搜索 (Numeric Range Query)原理
    Lucene 4 和 Solr 4 学习笔记(3)
    Class LegacyNumericRangeQuery
    LegacyNumericUtils
    Class NumericRangeQuerys

    展开全文
  • 一、基本类型包装类介绍 ...例如:通过文本框获得用户输入的数字数据,因为文本框里面书写文本数据的,所以后台得到的都字符串。如果想要对字符串中的数字进行运算,必须将字符串转换成数字。  怎么解决上述出...

    一、基本类型包装类介绍

      Java有8种基本类型byte,int,long,double,float,boolean,char,short,它们只能做赋值、运算、取值等简单操作,相对其进行复杂操作就不方便了。例如:通过文本框获得用户输入的数字数据,因为文本框里面是书写文本数据的,所以后台得到的都是字符串。如果想要对字符串中的数字进行运算,必须将字符串转换成数字。

      怎么解决上述出现的问题呢?

      为了对基本数据类型进行更多的操作,更方便的操作,Java就针对每一种基本数据类型值封装成了对象,8种基本数据类型封装后的对象称为包装类。将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。

    二、基本类型和对象类型

      基本类型与对象类型最大的不同点在于,基本类型基于数值,对象类型基于引用

      基本类型的变量在栈的局部变量表中直接存储的具体数值,而对象类型的变量则存储的是堆中的引用。

      显然,相对于基本类型的变量来说,对象类型的变量需要占用更多的内存空间

      上面说到,基本类型基于数值,所以基本类型是没有类而言的,是不存在类的概念的,也就是说,变量只能存储数值,而不具备操作数据的方法。对象类型则截然不同,变量实际上是某个类的实例,可以拥有属性方法等信息,不再单一的存储数值,可以提供各种各样对数值的操作方法,但代价就是牺牲一些性能并占用更多的内存空间。

      之所以 Java 里没有一刀切了基本类型,就是看在基本类型占用内存空间相对较小,在计算上具有高于对象类型的性能优势,当然缺点也是不言而喻的。

      所以一般都是结合两者在不同的场合下适时切换,那么 Java 中提供了哪些「包装类型」来弥补「基本类型」不具备面向对象思想的劣势呢?

       

      可以看到,除了 int 和 char 两者的包装类名变化有些大以外,其余六种基本类型对应的包装类名,都是大写了首字母而已。

      注意,所有的包装类都是final修饰的,也就是它们都是无法被继承和重写的。

      下面重点介绍Integer类和Character类,其它类型都是相似的。

    三、Integer类

      Integer 类在对象中包装了一个基本类型 int 的值。该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。

    3.1 构造方法

    • public Integer(int value)
    • public Integer(String s)  注意:这个字符串必须是由数字字符组成
    public class IntegerDemo {
        public static void main(String[] args) {
            // 方式1
            int i = 100;
            Integer ii = new Integer(i);
            System.out.println("ii:" + ii);
    
            // 方式2
            String s = "100";
            // NumberFormatException
            // String s = "abc";
            Integer iii = new Integer(s);
            System.out.println("iii:" + iii);
        }
    }

    3.2 int类型和String类型的相互转换

      int类型转成String类型:String.valueOf(number)

    // int -- String
    int number = 100;
    // 方式1
    String s1 = "" + number;
    System.out.println("s1:" + s1);
    // 方式2(推荐)
    String s2 = String.valueOf(number);
    System.out.println("s2:" + s2);
    // 方式3
    // int -- Integer -- String
    Integer i = new Integer(number);
    String s3 = i.toString();
    System.out.println("s3:" + s3);
    // 方式4
    // public static String toString(int i)
    String s4 = Integer.toString(number);
    System.out.println("s4:" + s4);

      String类型转成int类型:Integer.parseInt(s)

    // String -- int
    String s = "100";
    // String -- Integer -- int
    Integer ii = new Integer(s);
    // public int intValue()
    int x = ii.intValue();
    System.out.println("x:" + x);
    //方式2(推荐)
    //public static int parseInt(String s)
    int y = Integer.parseInt(s);
    System.out.println("y:"+y);

    3.3 常用的基本进制转换

    【十进制到二进制,八进制,十六进制】

    • public static String toBinaryString(int i)
    • public static String toOctalString(int i)
    • public static String toHexString(int i)
    // 十进制到二进制,八进制,十六进制
    System.out.println(Integer.toBinaryString(100));
    System.out.println(Integer.toOctalString(100));
    System.out.println(Integer.toHexString(100));
    System.out.println("-------------------------");

     【十进制到其他进制】

      public static String toString(int i,int radix):返回用第二个参数指定基数表示的第一个参数的字符串表示形式。

    // 十进制到其他进制
    System.out.println(Integer.toString(100, 10));
    System.out.println(Integer.toString(100, 2));
    System.out.println(Integer.toString(100, 8));
    System.out.println(Integer.toString(100, 16));
    System.out.println(Integer.toString(100, 5));
    System.out.println(Integer.toString(100, 7));
    System.out.println(Integer.toString(100, -7));
    System.out.println(Integer.toString(100, 70));
    System.out.println(Integer.toString(100, 1));
    System.out.println(Integer.toString(100, 17));
    System.out.println(Integer.toString(100, 32));
    System.out.println(Integer.toString(100, 37));
    System.out.println(Integer.toString(100, 36));
    System.out.println("-------------------------");

      由这个我们也看到了进制的范围:2-36

      为什么呢?0,...9,a...z

    【其他进制到十进制】

      public static int parseInt(String s,int radix):使用第二个参数指定的基数,将字符串参数解析为有符号的整数。

    //其他进制到十进制
    System.out.println(Integer.parseInt("100", 10));
    System.out.println(Integer.parseInt("100", 2));
    System.out.println(Integer.parseInt("100", 8));
    System.out.println(Integer.parseInt("100", 16));
    System.out.println(Integer.parseInt("100", 23));

    3.4 自动装、拆箱

      JDK5的新特性

      自动装箱:把基本类型转换为包装类类型

      自动拆箱:把包装类类型转换为基本类型

    public class IntegerDemo {
        public static void main(String[] args) {
            // 定义了一个int类型的包装类类型变量i
            // Integer i = new Integer(100);
            Integer ii = 100;
            ii += 200;
            System.out.println("ii:" + ii);
    
            // 通过反编译后的代码
            // Integer ii = Integer.valueOf(100); //自动装箱
            // ii = Integer.valueOf(ii.intValue() + 200); //自动拆箱,再自动装箱
            // System.out.println((new StringBuilder("ii:")).append(ii).toString());
        }
    }

      注意:在使用时,Integer x = null;上面的代码就会出现NullPointerException。

    3.5 缓冲池 

    public class IntegerDemo {
        public static void main(String[] args) {
            Integer i1 = new Integer(127);
            Integer i2 = new Integer(127);
            System.out.println(i1 == i2);//false
            System.out.println(i1.equals(i2));//true
            System.out.println("-----------");
    
            Integer i3 = new Integer(128);
            Integer i4 = new Integer(128);
            System.out.println(i3 == i4);//false
            System.out.println(i3.equals(i4));//true
            System.out.println("-----------");
    
            Integer i5 = 128;
            Integer i6 = 128;
            System.out.println(i5 == i6);
            System.out.println(i5.equals(i6));//false
            System.out.println("-----------");//true
    
            Integer i7 = 127;
            Integer i8 = 127;
            System.out.println(i7 == i8);//true
            System.out.println(i7.equals(i8));//true
    
            // 通过查看valueOf()的源码,我们就知道了,针对-128到127之间的数据,做了一个数据缓冲池,如果数据是该范围内的,每次并不创建新的空间
            // Integer ii = Integer.valueOf(127);
        }
    }

      注意:Integer的数据直接赋值,如果在-128到127之间,会直接从缓冲池里获取数据

    四、Character类

      Character 类在对象中包装一个基本类型 char 的值。此外,该类提供了几种方法,以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然

    4.1 构造方法

      Character(char value)

    public class CharacterDemo {
        public static void main(String[] args) {
            // 创建对象
            // Character ch = new Character((char) 97);
            Character ch = new Character('a');
            System.out.println("ch:" + ch);
        }
    }

    4.2 成员方法

    • public static boolean isUpperCase(char ch):判断给定的字符是否是大写字符
    • public static boolean isLowerCase(char ch):判断给定的字符是否是小写字符
    • public static boolean isDigit(char ch):判断给定的字符是否是数字字符
    • public static char toUpperCase(char ch):把给定的字符转换为大写字符
    • public static char toLowerCase(char ch):把给定的字符转换为小写字符
    public class CharacterDemo {
        public static void main(String[] args) {
            // public static boolean isUpperCase(char ch):判断给定的字符是否是大写字符
            System.out.println("isUpperCase:" + Character.isUpperCase('A'));
            System.out.println("isUpperCase:" + Character.isUpperCase('a'));
            System.out.println("isUpperCase:" + Character.isUpperCase('0'));
            System.out.println("-----------------------------------------");
            // public static boolean isLowerCase(char ch):判断给定的字符是否是小写字符
            System.out.println("isLowerCase:" + Character.isLowerCase('A'));
            System.out.println("isLowerCase:" + Character.isLowerCase('a'));
            System.out.println("isLowerCase:" + Character.isLowerCase('0'));
            System.out.println("-----------------------------------------");
            // public static boolean isDigit(char ch):判断给定的字符是否是数字字符
            System.out.println("isDigit:" + Character.isDigit('A'));
            System.out.println("isDigit:" + Character.isDigit('a'));
            System.out.println("isDigit:" + Character.isDigit('0'));
            System.out.println("-----------------------------------------");
            // public static char toUpperCase(char ch):把给定的字符转换为大写字符
            System.out.println("toUpperCase:" + Character.toUpperCase('A'));
            System.out.println("toUpperCase:" + Character.toUpperCase('a'));
            System.out.println("-----------------------------------------");
            // public static char toLowerCase(char ch):把给定的字符转换为小写字符
            System.out.println("toLowerCase:" + Character.toLowerCase('A'));
            System.out.println("toLowerCase:" + Character.toLowerCase('a'));
        }
    }

    【练习】

      统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数。(不考虑其他字符)

    /*
     * 分析:
     *         A:定义三个统计变量。
     *             int bigCont=0;
     *             int smalCount=0;
     *             int numberCount=0;
     *         B:键盘录入一个字符串。
     *         C:把字符串转换为字符数组。
     *         D:遍历字符数组获取到每一个字符
     *         E:判断该字符是
     *             大写    bigCount++;
     *             小写    smalCount++;
     *             数字    numberCount++;
     *         F:输出结果即可
     */
    public class CharacterTest {
        public static void main(String[] args) {
            // 定义三个统计变量。
            int bigCount = 0;
            int smallCount = 0;
            int numberCount = 0;
    
            // 键盘录入一个字符串。
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入一个字符串:");
            String line = sc.nextLine();
    
            // 把字符串转换为字符数组。
            char[] chs = line.toCharArray();
    
            // 遍历字符数组获取到每一个字符
            for (int x = 0; x < chs.length; x++) {
                char ch = chs[x];
    
                // 判断该字符
                if (Character.isUpperCase(ch)) {
                    bigCount++;
                } else if (Character.isLowerCase(ch)) {
                    smallCount++;
                } else if (Character.isDigit(ch)) {
                    numberCount++;
                }
            }
    
            // 输出结果即可
            System.out.println("大写字母:" + bigCount + "个");
            System.out.println("小写字母:" + smallCount + "个");
            System.out.println("数字字符:" + numberCount + "个");
        }
    }

     

     

     

    参考:https://www.cnblogs.com/yangming1996/p/8830305.html

    转载于:https://www.cnblogs.com/yft-javaNotes/p/10851717.html

    展开全文
  • 也许因为使用了Vim的缘故,当然,还有一个主要原因最近使用文本编辑器编写TXT文件频率很高,之后选择其他的文本编辑器,polaris都 会优先考虑对TXT文件类型的可配置性。这方面Vim一个很好的选择。前面说过,...
  • 可以先输入英文状态下的单引号,再输入数字可以先将单元格格式设置为文本,再输入数字2、以0补位工号一团乱,有一位数,两位数…,现需将工号补齐六位数,不足的以0补位,该怎么操作呢?选中要设置的单元格...
  • 可以先输入英文状态下的单引号,再输入数字可以先将单元格格式设置为文本,再输入数字2、以0补位工号一团乱,有一位数,两位数…,现需将工号补齐六位数,不足的以0补位,该怎么操作呢?选中要设置的单元格...
  • 文本格式的数字转换成真正的数字 设置页码 Excel表格里如何插入页码的? 如何设置页脚首页为第5页 表格的页脚问题 无拘无束的页眉 打印表头 Excel打印中如何不显示错误值符号 对于一些不可打印的字符的处理 用那个...
  • 33、数字转字符有多少种方式,分别什么 22 34、Java创建对象有几种方式 22 35、写出验证Email的正则表达式 22 39、说出十种常见的异常 22 40什么检查性异常和非检查性异常? 23 41、Java的异常处理机制什么? ...
  • EXCEL函数公式集

    热门讨论 2010-03-16 03:26:38
    文本格式的数字转换成真正的数字 设置页码 Excel表格里如何插入页码的? 如何设置页脚首页为第5页 表格的页脚问题 无拘无束的页眉 打印表头 Excel打印中如何不显示错误值符号 对于一些不可打印的字符的处理 用那个...
  • excel的使用

    2012-11-25 17:06:01
    (1) 分数的输入如果直接输入“1/5”,系统会将其变为“1月5日”,解决办法:先输入“0”,然后输入空格,再输入分数“1/5”。(2) 序列“001”的输入如果直接输入“001”,系统会自动判断001为数据1,解决办法...
  • 如果你使用 EditPlus 进行文本编辑,那么每次创建文本文件,编辑后保存时,尽管文件类型下拉列表中显示的是文本文件, EditPlus 还是询问你是否添加".txt"后缀,是不是很烦? 解决方法: ① 在程序目录建立一个空的...
  • 本章解决数据内容的安全性,介绍Java的加密和解密技术。学完该章可以通过Java编程对各种数据进行各种形式的加密。密码学也安全机制的基础。 第三章 解决的主要问题——和源代码相关的安全性 编写好的程序给...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    6.2 显式类型转换 .53 6.3 小 结 .56 第七章 表 达 式 .58 7.1 操 作 符 .58 7.2 算术操作符和算术表达式.59 7.3 赋值操作符和赋值表达式.64 7.4 关系操作符和关系表达式.65 <<page 2>> page begin...
  • C#微软培训资料

    2014-01-22 14:10:17
    6.2 显式类型转换 .53 6.3 小 结 .56 第七章 表 达 式 .58 7.1 操 作 符 .58 7.2 算术操作符和算术表达式.59 7.3 赋值操作符和赋值表达式.64 7.4 关系操作符和关系表达式.65 <<page 2>> page begin...
  •  所有的ASCII码都可以用“\”加数字(一般8进制数字)来表示。而C中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了...
  • wxPython学习手册

    热门讨论 2014-04-19 08:01:58
    2.8 一些最常见的错误现象及解决方法? 48 2.9 总结 49 3、在事件驱动环境中工作 51 3.1 要理解事件,我们需要知道哪些术语? 51 3.2 什么事件驱动编程? 52 3.2.1 编写事件处理器 54 3.2.2 设计事件驱动程序 55 ...
  • 2.26 一些结构声明中的这些冒号和数字是什么意思? 2.27 为什么人们那么喜欢用显式的掩码和位操作而不直接声明位域? 第3章 表达式 求值顺序 3.1 为什么这样的代码不行?a[i]=i++; 3.2 使用我的编译器,下面的...
  • 事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找这个数组的某一项的值多少的问题。 2.13总结回顾 37 ...
  • 事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找这个数组的某一项的值多少的问题。 2.13总结回顾 37 ...
  • 大话数据结构

    2019-01-10 16:35:22
    事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找这个数组的某一项的值多少的问题。 2.13总结回顾 37 ...
  • 大话数据结构 程杰

    2018-09-01 10:06:43
    事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找这个数组的某一项的值多少的问题。 2.13总结回顾 37 ...
  • 2.26 一些结构声明中的这些冒号和数字是什么意思? 31 2.27 为什么人们那么喜欢用显式的掩码和位操作而不直接声明位域? 32 第3章 表达式 33 求值顺序 33 3.1 为什么这样的代码不行?a[i]= i++; 33 3.2 ...
  • 2.11 最坏情况与平均情况 35 2.12 算法空间复杂度 36 事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找...
  • 光标移到标题下,选“插入︱交叉引用”,“引用类型”为“书签”,点“参考文献文本”后插入,这样就把参考文献文本复制了一份。选中刚刚插入的文本,按格式要求修改字体字号等,并用项目编号进行自动编号。 到这里...
  • 《你必须知道的495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    2.26 一些结构声明中的这些冒号和数字是什么意思? 31 2.27 为什么人们那么喜欢用显式的掩码和位操作而不直接声明位域? 32 第3章 表达式 33 求值顺序 33 3.1 为什么这样的代码不行?a[i]= i++; 33 3.2 ...
  • 6.4.4 unicode怎么样? 6.4.5 truetype和大字体 6.5 插入符(不是光标) 6.5.l 插入符函数 6.5.2 typer 程序 第七章 鼠标 7.1 鼠标基础 7.1.1 一些简单的定义 7.2 客户区鼠标消息 7.2.l ...
  • 大话数据结构-程杰

    2014-07-13 23:45:52
    事先建立一个有2050大的数组,然后把所有年份按下标数字对应,如果闰年,此数组项的值就是1,如果不是就是0。这样,所谓的判断某一年是否闰年就变成了查找这个数组的某一项的值多少的问题。 2.13 总结回顾 37...

空空如也

空空如也

1 2 3
收藏数 44
精华内容 17
关键字:

怎么解决数字是文本类型