导航:首页 > 编程语言 > 一致性hashjava

一致性hashjava

发布时间:2022-04-12 11:59:31

java hashcode相同,equals一定为真equals为真,hashcode不一定为真

hashcode()和equals(object
o)方法是java所有类的基类object类中的方法,所有类都继承了object类以及其中的方法。equals与hashcode的定义必须一致,即:如果x.equals(y)返回true,那么x.hashcode()就必须与y.hashcode()具有相同的值。当然,对于两个不同的对象,x.hashcode()与y.hashcode()基本不会相同(不排除极少数情况会相同)。
在基类object类中,默认的equals方法是判断两个对象是否具有相同的引用,如果具有相同的引用,那它们必然是相等的。不过这种判断方法并不是一直都通用,比如:有的时候,两辆汽车,只要型号配置相同,就可以认为是相等的,出厂日期可以忽略不计。这种时候就需要在你的类中重写equals方法,对于这个类用你自己的方法来判断两个对象是否相等(重写equals方法就必须重新定义hashcode()方法)。
注:如果对两个对象进行==操作,那么就会自动调用object类默认的equals方法来进行比较。希望能帮到你~

⑵ 如何正确实现Java中的hashCode方法

正确实现Java中的hashCode方法:
相等和哈希码

相等是从一般的方面来讲,哈希码更加具有技术性。如果我们在理解方面存在困难,我们可以说,他们通过只是一个实现细节来提高了性能。
大多数的数据结构通过equals方法来判断他们是否包含一个元素,例如:
List<String> list = Arrays.asList("a", "b", "c");
boolean contains = list.contains("b");

这个变量contains结果是true,因为,虽然”b”是不相同的实例(此外,忽略字符串驻留),但是他们是相等的。
通过比较实例的每个元素,然后将比较结果赋值给contains是比较浪费的,虽然整个类的数据结构进行了优化,能够提升性能。
他们通过使用一种快捷的方式(减少潜在的实例相等)进行比较,从而代替通过比较实例所包含的每个元素。而快捷比较仅需要比较下面这些方面:
快捷方式比较即通过比较哈希值,它可以将一个实例用一个整数值来代替。哈希码相同的实例不一定相等,但相等的实例一定具有有相同的哈希值。(或应该有,我们很快就会讨论这个)这些数据结构经常通过这种这种技术来命名,可以通过Hash来识别他们的,其中,HashMap是其中最着名的代表。
它们通常是这样这样运作的
当添加一个元素,它的哈希码是用来计算内部数组的索引(即所谓的桶)
如果是,不相等的元素有相同的哈希码,他们最终在同一个桶上并且捆绑在一起,例如通过添加到列表。
当一个实例来进行contains操作时,它的哈希码将用来计算桶值(索引值),只有当对应索引值上存在元素时,才会对实例进行比较。
因此equals,hashCode是定义在Object类中。
散列法的思想
如果hashCode作为快捷方式来确定相等,那么只有一件事我们应该关心:相等的对象应该具有相同的哈希码,这也是为什么如果我们重写了equals方法后,我们必须创建一个与之匹配的hashCode实现的原因!
否则相等的对象是可能不会有相同的哈希码的,因为它们将调用的是Object's的默认实现。
HashCode 准则
引用自官方文档
hashCode通用约定:
* 调用运行Java应用程序中的同一对象,hashCode方法必须始终返回相同的整数。这个整数不需要在不同的Java应用程序中保持一致。
* 根据equals(Object)的方法来比较,如果两个对象是相等的,两个对象调用hashCode方法必须产生相同的结果。
* 根据equals(Object)的方法是比较,如果两个对象是不相等的,那么两个对象调用hashCode方法并不一定产生不同的整数的结果。但是,程序员应该意识到给不相等的对象产生不同的整数结果将有可能提高哈希表的性能。
第一点反映出了相等的一致性属性,第二个就是我们上面提出的要求。第三个阐述了一个重要的细节,我们将在稍后讨论。
HashCode实现
下面是非常简单的Person.hashCode的实现
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}

