HashMap,HashSet
HashMap,HashSet
摘自:https://www.cnblogs.com/skywang12345/p/3310887.html#a1
目录
3. 疑问:如果两个key通过hash%Entry[].length得到的 index相同,会不会有覆盖的危险? 4
3. HashSet源码解析(基于JDK1.6.0_45) 8
2. 深入理解ConcurrentHashMap原理分析即线程安全问题 18
1) ConcurrentHashMap与HashTable的区别 18
七、 HashMap,HashTable和ConcurrentHashMap的区别 21
1. HashMap与ConcurrentHashMap的区别 21
2. ConcurrentHashMap vs Hashtable vs Synchronized Map区别 21
- HashMap(键值对形式存取,键值不能相同)
- HashMap的数据结构
数组的特点是:寻址容易,插入和删除困难。
链表的特点是:寻址困难,插入和删除容易。
综合这两者的特性,得到一种寻址容易,插入删除也容易的数据结构:这就是我们要提起的哈希表。
哈希表有多种不同的实现方法,我们接下来解释的是最常用的方法——拉链法,我们可以理解为"链表的数组":如图:
从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中的呢?一般情况下是通过hash(key)%len获得,也就是元素的key的哈希值对数组的长度取余得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12,28,108,140都存储在数组下标为12的链表的位置。
HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组,这可能让我们很不解,一个线性的数据怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。
首先HahsMap里面实现了一个静态内部类,其重要的属性有key,value,next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
- HashMap的存取实现
既然是线性数组,为什么能随机存取呢?这里HashMap用了一个小算法,大致HashMap 采用一种所谓的"Hash 算法"来决定每个元素的存储位置。当程序执行 map.put(String,Obect)方法时,系统将调用String的 hashCode() 方法得到其 hashCode 值——每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置。是这样实现:
//存储时: int hash = key.hashCode();// 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值 int index = hash % Entry[].length; Entry[index] = value; //取值时: int hash = key.hashCode(); int index = hash % Entry[].length; return Entry[index]; |
这里的话我们:
对于存储:
- 通过hashCode()计算key的hash值;
- 通过key的hash值对数组长度取余得到该key的value在数组中的下标;
- 将value赋值给Entry[index]实现键值对的存储
对于取值:
- 首先也是计算key的hash值;
- 计算key的hash值对数组长度取余得到该key的value在数组中的下标;
- 通过返回return Entry[index]得到键key所对应的值。
- 疑问:如果两个key通过hash%Entry[].length得到的 index相同,会不会有覆盖的危险?
这样占用的内存会很大
这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方,第一个键值对A进来,通过计算其key的hash得到的index=0,记作:Entry[0]=A。一会又进来一个键值对B,通过计算其index也等于0;现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。到这里为止,HashMap的大致实现,我们应该已经清楚了。
当然HashMap里面也包含一些优化方面的实现,这里也说一下。比如:Entry[]的长度一定后,随着map里面数据的越来越长,这样同一个index的链就会很长,会不会影响性能?HashMap里面设置一个因素(也称为因子),随着map的size越来越大,Entry[]会以一定的规则加长长度。
- 解决hash冲突的方法
- 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
- 再哈希法
- 链地址法
- 建立一个公共溢出区
Java中hashMap的解决方法就是采用链地址法。
- Hash冲突是什么?
若两个不相等的 key 产生了相等的哈希值,这时则需要采用哈希冲突。
首先,HashMap是由线性数组组成的,现在我们假设初始数组的长度为5;然后我们存储数据,假设存储的第一个数据的键值的hashcode计算出来的值为6,然后我们通过hashcode计算出来的值与数组长度取余得到存储第一个数据的下标,即6%5=1;当我们存储另外的数据,如果通过键值的hashcode计算出来的值是11,那么此时计算出数据的下标11%5=1也是1。这就是哈希冲突。
- 如何解决哈希冲突?
Java采用拉链法解决哈希冲突。
- 得到一个key;
- 计算key的hashValue;
- 根据 hashValue 值定位到 data[hashValue] 。( data[hashValue] 是一条链表)
- 若 data[hashValue] 为空则直接插入,不然则添加到链表头部。
- HashMap的put()和remove()方法
HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("wang", 01); map.put("wang",02); System.out.println(map.get("wang")); System.out.println("----------------"); map.remove("wang"); System.out.println(map.get("wang")); |
*************** 2 ---------------- null |
- 两次插入的键相同时不是哈希冲突
方法:则直接更新该键的值
- 两次插入的键不同时,但是得到相同的hashValue时,是哈希冲突
方法:将值插入到单链表的头结点。
- 删除关键字值为k的记录,应先在该关键字值的哈希地址处的单链表中找到该记录,然后删除之。
- HashSet(是一个没有重复元素的集合)
- HashSet简介
HashSet是一个没有重复元素的集合,它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用null元素。
HashSet是非同步的,如果多个线程同时访问一个HashSet,而其中至少一个线程修改了该set,那么它必须保持外部同步。这通常是通过对自然封装该set的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用Collections.synchronizedSet方法来包装set,最好在创建完成时完成这一操作,以防止对该set进行意外的不同步访问:
Set s = Collections.synchronizedSet(new HashSet(...));
HashSet通过iterator()迭代器进行访问。
- HashSet的数据结构
HashSet的继承关系如下:
java.lang.Object
↳ java.util.AbstractCollection<E>
↳ java.util.AbstractSet<E>
↳ java.util.HashSet<E>
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable { }
从上图可以看出:
- HashSet继承与AbstractSet,并且实现了Set接口
- HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,HashSet的操作函数,实际上都是通过map实现的。
- HashSet源码解析(基于JDK1.6.0_45)
为了更了解HashSet的原理,下面对HashSet源码代码作出分析。
package java.util; public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; // HashSet是通过map(HashMap对象)保存内容的 private transient HashMap<E,Object> map; // PRESENT是向map中插入key-value对应的value // 因为HashSet中只需要用到key,而HashMap是key-value键值对; // 所以,向map中添加键值对时,键值对的值固定是PRESENT private static final Object PRESENT = new Object(); // 默认构造函数 public HashSet() { // 调用HashMap的默认构造函数,创建map map = new HashMap<E,Object>(); } // 带集合的构造函数 public HashSet(Collection<? extends E> c) { // 创建map。 // 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢? // 首先,说明(c.size()/.75f) + 1 // 因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。 // 当HashMap的"阈值"(阈值=HashMap总的大小*加载因子) < "HashMap实际大小"时, // 就需要将HashMap的容量翻倍。 // 所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。 // 接下来,说明为什么是 16 。 // HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍; // HashMap的构造函数中也会重新计算,找出比"指定大小"大的最小的2的指数倍的数。 // 所以,这里指定为16是从性能考虑。避免重复计算。 map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); // 将集合(c)中的全部元素添加到HashSet中 addAll(c); } // 指定HashSet初始容量和加载因子的构造函数 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } // 指定HashSet初始容量的构造函数 public HashSet(int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); } // 返回HashSet的迭代器 public Iterator<E> iterator() { // 实际上返回的是HashMap的"key集合的迭代器" return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } // 将元素(e)添加到HashSet中 public boolean add(E e) { return map.put(e, PRESENT)==null; } // 删除HashSet中的元素(o) public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } // 克隆一个HashSet,并返回Object对象 public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } // java.io.Serializable的写入函数 // 将HashSet的"总的容量,加载因子,实际容量,所有的元素"都写入到输出流中 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } // java.io.Serializable的读取函数 // 将HashSet的"总的容量,加载因子,实际容量,所有的元素"依次读出 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMap int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i=0; i<size; i++) { E e = (E) s.readObject(); map.put(e, PRESENT); } } } |
说明: HashSet的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过HashMap实现的,若对HashSet的理解有困难,建议先学习以下HashMap;学完HashMap之后,在学习HashSet就非常容易了。
- HashSet的遍历方式
- 通过Iterator遍历HashSet
第一步:根据iterator()获取HashSet的迭代器
遍历迭代器获取各个元素
// 假设set是HashSet对象 for(Iterator iterator = set.iterator(); iterator.hasNext(); ) { iterator.next(); } |
- 通过for-each遍历HashSet
- 第一步:根据toArray()获取HashSet的元素集合对应的数组
- 遍历数组,获取各个元素
// 假设set是HashSet对象,并且set中元素是String类型 String[] arr = (String[])set.toArray(new String[0]); for (String str:arr) System.out.printf("for each : %s\n", str); |
向HashSet中添加元素,如果set中元素已存在,则返回false;如果不存在,则返回true。
- TreeMap与HashMap的区别和共同点
TreeMap |
HashMap |
TreeMap实现了SortMap接口,是基于红黑树的 |
HashMap实现了Map接口,是基于哈希散列表的 |
TreeMap默认按键的升序排序 |
HashMap随机存储 |
TreeMap的遍历是Iterator按顺序遍历的 |
HahsMap的遍历是Iterator随机遍历的 |
TreeMap键和值都不能为空 |
HashMap键只能有一个null,值可以有多个null |
TreeMap插入删除查找的效率比较低 |
HashMap插入删除查找的效率比较高 |
非线程安全的 |
非线程安全的 |
- HashMap和HashTable的区别
在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = new Object();,
当有新值加入时,底层的HashMap会判断Key值是否存在(HashMap细节请移步深入理解HashMap),如果不存在,则插入新值,同时这个插入的细节会依照HashMap插入细节;如果存在就不插入
HashMap |
HashSet |
HashMap实现了Map接口 |
HashSet实现了Set接口 |
HashMap存储键值对 |
HashSet仅仅存储对象,存储的是键,他们的值是相同的。 |
使用pub()方法将元素放入map中 |
使用add()方法将元素放入set中 |
HashMap中使用键对象来计算hashcode值(不会返回true和false) |
HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
HashMap查找比较快,因为是使用唯一的键来获取对象 |
HashSet较HashMap来说比较慢 |
- HashMap和HashTable的区别
HashMap |
HashTable |
HashMap是基于AbstractMap |
HashTable基于Dictionary类 |
HashMap可以允许存在一个为null的key和任意个为null的value |
HashTable中的key和value都不允许为null |
HashMap时单线程安全的,多线程是不安全的 |
Hashtable是多线程安全的 |
HashMap仅支持Iterator的遍历方式 |
Hashtable支持Iterator和Enumeration两种遍历方式 |
Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。Hashtable的方法都用synchronized来修饰,所以它是线程同步的。
Hashtable的遍历:
遍历Hashtable的键值对
第一步:根据entrySet()获取Hashtable的"键值对"的Set集合。
通过Iterator迭代器遍历"第一步"得到的集合。
|
通过Iterator遍历Hashtable的键
第一步:根据keySet()获取Hashtable的"键"的Set集合。
第二步:通过Iterator迭代器遍历"第一步"得到的集合。
// 假设table是Hashtable对象 // table中的key是String类型,value是Integer类型 String key = null; Integer integ = null; Iterator iter = table.keySet().iterator(); while (iter.hasNext()) { // 获取key key = (String)iter.next(); // 根据key,获取value integ = (Integer)table.get(key); } |
通过Iterator遍历Hashtable的值
第一步:根据value()获取Hashtable的"值"的集合。
第二步:通过Iterator迭代器遍历"第一步"得到的集合
// 假设table是Hashtable对象 // table中的key是String类型,value是Integer类型 Integer value = null; Collection c = table.values(); Iterator iter= c.iterator(); while (iter.hasNext()) { value = (Integer)iter.next(); } |
通过Enumeration遍历Hashtable的键
第一步:根据keys()获取Hashtable的集合。
第二步:通过Enumeration遍历"第一步"得到的集合
Enumeration enu = table.elements(); while(enu.hasMoreElements()) { System.out.println(enu.nextElement()); } |
通过Enumeration遍历Hashtable的值
第一步:根据elements()获取Hashtable的集合。
第二步:通过Enumeration遍历"第一步"得到的集合
Enumeration enu = table.elements(); while(enu.hasMoreElements()) { System.out.println(enu.nextElement()); } |
- ConcurrentHashMap的应用
- concurrentHashMap的优势
首先常用的三种HashMap包括HashMap,HashTable和concurrentHashMap:
- HashMap在并发编程过程中使用可能导致死循环,因为插入过程不是原子操作,每个HashEntry是一个链表节点,很可能在插入的过程中,已经设置了后节点,实际还未插入,最终反而插入在后节点之后,造成链中出现环,破坏了链表的性质,失去了尾节点,出现死循环。
- HashTable因为内部是采用synchronized来保证线程安全的,但在线程竞争激烈的情况下HashTable的效率下降得很快因为synchronized关键字会造成代码块或方法成为为临界区(对同一个对象加互斥锁),当一个线程访问临界区的代码时,其他线程也访问同一临界区时,会进入阻塞或轮询状态。究其原因,实际上是有获取锁意向的线程的数目增加,但是锁还是只有单个,导致大量的线程处于轮询或阻塞,导致同一时间段有效执行的线程的增量远不及线程总体增量。
- 在查询时,尤其能够体现出CocurrentHashMap在效率上的优势,HashTable使用Sychronized关键字,会导致同时只能有一个查询在执行,而Cocurrent则不采取加锁的方法,而是采用volatile关键字,虽然也会牺牲效率,但是由于Sychronized,于该文末尾继续讨论。
- CocurrentHashMap利用锁分段技术增加了锁的数目,从而使争夺同一把锁的线程的数目得到控制。
- 锁分段技术就是对数据集进行分段,每段竞争一把锁,不同数据段的数据不存在锁竞争,从而有效提高高并发访问效率。
- CocurrentHashMap在get方法是无需加锁的,因为用到的共享变量都采用volatile关键字修饰,巴证共享变量在线程之间的可见性(每次读取都先同步缓存和内存,直接从内存中获取值,虽然不是原子操作,但根据JAVA内存模型的happen before原则,对volatile字段的写入操作先于读操作,能够保证不会脏读),volatile为了让变量提供线程之间的内存可见性,会禁止程序执行结果的重排序(导致缓存优化的效果降低)
- 深入理解ConcurrentHashMap原理分析即线程安全问题
- ConcurrentHashMap与HashTable的区别
HashTable 的put()源代码
从代码可以看出来在所有put 的操作的时候都需要用 synchronized 关键字进行同步。并且key 不能为空。
这样相当于每次进行put 的时候都会进行同步当10个线程同步进行操作的时候,就会发现当第一个线程进去其他线程必须等待第一个线程执行完成,才可以进行下去。性能特别差。
- ConcurrentHashMap详解
分段锁技术:ConcurrentHashMap相比 HashTable而言解决的问题就是的它不是锁全部数据,而是锁一部分数据,这样多个线程访问的时候就不会出现竞争关系。不需要排队等待了。
从图中可以看出来ConcurrentHashMap的主干是个Segment数组。、
它把区间按照并发级别(concurrentLevel),分成了若干个segment。默认情况下内部按并发级别为16来创建。对于每个segment的容量,默认情况也是16。
ConcurrentHashMap是由Segment数组和HashEntry数组组成.
Segment是一种可重入锁,在ConcurrentHashMap里扮演锁的角色;
HashEntry则用于存储键值对数据.
一个ConcurrentHashMap里包含一个Segment数组.
Segment的结构和HashMap类似,是一种数组和链表结构.
一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,
必须首先获得与它对应的Segment锁
这就是为什么ConcurrentHashMap支持允许多个修改同时并发进行,原因就是采用的Segment分段锁功能,每一个Segment 都想的于小的hash table并且都有自己锁,只要修改不再同一个段上就不会引起并发问题。
- HashMap,HashTable和ConcurrentHashMap的区别
- HashMap与ConcurrentHashMap的区别
- 他们之间的第一个重要的区别就是ConcurrentHashMap是线程安全的和在并发环境下不需要加额外的同步。
- 你可以使用Collections.synchronizedMap(HashMap)来包装HashMap作为同步容器,这时它的作用几乎与Hashtable一样,当每次对Map做修改操作的时候都会锁住这个Map对象,而ConcurrentHashMap会基于并发的等级来划分整个Map来达到线程安全,它只会锁操作的那一段数据而不是整个Map都上锁。
- ConcurrentHashMap有很好的扩展性,在多线程环境下性能方面比做了同步的HashMap要好,但是在单线程环境下,HashMap会比ConcurrentHashMap好一点。
- ConcurrentHashMap vs Hashtable vs Synchronized Map区别
虽然三个集合类在多线程并发应用中都是线程安全的,但是他们有一个重大的差别,就是他们各自实现线程安全的方式。
- Hashtable是jdk1的一个遗弃的类,它把所有方法都加上synchronized关键字来实现线程安全,所有的方法都同步这样造成多个线程访问效率特别低。
- Synchronized Map与HashTable差别不大,也是在并发中作类似的操作,两者的唯一区别就是Synchronized Map没被遗弃,它可以通过使用Collections.synchronizedMap()来包装Map作为同步容器使用。
- ConcurrentHashMap的设计有点特别,表现在多个线程操作上。ConcurrentHashMap不需要锁整个Map,相反它划分了多个段(segments),要操作哪一段才上锁那段数据。
HashMap,HashSet的更多相关文章
- 对hashmap,hashset,hashtable的理解
1.首先先理一下java的集合关系,Collection和Map接口是所有集合接口的根结点,其他集合都直接或者间接的实现了他们中的一个:collection下有:list(元素可重复)和set(不可重 ...
- [Java语言] HashMap,HashSet,Hashtable,Vector,ArrayList 的关系 <转>
这么几个比较常用的但是比较容易混淆的概念同出于 java.util 包.本文仅作几个类的浅度解析. (本文基于JDK1.7,源码来自openjdk1.7.) ├── Collection │ ├── ...
- java常用集合类:Deque,ArrayList,HashMap,HashSet
图一:java collection 类图 Queue家族 无论是queue还是stack,现在常用的是Deque的实现类:如单线程的ArrayQueue,多线程的ArrayBlockingQueue ...
- java该HashTable,HashMap和HashSet
同一时候我们也对HashSet和HashMap的核心方法hashcode进行了具体解释,见<探索equals()和hashCode()方法>. 万事俱备,那么以下我们就对基于hash算法的 ...
- HashMap和HashSet的源代码分析
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final float DEFAULT_LOAD_ ...
- LinkedList,HashSet,HashMap
LinkedList底层源码是采用双向链表实现的 private static class Node<E> { E item;//节点值 Node<E> next;//节点后指 ...
- == 和 equals,equals 与 hashcode,HashSet 和 HashMap,HashMap 和 Hashtable
一:== 和 equals == 比较引用的地址equals 比较引用的内容 (Object 类本身除外) String obj1 = new String("xyz"); Str ...
- 关于HashMap,HashTable,HashSet浅析
首先,最重要的,HashMap 作为一个我们使用非常多的集合.最常被大家认知的是,它是一个key-value形式存储数据的数据结构,可以实现快速的存,取操作. 关于HashMap的源码,我们截取一 ...
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
随机推荐
- mysql 如何减少InnoDB关闭时间
原文连接: https://www.percona.com/blog/2009/04/15/how-to-decrease-innodb-shutdown-times/ 有时,运行InnoDB的MyS ...
- Linux_CentOS 中systemctl 管理服务、防火墙 firewalld 以及 SELinux 配置
使用 systemctl 管理服务 systemctl 就是 service 和 chkconfig 这两个命令的整合,在 CentOS 7 就开始被使用了,systemctl是系统服务管理器命令,它 ...
- SDN实验---Ryu的应用开发(一)Hub实现
补充: (一)Ubuntu下安装Notepadqq 背景:为什么安装Notepadqq Notepad++ 不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的外挂模组.但是可惜的是N ...
- 使用 Fiddler 代理调试本地手机页面
文件下载:http://files.cnblogs.com/files/dtdxrk/fiddler4_4.6.2.0_setup.rar 从事前端开发的同学一定对 Fiddler 不陌生,它是一个非 ...
- [LeetCode] 6. ZigZag Converesion 之字型转换字符串
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like ...
- [LeetCode] 535. Encode and Decode TinyURL 编码和解码短网址
Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...
- 【电商日志项目之六】数据分析-Hive方式
环境 hadoop-2.6.5 hive-1.2.1 一.Hive和Hbase整合如果使用Hive进行分析,Hive要从Hbase取数据(当然可以直接将数据存到Hive),那么就需要将Hive和HBa ...
- PHP命令行参数
原文地址:http://php.swoole.com/wiki/PHP%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0 PHP命令行参数 目录 [隐藏] ...
- [计算机视觉][神经网络与深度学习]Faster R-CNN配置及其训练教程2
faster-rcnn分为matlab版本和python版本,首先记录弄python版本的环境搭建过程.matlab版本见另一篇:faster-rcnn(testing): ubuntu14.04+c ...
- Qt5 QtQuick系列----QtQuick的Secne Graph剖析(3)-- qml与OpenGl结合
我读的书愈多,就愈亲近世界,愈明了生活的意义,愈觉得生活的重要. -- 高尔基 需要先看:https://blog.csdn.net/qq_35865125/article/details/86485 ...