差点忘了最常用的String类型,我们对String的大多数方法都已经很熟了,这里就挑几个平时不会直接接触的点来解析一下。

先来看看它的成员变量

public final class String  {
private final char value[];
private int hash; // Default to 0
}
  • string的内容其实就是一个char数组;
  • hash字段缓存了string的哈希值,因为string经常作为hashmap的key,这样能提高性能;

hashCode

public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

String的哈希值采用一种延迟计算的策略,计算方法很简单,就是把每个char都当做int,通过公式h=31*h+char的计算最终值。由于String是不可变对象,在生命周期内,hash值只需要计算一次。

subString

public String substring(int beginIndex, int endIndex) {
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
public String(char value[], int offset, int count) {
this.value = Arrays.copyOfRange(value, offset, offset+count);
}

注:上面的代码为了节省空间,省略了异常检查逻辑

subString会创建一个全新的字符串,而不是共享原字符串的char数组。按道理讲,由于String是不可变的,那么subString和原string共享char数组是安全的,可能是出于其他方面的考虑:虽然节省了一点空间,但是需要额外增加一个offset和size成员,整体效率未必更佳。

码点(CodePoint)

简单介绍一下码点和码元的概念,一个码点是某种字符编码方案里面,某个字符对应的绝对编码值。而码元则是指具体的字符存储方案下,最小的存储单元。

对java的String来说,存储的是unicode字符,使用的编码方案是utf-16,码元是char(16bit); utf-16的大部分码点用一个char足够了,但是有少部分需要两个char。

我们来看一段从码点序列创建String的代码:

public String(int[] codePoints, int offset, int count) {
final int end = offset + count; // Pass 1: Compute precise size of char[]
int n = count;
for (int i = offset; i < end; i++) {
int c = codePoints[i];
if (Character.isBmpCodePoint(c))
continue;
else if (Character.isValidCodePoint(c))
n++;
else throw new IllegalArgumentException(Integer.toString(c));
} // Pass 2: Allocate and fill in char[]
final char[] v = new char[n]; for (int i = offset, j = 0; i < end; i++, j++) {
int c = codePoints[i];
if (Character.isBmpCodePoint(c))
v[j] = (char)c;
else
Character.toSurrogates(c, v, j++);
} this.value = v;
}
  • 同样,上面的代码删除了异常处理逻辑;

  • 请注意,码点用int来表示

  • 第一个循环,计算码点数组codePoints,需要多少长度的char数组:

    • Character.isBmpCodePoint 判断是否一个基本多文种平面码点,这样的码点只需1个char
    • 否则需要两个码点,长度+1
  • 第二个循环,把码点值经过转换存入char数组

    • 如果是基本码点,直接放入
    • 如果是非基本码点,需要做一个计算,生成两个char

因此,如果你要把String当做一段实际的文本,并处理当中的单个文字,通过遍历char的方式是不行的,而要使用codePoint相关方法。关于编码相关的知识,不是本文要讲的内容,大家自行查阅资料。

java基础类型源码解析之String的更多相关文章

  1. java基础类型源码解析之HashMap

    终于来到比较复杂的HashMap,由于内部的变量,内部类,方法都比较多,没法像ArrayList那样直接平铺开来说,因此准备从几个具体的角度来切入. 桶结构 HashMap的每个存储位置,又叫做一个桶 ...

  2. java集合类型源码解析之ArrayList

    前言 作为一个老码农,不仅要谈架构.谈并发,也不能忘记最基础的语言和数据结构,因此特开辟这个系列的文章,争取每个月写1~2篇关于java基础知识的文章,以温故而知新. 如无特别之处,这个系列文章所使用 ...

  3. Java基础——集合源码解析 List List 接口

    今天我们来学习集合的第一大体系 List. List 是一个接口,定义了一组元素是有序的.可重复的集合. List 继承自 Collection,较之 Collection,List 还添加了以下操作 ...

  4. java集合类型源码解析之PriorityQueue

    本来第二篇想解析一下LinkedList,不过扫了一下源码后,觉得LinkedList的实现比较简单,没有什么意思,于是移步PriorityQueue. PriorityQueue通过数组实现了一个堆 ...

  5. 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理

    一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...

  6. Java 8 ThreadLocal 源码解析

    Java 中的 ThreadLocal是线程内的局部变量, 它为每个线程保存变量的一个副本.ThreadLocal 对象可以在多个线程中共享, 但每个线程只能读写其中自己的副本. 目录: 代码示例 源 ...

  7. Java泛型底层源码解析-ArrayList,LinkedList,HashSet和HashMap

    声明:以下源代码使用的都是基于JDK1.8_112版本 1. ArrayList源码解析 <1. 集合中存放的依然是对象的引用而不是对象本身,且无法放置原生数据类型,我们需要使用原生数据类型的包 ...

  8. Java集合---LinkedList源码解析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  9. Java线程池源码解析

    线程池 假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率.线程池能够对线程进行统一的分 ...

随机推荐

  1. jQuery 事件介绍

    什么是事件?页面对不同访问者的响应叫做事件.事件处理程序指的是当 HTML 中发生某些事件时所调用的方法. 常用的时间主要有以下几种: click()事件:click() 方法是当按钮点击事件被触发时 ...

  2. H5之拖拽

    步骤: 1.为将要拖拽的元素设置允许拖拽,并赋予dragstart事件将其id转换成数据保存: 2.为容器添加dragover属性添加事件阻止浏览器默认事件,允许元素放置,并赋予drop事件进行元素的 ...

  3. Java 之 File类(文件操作)

    一.概述 java.io.File 类是文件和目录路径名册抽象表示,主要用于文件和目录的创建.查找和删除等操作. File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法. 路径问题: ...

  4. S3C2440 OpenJtag

    C:\Users\Administrator\Desktop>oflash.exe leds.bin +--------------------------------------------- ...

  5. Spring Boot 揭秘与实战之RabbitMQ

    Spring Boot 整合 RabbitMQ Spring Boot 整合 RabbitMQ 是非常容易,只需要两个步骤. 首先,在 pom.xml 中增加 RabbitMQ 依赖. <dep ...

  6. 【robotframework】robotframework基本使用

    一.创建项目 1.创建测试项目 选择菜单栏 file----->new Project Name 输入项目名称:Type 选择 Directory. 2.创建测试套件 右键点击“测试项目”选择 ...

  7. Gitlab CI/CD任务一直处于pending

    在注册Runner时候这里输入了tag,这里指的是runner的标签,可以设置多个  ,分别用 ,号分割 .gitlab-ci.yml文件中 stages: - pull - package - bu ...

  8. Java基本知识点o(1), o(n), o(logn), o(nlogn)的了解

    在描述算法复杂度时,经常用到o(1), o(n), o(logn), o(nlogn)来表示对应算法的时间复杂度, 这里进行归纳一下它们代表的含义: 这是算法的时空复杂度的表示.不仅仅用于表示时间复杂 ...

  9. c++ vector容器基本用法

    基本用法 #include<iostream> #include<string> #include<vector> using namespace std; cla ...

  10. jquery头部高亮显示方法

    做网站都有头部导航栏,但导航栏的高亮显示一般都是后台技术人员做传参判断来显示高亮样式,今天来教大家一个用jquery来显示 废话不多说,直接上代码 第一步,在需要的页面中写入 <input ty ...