person’s是通过多个字段结合来计算哈希码的。都是通过Object的hash函数来计算。
选择字段
但哪些字段是相关的吗?需求将会帮助我们回答这个问题:如果相等的对象必须具有相同的哈希码,那么计算哈希码就不应包括任何不用于相等检查的字段。(否则两个对象只是这些字段不同但是仍然有可能会相等,此时他们这两个对象哈希码却会不相同。)
所以用于哈希组字段应该相等时使用的字段的子集。默认情况下都使用相同的字段,但有一些细节需要考虑。
一致性
首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。
正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。
这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。
结论:最好不要使用可变字段计算哈希码!
性能
哈希码最终计算的频率与可能调用equals差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals相比,优化之后又更大的上升空间。
除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以Lists和sets为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。
如果性能是至关重要的,使用Objects.hash因为需要为varargs创建一个数组也许并不是最好的选择。但一般规则优化是适用的:不要过早地使用一个通用的散列码算法,也许需要放弃集合,只有优化分析显示潜在的改进。
碰撞
总是关注性能,这个实现怎么呢?
@Override
public int hashCode() {
return 0;
}

快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段!
但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用contains将会触发对整个list线性扫描。
我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。
怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。
防止碰撞的另一部分是使用实际计算散列的算法。
计算Hsah
最简单的方法来计算一个字段的哈希码是通过直接调用hashCode,结合的话会自动完成。常见的算法是首先在以任意数量的数值(通常是基本数据类型)反复进行相乘操作再与字段哈希码相加
int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;

这可能导致溢出,但是不是特别有问题的,因为他们并没有产生Java异常。
注意,即使是非常良好的的哈希算法也可能因为输入特定的模式的数据有导致频繁碰撞。作为一个简单的例子假设我们会计算点的散列通过增加他们的x和y坐标。当我们处理f(x) = -x线上的点时,线上的点都满足:x + y == 0,将会有大量的碰撞。
但是:我们可以使用一个通用的算法,只到分析表明并不正确,才需要对哈希算法进行修改。
总结
我们了解到计算哈希码就是压缩相等的一个整数值:相等的对象必须有相同的哈希码,而出于对性能的考虑:最好是尽可能少的不相等的对象共享相同的哈希码。
这就意味着如果重写了equals方法,那么就必须重写hashCode方法
当实现hashCode
使用与equals中使用的相同的字段(或者equals中使用字段的子集)
最好不要包含可变的字段。
对集合不要考虑调用hashCode
如果没有特殊的输入特定的模式,尽量采用通用的哈希算法
记住hashCode性能,所以除非分析表明必要性,否则不要浪费太多的精力。

⑶ java中的equals,hashcode的区别和联系

equals
publicbooleanequals(Objectobj)
指示其他某个对象是否与此对象“相等”。
equals方法在非空对象引用上实现相等关系:
自反性:对于任何非空引用值x,x.equals(x)都应返回
true。
对称性:对于任何非空引用值x和y,当且仅当
y.equals(x)返回true时,x.equals(y)才应返回
true。
传递性:对于任何非空引用值x、y和z,如果
x.equals(y)返回true,并且y.equals(z)返回
true,那么x.equals(z)应返回true。
一致性:对于任何非空引用值x和y,多次调用
x.equals(y)始终返回true或始终返回false,前提是对象上
equals比较中所用的信息没有被修改。
对于任何非空引用值x,x.equals(null)都应返回
false。
Object类的equals方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值
x和y,当且仅当x和y引用同一个对象时,此方法才返回true(x==y具有值true)。
注意:当此方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

参数:
obj-要与之比较的引用对象。
返回:
如果此对象与obj参数相同,则返回true;否则返回false。
另请参见:
hashCode(),Hashtable
hashCode
publicinthashCode()
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。
hashCode的常规协定是:
在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行
equals比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据equals(Object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用
hashCode方法都必须生成相同的整数结果。
如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由Object类定义的hashCode
方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是JavaTM编程语言不需要这种实现技巧。)

返回:
此对象的一个哈希码值。
另请参见:
equals(java.lang.Object),
Hashtable

也就是说equals和hashCode的关系只是在于一个协定,equals默认判断依据是对象是否相等,hashCode()得到对象的内存地址的一个特殊计算得到的值,协定内容是:equals相等的对象的hashCode值相等,所以要求重写了equals之后重写hashCode。

equals是判断对象是否相等的方法。hashCode是得到对象hash值的方法,对象hash值默认是根据内存地址计算得到。equals默认表的是对象内存地址。

