首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的快速增量散列

Java中的快速增量散列
EN

Stack Overflow用户
提问于 2014-11-14 11:02:33
回答 2查看 1.9K关注 0票数 2

我在找一个散列函数。就我的目的而言(在导入过程中标识更改的对象),它应该具有以下属性:

  1. 快地
  2. 可以使用增量,即我可以像这样使用它: Hasher h= new ();h.add("somestring");h.add(“另一部分”);h.add("eveno“); 而不影响其他属性或在整个过程中将字符串保存在内存中。
  3. 防碰撞。如果在我的余生中,每天比较不同字符串的两个散列值100万次,那么碰撞的风险应该是可以忽略的。

它不需要对创建冲突的恶意尝试具有安全性。

我能用什么算法?优先采用在Java中存在的免费实现的算法。

澄清

  1. 哈希不一定很长。例如,一个字符串就可以了。
  2. 要散列的数据将来自一个文件或数据库,其中包含许多10 db或数GB的数据,这些数据将被分发到不同的散列中。因此,将完整的字符串保存在内存中并不是一个真正的选择。
EN

回答 2

Stack Overflow用户

发布于 2014-11-18 18:00:10

哈希是一个明智的话题,很难根据您的问题推荐任何这样的散列。您可能需要在https://security.stackexchange.com/上问这个问题,以获得关于某些应用程序中hashs可用性的专家意见。

到目前为止,我所理解的是,大多数散列都是在核心中逐步实现的;另一方面,执行时间并不是那么容易预测。

我向您介绍两个Hasher实现,它们依赖于“在Java中存在的免费实现”。这两个实现的构造方式都可以在调用String之前任意拆分add(),并获得相同的结果,只要您不更改它们中字符的顺序:

代码语言:javascript
复制
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * Created for https://stackoverflow.com/q/26928529/1266906.
 */
public class Hashs {

    public static class JavaHasher {
        private int hashCode;

        public JavaHasher() {
            hashCode = 0;
        }

        public void add(String value) {
            hashCode = 31 * hashCode + value.hashCode();
        }

        public int create() {
            return hashCode;
        }
    }

    public static class ShaHasher {
        public static final Charset UTF_8 = Charset.forName("UTF-8");
        private final MessageDigest messageDigest;

        public ShaHasher() throws NoSuchAlgorithmException {
            messageDigest = MessageDigest.getInstance("SHA-256");
        }

        public void add(String value) {
            messageDigest.update(value.getBytes(UTF_8));
        }

        public byte[] create() {
            return messageDigest.digest();
        }
    }

    public static void main(String[] args) {
        javaHash();

        try {
            shaHash();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();  // TODO: implement catch
        }
    }

    private static void javaHash() {
        JavaHasher h = new JavaHasher();
        h.add("somestring");
        h.add("another part");
        h.add("eveno more");
        int hash = h.create();
        System.out.println(hash);
    }

    private static void shaHash() throws NoSuchAlgorithmException {
        ShaHasher h = new ShaHasher();
        h.add("somestring");
        h.add("another part");
        h.add("eveno more");
        byte[] hash = h.create();
        System.out.println(Arrays.toString(hash));
        System.out.println(new BigInteger(1, hash));
    }
}

在这里,"SHA-256“显然可以被其他常见的散列算法所取代;Java提供了相当多的散列算法。

现在,您调用了一个Long作为返回值,这意味着您正在寻找64位哈希。如果这真的是故意的,请看一下What is a good 64bit hash function in Java for textual strings?的答案。接受的答案是JavaHasher的一个细微的变体,因为String.hashCode()做的计算基本相同,但溢出边界较低:

代码语言:javascript
复制
    public static class Java64Hasher {
        private long hashCode;

        public Java64Hasher() {
            hashCode = 1125899906842597L;
        }

        public void add(CharSequence value) {
            final int len = value.length();

            for(int i = 0; i < len; i++) {
                hashCode = 31*hashCode + value.charAt(i);
            }
        }

        public long create() {
            return hashCode;
        }
    }

对你们的观点:

  1. 快地 由于SHA-256比其他两种方法慢,我仍然认为这三种方法都是快速的。
  2. 可以在不损害其他属性或在整个过程中将字符串保存在内存中的情况下使用增量。 我不能保证ShaHasher的那个属性是基于块的,而且我缺少源code.Still,我建议最多保留一个块、散列和一些内部状态。另外两个显然只存储对add()调用之间的部分散列。
  3. 防碰撞。如果在我的余生中,每天比较不同字符串的两个散列值100万次,那么碰撞的风险应该是可以忽略的。 对于每一个散列,都存在冲突。给定良好的分布,散列的位大小是影响碰撞发生频率的主要因素。JavaHasher用于例如HashMap,并且似乎“无冲突”,足以将彼此相隔很远的类似密钥分发出去。至于更深层次的分析:做你自己的测试或者问你当地的安全工程师-对不起。

我希望这给了一个好的起点,细节可能主要是基于意见的.

票数 3
EN

Stack Overflow用户

发布于 2014-11-19 16:05:48

不是作为一个答案,只是为了证明哈希碰撞比人类直觉假设的可能性大得多。

下面的小程序生成2^31个不同的字符串,并检查它们的散列是否发生碰撞。它通过保持每个可能的散列值的跟踪位(因此您需要>512 It堆来运行它)来实现这一点,并在遇到每个哈希值时将它们标记为"used“。它需要几分钟才能完成。

代码语言:javascript
复制
public class TestStringHashCollisions {

    public static void main(String[] argv) {
        long collisions = 0;
        long testcount = 0;
        StringBuilder b = new StringBuilder(64);
        for (int i=0; i>=0; ++i) {
            // construct distinct string
            b.setLength(0);
            b.append("www.");
            b.append(Integer.toBinaryString(i));
            b.append(".com");

            // check for hash collision
            String s = b.toString();
            ++testcount;
            if (isColliding(s.hashCode()))
                ++collisions;

            // progress printing
            if ((i & 0xFFFFFF) == 0) {
                System.out.println("Tested: " + testcount + ", Collisions: " + collisions);
            }
        }
        System.out.println("Tested: " + testcount + ", Collisions: " + collisions);
        System.out.println("Collision ratio: " + (collisions / (double) testcount));
    }

    // storage for 2^32 bits in 2^27 ints
    static int[] bitSet = new int[1 << 27];

    // test if hash code has appeared before, mark hash as "used"
    static boolean isColliding(int hash) {
        int index = hash >>> 5;
        int bitMask = 1 << (hash & 31);
        if ((bitSet[index] & bitMask) != 0)
            return true;
        bitSet[index] |= bitMask;
        return false;
    }

}

您可以轻松地调整字符串生成部分,以测试不同的模式。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26928529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档