用Java编写的现有系统使用字符串的哈希码作为负载平衡的路由策略。
现在,我无法修改系统,但需要生成共享相同哈希码的字符串以测试最差情况。
我从命令行提供这些字符串,并希望系统将所有这些字符串路由到同一目的地。
是否可以生成大量共享相同哈希码的字符串?
要明确这个问题:
String[] getStringsInSameHashCode(int number){
//return an array in length "number"
//Every element of the array share the same hashcode.
//The element should be different from each other
}
备注:任何hashCode值都可以接受。字符串是什么没有约束。但它们应该彼此不同。
编辑: 不接受String类的重写方法,因为我从命令行提供这些字符串。
仪器也是不可接受的,因为这会对系统产生一些影响。
答案 0 :(得分:23)
因为你可以阅读中文,你可以查看我的帖子 http://www.hetaoblog.com/myblogs/post/%E8%AF%B4%E4%B8%80%E8%AF%B4java%E9%87%8C%E9%9D%A2%E7%9A%84hashcode-string-hashcode.jhtml
看一下测试方法,基本上,只要你匹配, a1 * 31 + b1 = a2 * 31 + b2,表示(a1-a2)* 31 = b2-b1
public void testHash()
{
System.out.println("A:" + ((int)'A'));
System.out.println("B:" + ((int)'B'));
System.out.println("a:" + ((int)'a'));
System.out.println(hash("Aa".hashCode()));
System.out.println(hash("BB".hashCode()));
System.out.println(hash("Aa".hashCode()));
System.out.println(hash("BB".hashCode()));
System.out.println(hash("AaAa".hashCode()));
System.out.println(hash("BBBB".hashCode()));
System.out.println(hash("AaBB".hashCode()));
System.out.println(hash("BBAa".hashCode()));
}
你会得到
A:65
B:66
a:97
2260
2260
2260
2260
2019172
2019172
2019172
2019172
编辑:有人说这不够直截了当。我在下面添加了
@Test
public void testN() throws Exception {
List<String> l = HashCUtil.generateN(3);
for(int i = 0; i < l.size(); ++i){
System.out.println(l.get(i) + "---" + l.get(i).hashCode());
}
}
AaAaAa---1952508096
AaAaBB---1952508096
AaBBAa---1952508096
AaBBBB---1952508096
BBAaAa---1952508096
BBAaBB---1952508096
BBBBAa---1952508096
BBBBBB---1952508096
下面是源代码,它可能效率不高,但它可以工作:
public class HashCUtil {
private static String[] base = new String[] {"Aa", "BB"};
public static List<String> generateN(int n)
{
if(n <= 0)
{
return null;
}
List<String> list = generateOne(null);
for(int i = 1; i < n; ++i)
{
list = generateOne(list);
}
return list;
}
public static List<String> generateOne(List<String> strList)
{
if((null == strList) || (0 == strList.size()))
{
strList = new ArrayList<String>();
for(int i = 0; i < base.length; ++i)
{
strList.add(base[i]);
}
return strList;
}
List<String> result = new ArrayList<String>();
for(int i = 0; i < base.length; ++i)
{
for(String str: strList)
{
result.add(base[i] + str);
}
}
return result;
}
}
查看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;
}
答案 1 :(得分:7)
我认为从长字符串中找到一个等于哈希的字符串太难了,当找到一个短字符串(2或3)的等哈希字符串时很容易。 看下面的等式。 (对不起,我发布的图片不能让我成为新成员)
请注意,“FB”和“Ea”具有相同的哈希码,并且任何两个字符串(如s1 +“FB”+ s2和s1 +“Ea”+ s2)将具有相同的哈希码。 因此,简单的解决方案是找到现有字符串的任何2-char子字符串,并用具有相同哈希码的2-char子字符串替换
例如,我们有字符串“helloworld” 获取2-char子串“he”,hashcode(“he”)='h'* 31 +'e'=('h'* 31 + 31)+('e' - 31)=('h'+ 1 )* 31 +'F'='i'+'F'=哈希码(“iF”) 所以欲望字符串是“iFlloworld” 我们将'h'增加了1,我们可以增加2或3等(但如果溢出char值则会出错)
下面的代码运行良好的小级别,如果级别很大就会出错,使char值溢出,如果你愿意,我会稍后修复它(这个代码更改2个第一个字符,但我会编译代码为2最后一个字符因为2个第一个字符是最大值的计算值
public static String samehash(String s, int level) {
if (s.length() < 2)
return s;
String sub2 = s.substring(0, 2);
char c0 = sub2.charAt(0);
char c1 = sub2.charAt(1);
c0 = (char) (c0 + level);
c1 = (char) (c1 - 31 * level);
String newsub2 = new String(new char[] { c0, c1 });
String re = newsub2 + s.substring(2);
return re;
}
答案 2 :(得分:2)
给出字符串X,然后字符串Y =“ \ u0096 \ 0 \0ɪ\0ˬ” + X将与X共享相同的哈希码。
说明:
String.hashcode()返回Integer,并且Java中的每个Integer X都具有X = X + 2 *(Integer.MAX_VALUE + 1)的属性。在这里,Integer.MAX_VALUE = 2 ^ 31-1;
因此我们只需要找到字符串M,它的属性为M的哈希码%(2 *(Integer.MAX_VALUE + 1))= 0;
我发现“ \ u0096 \ 0 \0ɪ\0ˬ”:\ u0096的ascii码是150,\ 0的ascii码是0,ɪ的ascii码是618,ˬ的ascii码是748,所以其哈希码为150 * 31 ^ 5 + 618 * 31 ^ 2 + 748 = 2 ^ 32 = 0;
由您决定要使用哪个字符串,我选这个。
答案 3 :(得分:1)
您可以检测java.lang.String类,使其方法hashCode()始终返回相同的数字。
我认为Javassist是最容易做这种仪器的方法。
简而言之:
代码看起来像(大致):
ClassPool classPool = new ClassPool(true);
CtClass stringClass = classPool.get("java.lang.String");
CtMethod hashCodeMethod = stringClass.getDeclaredMethod("hashCode", null);
hashCodeMethod.setBody("{return 0;}");
byte[] bytes = stringClass.toBytecode();
ClassDefinition[] classDefinitions = new ClassDefinition[] {new ClassDefinition(String.class, bytes);
instrumentation.redefineClasses(classDefinitions);// this instrumentation can be obtained via Java-agent
另外,请不要忘记代理清单文件必须指定Can-Redefine-Classes: true
才能使用redefineClasses(ClassDefinition [])方法。
答案 4 :(得分:1)
我想知道是否有“通用”解决方案;例如一些常量字符串XYZ
,例如
s.hashCode() == (s + XYZ).hashCode()
表示任何字符串s
。找到这样一个字符串涉及解决一个相当复杂的等式......这超出了我生锈的数学能力。但是,当h == 31*h + ch
和true
都为零时,h
总是ch
让我明白了!
基于这种见解,以下方法应该创建一个与其参数具有相同哈希码的不同String:
public String collider(String s) {
return "\0" + s;
}
如果你的NUL字符有问题,那么在任何字符串前面加上哈希码为零的字符串也会起作用...虽然碰撞字符串会比你使用零字符串更长。
答案 5 :(得分:0)
String s = "Some String"
for (int i = 0; i < SOME_VERY_BIG_NUMBER; ++i) {
String copy = new String(s);
// Do something with copy.
}
这对你有用吗?它只是创建了许多相同字符串文字的副本,然后您可以在测试中使用它们。