⑷ java中Hashtable和HashMap的区别分析

1 HashMap不是线程安全的

hastmap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value,而hashtable不允许。

2 HashTable是线程安全的一个Collection。

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

public static void main(String args[])
{
HashTable h=new HashTable();
h.put("用户1",new Integer(90));
h.put("用户2",new Integer(50));
h.put("用户3",new Integer(60));
h.put("用户4",new Integer(70));
h.put("用户5",new Integer(80));
Enumeration e=h.elements();
while(e.hasMoreElements()){
System.out.println(e.nextElement());
}

map 的方法:

clear()从 Map 中删除所有映射

remove(Object key)从 Map 中删除键和关联的值

put(Object key, Object value)将指定值与指定键相关联

get(Object key)返回与指定键关联的值

containsKey(Object key)如果 Map 包含指定键的映射,则返回 true

containsValue(Object value)如果此 Map 将一个或多个键映射到指定值,则返回 true

isEmpty()如果 Map 不包含键-值映射,则返回 true size()返回 Map 中的键-值映射的数目

这些都代表了Java中的集合,这里主要从其元素是否有序,是否可重复来进行区别记忆,以便恰当地使用,当然还存在同步方面的差异,见上一篇相关文章。

有序否

允许元素重复否

Collection





List





Set

AbstractSet





HashSet

TreeSet

是(用二叉树排序)

Map

AbstractMap



使用key-value来映射和存储数据,Key必须惟一,value可以重复

HashMap

TreeMap

是(用二叉树排序)

List 接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList。你可以将任何东西放到一个List容器中,并在需要时从中取出。ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快,而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。在具体应用时可以根据需要自由选择。前面说的Iterator只能对容器进行向前遍历,而 ListIterator则继承了Iterator的思想,并提供了对List进行双向遍历的方法。

Set接口也是 Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSet和TreeSet类。HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用类:Collections和 Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。

Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。Map有两种比较常用的实现: HashMap和TreeMap。HashMap也用到了哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub (Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。

⑸ java分布式架构有哪些技术

既然是分布式系统,系统间通信的技术就不可避免的要掌握。

首先,我们必须掌握一些基本知识,例如网络通信协议(例如TCP / UDP等),网络IO(Blocking-IO,NonBlocking-IO,Asyn-IO),网卡(多队列等)。 了解有关连接重用,序列化/反序列化,RPC,负载平衡等的信息。

在学习了这些基本知识之后,您基本上可以在分布式系统中编写一个简单的通信模块,但这实际上还远远不够。 现在,您已经进入了分布式字段,您已经对规模有很多要求。 这意味着需要一种通信程序,该程序可以支持大量连接,高并发性和低资源消耗。

大量的连接通常会有两种方式:

大量client连一个server

当前在NonBlocking-IO非常成熟的情况下,支持大量客户端的服务器并不难编写,但是在大规模且通常是长连接的情况下,有一点需要特别注意 ,即服务器挂起时不可能所有客户端都在某个时间点启动重新连接。 那基本上是一场灾难。 我见过一些没有经验的类似案例。 客户端规模扩大后,服务器基本上会在重新启动后立即刷新。 大量传入连接中断(当然,服务器的积压队列首先应设置为稍大一些)。 可以使用的通常方法是在客户端重新连接之前睡眠一段随机的时间。 另外,重连间隔采用避让算法。

一个client连大量的server

有些场景也会出现需要连大量server的现象,在这种情况下,同样要注意的也是不要并发同时去建所有的连接,而是在能力范围内分批去建。

除了建连接外,另外还要注意的地方是并发发送请求也同样,一定要做好限流,否则很容易会因为一些点慢导致内存爆掉。

这些问题在技术风险上得考虑进去,并在设计和代码实现上体现,否则一旦随着规模上去了,问题一时半会还真不太好解。

高并发这个点需要掌握CAS、常见的lock-free算法、读写锁、线程相关知识(例如线程交互、线程池)等,通信层面的高并发在NonBlocking-IO的情况下,最重要的是要注意在整体设计和代码实现上尽量减少对io线程池的时间占用。

低资源消耗这点的话NonBlocking-IO本身基本已经做到。

伸缩性

分布式系统基本上意味着规模不小。 对于此类系统,在设计时必须考虑可伸缩性。 在体系结构图上绘制的任何点,如果请求量或数据量继续增加,该怎么办? 通过添加机器来解决。 当然,此过程不需要考虑无限的情况。 如果您有经验的建筑师,从相对较小的规模到非常大型的范围,那么优势显然并不小,而且它们也将越来越稀缺。 。

横向可扩展性(Scale Out)是指通过增加服务器数量来提高群集的整体性能。 垂直可伸缩性(Scale Up)是指提高每台服务器的性能以提高集群的整体性能。 纵向可扩展性的上限非常明显,而分布式系统则强调水平可伸缩性。

分布式系统应用服务最好做成无状态的

应用服务的状态是指运行时程序因为处理服务请求而存在内存的数据。分布式应用服务最好是设计成无状态。因为如果应用程序是有状态的,那么一旦服务器宕机就会使得应用服务程序受影响而挂掉,那存在内存的数据也就丢失了,这显然不是高可靠的服务。把应用服务设计成无状态的,让程序把需要保存的数据都保存在专门的存储上(eg. 数据库),这样应用服务程序可以任意重启而不丢失数据,方便分布式系统在服务器宕机后恢复应用服务。

伸缩性的问题围绕着以下两种场景在解决:

无状态场景

对于无状态场景,要实现随量增长而加机器支撑会比较简单,这种情况下只用解决节点发现的问题,通常只要基于负载均衡就可以搞定,硬件或软件方式都有;

无状态场景通常会把很多状态放在db,当量到一定阶段后会需要引入服务化,去缓解对db连接数太多的情况。

有状态场景

所谓状态其实就是数据,通常采用Sharding来实现伸缩性,Sharding有多种的实现方式,常见的有这么一些:

2.1 规则Sharding

基于一定规则把状态数据进行Sharding,例如分库分表很多时候采用的就是这样的,这种方式支持了伸缩性,但通常也带来了很复杂的管理、状态数据搬迁,甚至业务功能很难实现的问题,例如全局join,跨表事务等。

2.2 一致性Hash

一致性Hash方案会使得加机器代价更低一些,另外就是压力可以更为均衡,例如分布式cache经常采用,和规则Sharding带来的问题基本一样。

2.3 Auto Sharding

Auto Sharding的好处是基本上不用管数据搬迁,而且随着量上涨加机器就OK,但通常Auto Sharding的情况下对如何使用会有比较高的要求,而这个通常也就会造成一些限制,这种方案例如HBase。

2.4 Copy

Copy这种常见于读远多于写的情况,实现起来又会有最终一致的方案和全局一致的方案,最终一致的多数可通过消息机制等,全局一致的例如zookeeper/etcd之类的,既要全局一致又要做到很高的写支撑能力就很难实现了。

即使发展到今天,Sharding方式下的伸缩性问题仍然是很大的挑战,非常不好做。

上面所写的基本都还只是解决的方向,到细节点基本就很容易判断是一个解决过多大规模场景问题的架构师,:)

稳定性

作为分布式系统,必须要考虑清楚整个系统中任何一个点挂掉应该怎么处理(到了一定机器规模,每天挂掉一些机器很正常),同样主要还是分成了无状态和有状态:

无状态场景

对于无状态场景,通常好办,只用节点发现的机制上具备心跳等检测机制就OK,经验上来说无非就是纯粹靠4层的检测对业务不太够,通常得做成7层的,当然,做成7层的就得处理好规模大了后的问题。

有状态场景

对于有状态场景,就比较麻烦了,对数据一致性要求不高的还OK,主备类型的方案基本也可以用,当然,主备方案要做的很好也非常不容易,有各种各样的方案,对于主备方案又觉得不太爽的情况下,例如HBase这样的,就意味着挂掉一台,另外一台接管的话是需要一定时间的,这个对可用性还是有一定影响的;

全局一致类型的场景中,如果一台挂了,就通常意味着得有选举机制来决定其他机器哪台成为主,常见的例如基于paxos的实现。

可维护性

维护性是很容易被遗漏的部分,但对分布式系统来说其实是很重要的部分,例如整个系统环境应该怎么搭建,部署,配套的维护工具、监控点、报警点、问题定位、问题处理策略等等。

⑹ java里equals和hashCode之间什么关系

1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的。
equals()方法在object类中定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。比如在String类中如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。当然了基本类型是进行值的比较,这个没有什么好说的。
我们还应该注意,Java语言对equals()的要求如下,这些要求是必须遵循的:
�6�1 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
�6�1 反射性:x.equals(x)必须返回是“true”。
�6�1 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
�6�1 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
�6�1 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。
2. 其次是hashcode() 方法,在object类中定义如下:
public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;

for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
解释一下这个程序(String的API中写到):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)
首先,想要明白hashCode的作用,你必须要先知道Java中的集合。
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。
你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。
也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。
哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。如果详细讲解哈希算法,那需要更多的文章篇幅,我在这里就不介绍了。
初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,
就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
所以,Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;2、如果两个对象的hashCode相同,它们并不一定相同 上面说的对象相同指的是用eqauls方法比较。
你当然可以不按要求去做了,但你会发现,相同的对象可以出现在Set集合中。同时,增加新元素的效率会大大下降。
3.这里我们首先要明白一个问题:
equals()相等的两个对象,hashcode()一定相等;
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解释下第3点的使用范围,我的理解是在object、String等类中都能使用。在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时,
Hashcode()方法根据String类的重写(第2点里面已经分析了)代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。

