同步操作将从 Java精选/Ebooks 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
HashMap一般采用String、Integer 等类作为key、因为这些类底层已经重写了hashcode、equals方法,用的是final修饰类在多线程情况下相对安全。
解题思路:
1、首先将给定的url调用hash方法计算出对应的hash的value,在10亿的url中相同url必然有着相同的value。
2、将文件的hash table放到第value%n台机器上。
3、value/n是机器上hash table的值。
将文件分布在多个机器上,这样要处理网路延时。假设有n台机器。
首先hash文件得到hash value v 将文件的hash table 放到第v%n 台机器上。 v/n是机器上hash table的值。
问题分析:
将文件的url进行hash,得到值value,相同的url的文件具有相同的value,所以会被分配到同一台机器v%n上。在同一台机器上的重复的url文件具有相同的value/n值,如果出现了冲突,不同的url在同一台机器上也可能有相同的value/n值。在每个机器上将value/n值作为key,url值作为value构成hash表进行去重。最后将内存中去重后的hash表中的value值即url写入磁盘。合并磁盘中的各部分url文件,完成去重。
每个url地址大小为 56byte;
服务器内容大小为 4G =4*1024=4096kb=4096*1024 byte;
Set系列集合添加元素无序的根本原因是底层采用哈希表存储元素。
JDK1.8以下版本:哈希表 = 数组 + 链表 + (哈希算法)
JDK1.8及以上版本:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
Iterator是JDK 1.2版本中添加的接口,支持HashMap、ArrayList等集合遍历接口。Iterator是支持fail-fast机制,当多个线程对同一个集合的内容进行操作时可能产生fail-fast事件。
Iterator有3个方法接口,Iterator能读取集合数据且可以对数据进行删除操作,而Enumeration只有2个方法接口,通过Enumeration只能读取集合的数据,而不能对数据进行修改。
Enumeration接口的处理性能是Iterator的两倍且内存使用也更少,但是Iterator接口比Enumeration要安全很多,主要是因为其他线程不能够修改正在被iterator遍历的集合中的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration接口来说是不可能的。
迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能实现。
Enumeration是JDK 1.0版本中添加的接口。Enumeration的方法接口为Vector、Hashtable等类提供了遍历接口。Enumeration本身不支持同步,而在Vector、Hashtable实现Enumeration时添加了同步。
HashMap默认长度16,扩容是2的n次方。
HashMap为了存取高效,要尽量较少碰撞,通俗的说就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就是要把数据存到哪个链表中的算法。
取模即hash%length,计算机中直接求余效率不如位移运算,JDK源码中做了相应的优化hash&(length-1),hash%length==hash&(length-1)的前提是length是2的n次方;
比如说10%8=2,10的二进制是1010,8的二进制是0100
0000 1010 0000 1000
这种取余操作对应2的幂次实际上也就是除数的值之前的数据被整除,后面的余数就是被除数剩余部分为1的值。
0000 1010除以0000 1000把0000 1000高位以及自己抹除,剩下0000 0010。换成与(&)的操作,就是把n(除数)-1和被除数,取余的结果“(n-1)&hash ”。
为什么这样可以均匀分布减少碰撞呢?
2的n次方实即为1后面有n个0,2的n次方-1即为n个1;
比如长度为9时,3&(9-1)=0 2&(9-1)=0 ,都在0上,碰撞了;
比如长度为8时,3&(8-1)=3 2&(8-1)=2 ,不同位置上,不碰撞。
其实就是按位“与”的时候,每一位都能&1,也就是和1111……1111111进行与运算。
Hash值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是内存是无法存储这种量级别数组的,所以这个散列值是不能直接使用的。可以针对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“(n-1) & hash”。这也就解释了HashMap的长度为什么是2的幂次方。
1、Collection接口存储一组不唯一,无序的对象;
2、List接口存储一组不唯一,有序(插入顺序)的对象;
3、Set接口存储一组唯一,无序的对象;
4、Map接口存储一组键值对象,提供key到value的映射。Key无序且唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)。
Comparable接口出自java.lang包,它有一个compareTo(Object obj)方法用来排序。
Comparator接口出自java.util包,它有一个compare(Object obj1, Object obj2)方 法用来排序。
一般对集合使用自定义排序时,需要重写compareTo()方法或compare()方法。
当需要对某一个集合实现两种排序方式,比如一个用户对象中的姓名和身份证分别采用一种排序方法。 方式一:重写compareTo()方法实现姓名、身份证排序
方式二:使用自定义的Comparator方法实现姓名、身份证排序
方法三:使用两个Comparator来实现姓名、身份证排序
其中方式二代表只能使用两个参数的形式Collections.sort()。
Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
实例代码如下:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SortList {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 10,5, 4, 2, 9, 7, 3, 8, 6);
System.out.print("原始数据:");
list.forEach(n -> {
System.out.print(n + ", ");
});
System.out.print("\n\r升序排列:");
Collections.sort(list);
list.forEach(n -> {
System.out.print(n + ", ");
});
System.out.print("\n\r降序排列:");
Collections.reverse(list);
list.forEach(n -> {
System.out.print(n + ", ");
});
}
}
执行结果如下:
原始数据:1, 10, 5, 4, 2, 9, 7, 3, 8, 6,
升序排列:1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
降序排列:10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
1、泛型的参数类型只能是类(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本,这是因参数类型是不确定的,不同版本的泛型类实例是不兼容的。
3、泛型类型的参数可以有多个。
3、泛型的参数类型可以使用extends语句,称为“有界类型”。
4、泛型的参数类型可以是通配符类型,例如Class类。
JDK1.8之前
JDK1.8之前HashMap底层是数组和链表结合在一起使用也就是链表散列。HashMap 通过 key的hashCode经过扰动函数处理过后得到hash值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的n指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
所谓扰动函数指的就是HashMap的hash方法。使用hash方法也就是扰动函数是为了防止一些实现比较差的hashCode()方法 换句话说使用扰动函数之后可以减少碰撞。
JDK1.8的hash方法相比于JDK1.7 hash方法更加简化,但是原理不变。
static final int hash(Object key) {
int h;
// key.hashCode():返回散列值也就是hashcode
// ^ :按位异或
// >>>:无符号右移,忽略符号位,空位都以0补齐
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
对比一下JDK1.7的HashMap的hash()方法源码:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
相比于JDK1.8的hash方法 ,JDK1.7的hash 方法的性能会稍差一点点,因为毕竟扰动了4次。
所谓 “拉链法” 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
JDK1.8之后
相比于之前的版本,JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)(将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。