起因:忽然想到平时用的HashMap 当key是字符串的时候为什么总可以覆盖,然后看了String的源码发现:

private final char value[];
private int hash; // Default to 0

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;
}

equals方法:

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

很显然hashCode和eques方法都是根据char[]数组中的char判断的,但是hashCode函数里面为什么是

h = 31 * h + val[i];这个数字为什么选择31吶,引起了我的兴趣。

下面是知乎上的回答:

The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization automatically.

设计者选择 31 这个值是因为它是一个奇质数。如果它是一个偶数,在使用乘法当中产生数值溢出时,原有数字的信息将会丢失,因为乘以二相当于位移。
选择质数的优势不是那么清晰,但是这是一个传统。31 的一个优良的性质是:乘法可以被位移和减法替代: 31 * i == (i << 5) - i
现代的 VM 可以自行完成这个优化。
As Goodrich and Tamassia point out, If you take over 50,000 English words (formed as the union of the word lists
provided in two variants of Unix), using the constants 31, 33, 37, 39, and 41 will produce less than 7 collisions
in each case. Knowing this, it should come as no surprise that many Java implementations choose one of these constants.
Coincidentally, I was in the middle of reading the section "polynomial hash codes" when I saw this question.
正如 Goodrich 和 Tamassia 指出的那样,如果你使用 31,33, 37,39 和 41 这几个数值,将其应用于 hashCode 的算法中,每一个数字对超过
50000 个英语单词(由两个 Unix 版本的字典的并集构成)产生的 hash 只会产生少于 7 个的冲突。知道了这个之后,Java 大多数的发行版均会使用这几个
数值之一的事实对你也不会显得奇怪了。巧合的是,我是在阅读『多项式哈希值』这一个章节的时候看到这个问题的。

可是为什么java可以s="abcd"这样直接赋值吶?难道和c语言里面的重载一样吗?

但是否定的:

因为 从语言一级来看,java不支持运算符重载,这点是肯定的。

String类的”=”,”+”,”+=”,看似运算符重载,实际不是,只是在java编译器里做了一点手脚。 
java编译器对String的运算符做了特殊处理。

例如:
String s = “a”; 
s += “b”; 
编译器转换成了: 
String s = “a”; 
s = (new StringBuilder()).append(s).append(“b”).toString();

HashSet: 继承的AbstractSet内

public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}

Integer:

public int hashCode() {
return hashCode(this.value);
} public static int hashCode(int var0) {
return var0;
}

Double:

public int hashCode() {
return hashCode(this.value);
} public static int hashCode(double var0) {
long var2 = doubleToLongBits(var0);
return (int)(var2 ^ var2 >>> 32);
}

>>:带符号右移。正数右移高位补0,负数右移高位补1

>>>:无符号右移。无论是正数还是负数,高位通通补0。

下面是关于hashCode的一些解释:

Hash是散列的意思,就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。关于散列值,有以下几个关键结论:

1、如果散列表中存在和散列原始输入K相等的记录,那么K必定在f(K)的存储位置上

2、不同关键字经过散列算法变换后可能得到同一个散列地址,这种现象称为碰撞

3、如果两个Hash值不同(前提是同一Hash算法),那么这两个Hash值对应的原始输入必定不同

HashCode

然后讲下什么是HashCode,总结几个关键点:

1、HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的

2、如果两个对象equals相等,那么这两个对象的HashCode一定也相同

3、如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写

4、如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置

String 源码探究的更多相关文章

  1. spring-cloud-sleuth+zipkin源码探究

    1. spring-cloud-sleuth+zipkin源码探究 1.1. 前言   粗略看了下spring cloud sleuth core源码,发现内容真的有点多,它支持了很多类型的链路追踪, ...

  2. spring-boot-2.0.3之quartz集成,数据源问题,源码探究

    前言 开心一刻 着火了,他报警说:119吗,我家发生火灾了. 119问:在哪里? 他说:在我家. 119问:具体点. 他说:在我家的厨房里. 119问:我说你现在的位置. 他说:我趴在桌子底下. 11 ...

  3. Vue源码探究-全局API

    Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...

  4. Vue源码探究-事件系统

    Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...

  5. Vue源码探究-状态初始化

    Vue源码探究-状态初始化 Vue源码探究-源码文件组织 Vue源码探究-虚拟DOM的渲染 本篇代码位于vue/src/core/instance/state.js 继续随着核心类的初始化展开探索其他 ...

  6. SpringBoot读取配置文件源码探究

    1. SpringBoot读取配置文件源码探究 1.1. 概览 springboot的源码是再原来的Spring源码上又包了一层,看过spring源码都知道,当我们从入口debug进去的时候,原来的S ...

  7. @Async源码探究

    1. @Async源码探究 1.1. 上代码 @SpringBootApplication @EnableAsync public class SpringbootLearnApplication { ...

  8. Vue源码探究-数据绑定的实现

    Vue源码探究-数据绑定的实现 本篇代码位于vue/src/core/observer/ 在总结完数据绑定实现的逻辑架构一篇后,已经对Vue的数据观察系统的角色和各自的功能有了比较透彻的了解,这一篇继 ...

  9. Vue源码探究-虚拟DOM的渲染

    Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...

随机推荐

  1. 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第4章编程练习3

    #include <iostream>#include <cstring>using namespace std;int main(){ char fname[20]; cha ...

  2. Java中Date, Calendar, SimpleDateFormat的相互转换

    import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...

  3. django之模型层(model)--多表相关操作(图书管理小练习)

    前面几篇随笔的数据库增删改查操作都是在单表的操作上的,然而现实中不可能都是单表操作,更多的是多表操作,一对一,一对多,多对多的表结构才是我们经常需要处理的,本篇将带我们了解多表操作的一些相关操作.也会 ...

  4. ACM-ICPC 2018 南京赛区网络预赛 E题

    ACM-ICPC 2018 南京赛区网络预赛 E题 题目链接: https://nanti.jisuanke.com/t/30994 Dlsj is competing in a contest wi ...

  5. Java中定义常量方法及建议(Class/Interface)

    Class定义常量方法(推荐方法) //final修饰符 public final class Constants { //私有构造方法 private Constants() {} public s ...

  6. CSS魔法堂:一起玩透伪元素和Content属性

    前言  继上篇<CSS魔法堂:稍稍深入伪类选择器>记录完伪类后,我自然而然要向伪元素伸出"魔掌"的啦^_^.本文讲讲述伪元素以及功能强大的Contet属性,让我们可以通 ...

  7. .Net转Java.05.为啥MySQL没有nolock

    今天忽然想到一个问题,原来为了提高SQL Server性能,公司规定查询语句一般都要加 WITH (NOLOCK)的 现在转Java了,用了MySQL为啥不提这个事情了? 先在MySQL里写了一个查询 ...

  8. iOS:如何实现在文字上添加拼音

    一.介绍 最近项目有一个需求,需要给朗诵的文字添加对应的拼音,而且要求使用原生的控件实现.一开始听到这个需求挺懵逼的,感觉有点难.后来,静下来想一下,其实还是可以实现的,无非就是自定义了.下面,就来说 ...

  9. kettle 6.1 按时间循环增量抽取数据

    场景:假设有一张表数据量很大,需要按一个时间来循环增量抽取 方法:主要是通过JOB自身调用,实现循环调用,类似于 函数自调用 的循环. 1.JOB全图: 2.获取增量时间,并设置增量时间环境变量 3. ...

  10. vs 2017 community中文版下载地址

    https://my.visualstudio.com/Downloads?pid=2190 SHA1: 109C6646A79844D8116DADB293A0B64754363C69 File n ...