4.谈到hashcode()和equals()就不能不说到hashset,hashmap,hashtable中的使用,具体是怎样呢,请看如下分析:
Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么hashset是根据什么原理来存取对象的呢?
在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?这就是问题的关键所在,经过一下午的查询求证终于获得了一点启示,和大家分享一下,在java的集合中,判断两个对象是否相等的规则是:
1),判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕
如果相等,转入2)
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。后面会重点讲到这个问题。)
2),判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。
比如下面的代码:

public static void main(String args[]){
String s1=new String("zhaoxudong");
String s2=new String("zhaoxudong");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()
System.out.println(s2.hashCode());
Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);
/*实质上在添加s1,s2时,运用上面说到的两点准则,可以知道hashset认为s1和s2是相等的,是在添加重复元素,所以让s2覆盖了s1;*/
Iterator it=hashset.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
最后在while循环的时候只打印出了一个”zhaoxudong”。
输出结果为:false
true
-967303459
-967303459
这是因为String类已经重写了equals()方法和hashcode()方法,所以在根据上面的第1.2条原则判定时,hashset认为它们是相等的对象,进行了重复添加。
但是看下面的程序:
import java.util.*;
public class HashSetTest
{
public static void main(String[] args)
{
HashSet hs=new HashSet();
hs.add(new Student(1,"zhangsan"));
hs.add(new Student(2,"lisi"));
hs.add(new Student(3,"wangwu"));
hs.add(new Student(1,"zhangsan"));

Iterator it=hs.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return num+":"+name;
}
}
输出结果为:
1:zhangsan
1:zhangsan
3:wangwu
2:lisi
问题出现了,为什么hashset添加了相等的元素呢,这是不是和hashset的原则违背了呢?回答是:没有
因为在根据hashcode()对两次建立的new Student(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,所以hashset把他当作不同的对象对待了,当然此时的equals()方法返回的值也不等(这个不用解释了吧)。那么为什么会生成不同的哈希码值呢?上面我们在比较s1和s2的时候不是生成了同样的哈希码吗?原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法,所以在比较时,是继承的object类中的hashcode()方法,呵呵,各位还记得object类中的hashcode()方法比较的是什么吧!!
它是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了(这个大家都能理解吧。。。),造成的结果就是两个对象的hashcode()返回的值不一样。所以根据第一个准则,hashset会把它们当作不同的对象对待,自然也用不着第二个准则进行判定了。那么怎么解决这个问题呢??
答案是:在Student类中重新hashcode()和equals()方法。
例如:
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public int hashCode()
{
return num*name.hashCode();
}
public boolean equals(Object o)
{
Student s=(Student)o;
return num==s.num && name.equals(s.name);
}
public String toString()
{
return num+":"+name;
}
}
根据重写的方法,即便两次调用了new Student(1,"zhangsan"),我们在获得对象的哈希码时,根据重写的方法hashcode(),获得的哈希码肯定是一样的(这一点应该没有疑问吧)。
当然根据equals()方法我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时,我们会发现运行结果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重复元素的问题已经消除。
关于在hibernate的pojo类中,重新equals()和hashcode()的问题:
1),重点是equals,重写hashCode只是技术要求(为了提高效率)
2),为什么要重写equals呢,因为在java的集合框架中,是通过equals来判断两个对象是否相等的
3),在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的。我们再来谈谈前面提到在向hashset集合中添加元素时,怎样判断对象是否相同的准则,前面说了两条,其实只要重写equals()这一条也可以。
但当hashset中元素比较多时,或者是重写的equals()方法比较复杂时,我们只用equals()方法进行比较判断,效率也会非常低,所以引入了hashcode()这个方法,只是为了提高效率,但是我觉得这是非常有必要的(所以我们在前面以两条准则来进行hashset的元素是否重复的判断)。
比如可以这样写:
public int hashCode(){
return 1;}//等价于hashcode无效
这样做的效果就是在比较哈希码的时候不能进行判断,因为每个对象返回的哈希码都是1,每次都必须要经过比较equals()方法后才能进行判断是否重复,这当然会引起效率的大大降低。
我有一个问题,如果像前面提到的在hashset中判断元素是否重复的必要方法是equals()方法(根据网上找到的观点),但是这里并没有涉及到关于哈希表的问题,可是这个集合却叫hashset,这是为什么??
我想,在hashmap,hashtable中的存储操作,依然遵守上面的准则。所以这里不再多说。这些是今天看书,网上查询资料,自己总结出来的,部分代码和语言是引述,但是千真万确是自己总结出来的。有错误之处和不详细不清楚的地方还请大家指出,我也是初学者,所以难免会有错误的地方,希望大家共同讨论。

⑺ java怎样实现手写签章

熟练使用各种数据结构和算法,数组、哈希、链表、排序树…,一句话要么是时间换空间要么是空间换时间,这里展开可以说一大堆,需要有一定的应用经验,用于解决各种性能或业务上的问题。
熟练使用linux操作系统,必备,没什么好说的 。

熟悉tcp协议,创建连接三次握手和断开连接四次握手的整个过程,不了解的话,无法对高并发网络应用做优化; 熟悉http协议,尤其是http头,我发现好多工作五年以上的都弄不清session和cookie的生命周期以及它们之间的关联。

系统集群、负载均衡、反向代理、动静分离,网站静态化 。

分布式存储系统nfs,fastdfs,tfs,Hadoop了解他们的优缺点,适用场景 。

分布式缓存技术memcached,redis,提高系统性能必备,一句话,把硬盘上的内容放到内存里来提速,顺便提个算法一致性hash 。

工具nginx必备技能超级好用,高性能,基本不会挂掉的服务器,功能多多,解决各种问题。

数据库的设计能力,mysql必备,最基础的数据库工具,免费好用,对它基本的参数优化,慢查询日志分析,主从复制的配置,至少要成为半个mysql dba。其他nosql数据库如mongodb。

⑻ java中值相同,hashcode一定相同吗

两者之间唯一的必然关系被你说反了,equls返回为true,则两者的hashcode一定相等,意即相等的对象必须具有相等的哈希码。每当equals方法被覆写,通常需要重写hashCode方法从而
保持对象行为的一致性。而具有相等的hashcode的两个对象equals不一定成立。你可以这样认为也行,hashcode是作为一个对象存储的参考,hash表本身是一种散列表,在数据存储这块,功效比较大,而equals是相当于两对象之间的属性(成员变量)“相等”,意即具有相同的行为(方法)。或许这样讲起来理解比较的费劲。举个例子,比如你定义class A有两个属性,int aA,aB,在定义一个class B也有两个属性,int bA,bB,然后覆写hashcode方法,A类为return aA*aB;B类为return bA*bB.现在情况已经很显然了,各自实例化一个对象:a,b,假如:a.aA=b.bA,a.aB=b.bB,相等,或者a.aA=b.bB,a.aB=b.bA两个对象a,b的hashcode一定相等,当时你能说两个对象相等吗?显然不能吧,a与b都是不同类的实例。连equals最基本的obj instance of A或是obj instance of B都不成立。如果是同一个类的不同对象,当两者拥有相同hashcode的时候,则一定相等,或者equals成立的时候则hashcode一定为真,这也就是所谓的相等的对象具有行为一致性。

⑼ java中哪些地方实现了一致性hash算法

关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法、一致性Hash算法的算法原理做了详细的解读。

算法的具体原理这里再次贴上:

先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),接着在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。

这种算法解决了普通余数Hash算法伸缩性差的问题,可以保证在上线、下线服务器的情况下尽量有多的请求命中原来路由到的服务器。

当然,万事不可能十全十美,一致性Hash算法比普通的余数Hash算法更具有伸缩性,但是同时其算法实现也更为复杂,本文就来研究一下,如何利用Java代码实现一致性Hash算法。在开始之前,先对一致性Hash算法中的几个核心问题进行一些探究。

⑽ 一致性哈希 java实现 怎么映射到圆环上

一致性哈希提出了在动态变化的Cache环境中,哈希算法应该满足的4个适应条件:单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。(这段翻译信息有负面价值的,当缓冲区大小变化时一致性哈希(Consistenthashing)尽量保护已分配的内容不会被重新映射到新缓冲区。)简单的哈希算法往往不能满足单调性的要求,如最简单的线性哈希:x→ax+bmod(P)在上式中,P表示全部缓冲的大小。不难看出,当缓冲大小发生变化时(从P1到P2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。哈希结果的变化意味着当缓冲空间发生变化时,所有的映射关系需要在系统内全部更新。而在P2P系统内,缓冲的变化等价于Peer加入或退出系统,这一情况在P2P系统中会频繁发生,因此会带来极大计算和传输负荷。单调性就是要求哈希算法能够应对这种情况。负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。从表面上看,一致性哈希针对的是分布式缓冲的问题,但是如果将缓冲看作P2P系统中的Peer,将映射的内容看作各种共享的资源(数据,文件,媒体流等),就会发现两者实际上是在描述同一问题。路由算法在一致性哈希算法中,每个节点(对应P2P系统中的Peer)都有随机分配的ID。在将内容映射到节点时,使用内容的关键字和节点的ID进行一致性哈希运算并获得键值。一致性哈希要求键值和节点ID处于同一值域。最简单的键值和ID可以是一维的,比如从0000到9999的整数集合。根据键值存储内容时,内容将被存储到具有与其键值最接近的ID的节点上。例如键值为1001的内容,系统中有ID为1000,1010,1100的节点,该内容将被映射到1000节点。为了构建查询所需的路由,一致性哈希要求每个节点存储其上行节点(ID值大于自身的节点中最小的)和下行节点(ID值小于自身的节点中最大的)的位置信息(IP地址)。当节点需要查找内容时,就可以根据内容的键值决定向上行或下行节点发起查询请求。收到查询请求的节点如果发现自己拥有被请求的目标,可以直接向发起查询请求的节点返回确认;如果发现不属于自身的范围,可以转发请求到自己的上行/下行节点。为了维护上述路由信息,在节点加入/退出系统时,相邻的节点必须及时更新路由信息。这就要求节点不仅存储直接相连的下行节点位置信息,还要知道一定深度(n跳)的间接下行节点信息,并且动态地维护节点列表。当节点退出系统时,它的上行节点将尝试直接连接到最近的下行节点,连接成功后,从新的下行节点获得下行节点列表并更新自身的节点列表。同样的,当新的节点加入到系统中时,首先根据自身的ID找到下行节点并获得下行节点列表,然后要求上行节点修改其下行节点列表,这样就恢复了路由关系。

阅读全文

与一致性hashjava相关的资料

热点内容
程序员看不懂怎么办 浏览:271
linux操作系统题 浏览:765
单片机无符号数加法 浏览:227
应用隐藏加密怎么关闭 浏览:269
汽车空调的压缩机电线有什么用 浏览:429
电脑加密图片如何取消加密 浏览:340
慧净电子51单片机视频 浏览:343
javamap赋值 浏览:165
什么app可以玩掌机游戏 浏览:46
java简单聊天室 浏览:462
通用汽车编程软件 浏览:432
一级抗震框架梁箍筋加密区规定是多少 浏览:974
教你如何把安卓手机变成苹果 浏览:11
app编译分类 浏览:323
怎么用服务器的资源包 浏览:199
oa软件手机登陆服务器地址 浏览:289
androidrtp打包 浏览:723
信息被加密码了怎么办 浏览:420
弹出光盘命令 浏览:517
kdj公式源码分享 浏览:355