HashMap

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Map and Set are most important data structures we use in development

Copyright © 直通硅谷

http://www.zhitongguigu.com/

There are multiple different maps and sets that are in Java

HashMap

Java: Map<A,B>

Map<String, Integer> hm = new HashMap<>();

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Basic Operations

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Return Type Method
Value   🤔 put(key, value)
Value get(key)
Value remove(key)
boolean containsKey(key)
boolean containsValue(value)
Set<Map.Entry<Key, Value>> entrySet()
Set<Key> keySet()
Collection<Value> values()
... size(); isEmpty(); putAll(Map<>);

Key: Object; Value: Object

Basic operations

Copyright © 直通硅谷

http://www.zhitongguigu.com/

hashmap : [<"apple", 1>, <"pear", 1>]

hashmap.put("banana", 3);

hashmap.remove("apple");

hashmap.get("pear");

hashmap.containsKey("kiwi");

hashmap.containsValue(3);

not a good one to use

Time complexity for HashMap

Copyright © 直通硅谷

http://www.zhitongguigu.com/

  • add 
  • remove
  • get
  • containsKey
  • containsValue
  • O(1)
  • O(1)
  • O(1)
  • O(1)
  • O(n)

Traverse a HashMap

Copyright © 直通硅谷

http://www.zhitongguigu.com/

// Map -> Set -> Iterator -> Map.Entry -> troublesome, not recommend!
Iterator<Entry<String,String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry<String,String> entry = (Map.Entry<String,String>) iterator.next();
	System.out.println("Key: " + entry.getKey() + " Value:" + entry.getValue());
}

// more elegant way, this should be the standard way, recommend!
for (Map.Entry<String, String> entry : map.entrySet()) {
	System.out.println("Key: " + entry.getKey() + " Value: " + entry.getValue());
}

// weired, but works anyway, not recommend!
for (Object key : map.keySet()) {
	System.out.println("Key: " + key.toString() + " Value: " + map.get(key));
}

So always use the second way!

🤔

🤔

Hashing

Copyright © 直通硅谷

http://www.zhitongguigu.com/

collision

Hash Function

Copyright © 直通硅谷

http://www.zhitongguigu.com/

a function that can take a key and compute an integer (or an index in a table) for it

What really matters: How could we find a hash function that could fast compute the index without collisions

Hash Function

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Division:

     X - Number of slots

     A - Key as int

A mod X to get index

Example:

<X = 17, A = 35> <X = 17, A = 6>

35%17 = 1; 8%17 = 8

Hash Function Division

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Hash Function Division

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Hash Function Java String

Copyright © 直通硅谷

http://www.zhitongguigu.com/

// hash default 0

public int More 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;
}

Hash Function Joshua Bloch

Copyright © 直通硅谷

http://www.zhitongguigu.com/

1. Create a int result and assign a non-zero value.

2. For every field f tested in the equals() method, calculate a hash code c by:
- If the field f is a boolean: calculate (f ? 0 : 1);
- If the field f is a byte, char, short or int: calculate (int)f;
- If the field f is a long: calculate (int)(f ^ (f >>> 32));
- If the field f is a float: calculate Float.floatToIntBits(f);
- If the field f is a double: calculate Double.doubleToLongBits(f) 
  and handle the return value like every long value;
- If the field f is an object: Use the result of the hashCode() 
  method or 0 if f == null;
- If the field f is an array: see every field as separate element 
  and calculate the hash value in a recursive fashion and combine 
  the values as described next.

3. Combine the hash value c with result:
    result = 37 * result + c;

4. Return result

// This should result in a proper distribution of hash values for most use situations.

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int hashCode() {
    // Start with a non-zero constant. Prime is preferred
    int result = 17;
    // Include a hash for each field.
    // Primatives
    result = 31 * result + (booleanField ? 1 : 0);                   // 1 bit   » 32-bit
    result = 31 * result + byteField;                                // 8 bits  » 32-bit 
    result = 31 * result + charField;                                // 16 bits » 32-bit
    result = 31 * result + shortField;                               // 16 bits » 32-bit
    result = 31 * result + intField;                                 // 32 bits » 32-bit
    result = 31 * result + (int)(longField ^ (longField >>> 32));    // 64 bits » 32-bit
    result = 31 * result + Float.floatToIntBits(floatField);         // 32 bits » 32-bit
    long doubleFieldBits = Double.doubleToLongBits(doubleField);     // 64 bits (double) » 64-bit (long) » 32-bit (int)
    result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));
    // Objects
    result = 31 * result + Arrays.hashCode(arrayField);              // var bits » 32-bit
    result = 31 * result + referenceField.hashCode();                // var bits » 32-bit (non-nullable)   
    result = 31 * result +                                           // var bits » 32-bit (nullable)   
        (nullableReferenceField == null
            ? 0
            : nullableReferenceField.hashCode());
    return result;
}

Hash Function Joshua Bloch

Collision

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Collision

Copyright © 直通硅谷

http://www.zhitongguigu.com/

No matter which function, you are using, you have to deal with collision

 

reason that there is collision:

  1. hash function is not perfect
  2. some keys just happen to map to the same index
  3. keys > slots

Collision

Copyright © 直通硅谷

http://www.zhitongguigu.com/

How to solve them:

  1. We cannot, just try to find a better algo
  2. Open hashing
  3. Closed hashing
  4. Expand the space (Load Factor)

Load factor: size/capacity

Normally if (LF > 0.75), we double the space. 

Collision

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Open hashing:

for each collision, we use a linked list to store them

Closed hashing: 

we store them in somewhere else in the table

Open hashing

Copyright © 直通硅谷

http://www.zhitongguigu.com/

🤔get(16);

🤔remove(24);

Closed hashing

Copyright © 直通硅谷

http://www.zhitongguigu.com/

🤔get(16);

🤔remove(24);

Hash Function Key Points

Copyright © 直通硅谷

http://www.zhitongguigu.com/

  1. Hash function is not random, given the same key, you can always find the corresponding hashing value.
  2. Easy and quick to compute.
  3. Distribution as even as possible

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

These two functions are very important in Java 

We can override them and it will affect our HashMap collision and basic operations 

Can we have Map<int, Object>?🤔

Hashcode & Equals example

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public class Test{
    private int num;
    private String data;

    public boolean equals(Object obj){
        if(this == obj) {
            return true;
        }
        if((obj == null) || (obj.getClass() != this.getClass())) {
            return false;
        }
        // object must be Test at this point
        Test test = (Test)obj;
        return num == test.num &&
        (data == test.data || (data != null && data.equals(test.data)));
    }

    public int hashCode(){
        int hash = 7;
        hash = 31 * hash + num;
        hash = 31 * hash + (null == data ? 0 : data.hashCode());
        return hash;
    }

    // other methods...
}

🤔

Override & Overload

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public boolean equals(Test test){
    if(this == test) {
        return true;
    }
    if((test == null)) {
        return false;
    }
    return num == test.num &&
    (data == test.data || (data != null && data.equals(test.data)));
}

@Override
public boolean equals(Object obj){
    if(this == obj) {
        return true;
    }
    if((obj == null) || (obj.getClass() != this.getClass())) {
        return false;
    }
    // object must be Test at this point
    Test test = (Test)obj;
    return num == test.num &&
    (data == test.data || (data != null && data.equals(test.data)));
}

class Dog{
    public void bark(){
        System.out.println("woof ");
    }
 
    //overloading method
    public void bark(int num){
    	for(int i=0; i<num; i++)
            System.out.println("woof ");
    }
}

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

🤔equals <-> hashcode

equals --> hashcode

hashcode --> equals

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Primitive Type Wrapper class:

Integer, Short, Long, Byte,Char, Float, Double, Boolean

 

For these class, equals is same as ==

🤔

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}
// Output: false
class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}
// Output: true

// This Integer caching works only on autoboxing
Integer b2 =Integer.valueOf(127)

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Primitive Type Wrapper class:

Short, Long, Byte, Char, Float, Double, Boolean

For these class, equals is same as ==

 

Unless compare primitive type. Don't use ==

Don't rely on two references being identical.

Always use .equals()

 

Should always override .equals() for data objects.

If no override, find its parent class till Object (==)

Hashcode & Equals

Copyright © 直通硅谷

http://www.zhitongguigu.com/

During implementation

If obj1.equals(obj2) return true

then must have

obj1.hashcode() == obj2.hashcode()

// Object equals will be same as ==
public boolean equals(Object obj) {
    return (this == obj);
}

Java Map & Set

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Hash Tree LinkedHash
Map HashMap TreeMap LinkedHashMap
Set HashSet TreeSet LinkedHashSet

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Different Maps and Sets

Two Sum

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given an array of integers (no duplicate), 
find two numbers such that they add up to a specific target number.
The function twoSum should return the two numbers such that 
they add up to the target, where number1 must be less than number2.
You may assume that each input would have exactly one solution.

​Example:
Input: numbers={2, 7, 11, 15}, target=9
Output: {2, 7}
public int[] twoSum(int[] nums, int target) {
    // TODO: implement this function.
}

Two Sum [Return Value]

Two Sum [Return Index]

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Difference? 

What's the previous question?

What's the previous solution?

Will it work?

Given an array of integers, return indices of the two numbers 
such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:
Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]

🤔

Two Sum [Return Index]

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given an array of integers, return indices of the two numbers 
such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:
Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]
Two Sum Return Value Two Sum Return Index
Map current hw current example
Sort previous hw [harder]optional hw

🤔

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        map.put(nums[i], i);
    }
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement) && map.get(complement) != i) {
            return new int[] { i, map.get(complement) };
        }
    }
    throw new IllegalArgumentException("No two sum solution");
}

Two Sum [Return Index]

🤔

duplicates? [4, 2, 4] t=8

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] {map.get(complement), i};
        }
        map.put(nums[i], i);
    }
    return null;
}

Two Sum [Return Index]

Time Space
Sort
HashMap

O(nlogn)

O(1)

O(n)

O(n)

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given a pattern and a string str, find if str follows the same pattern.

pattern = "abba", str = "dog cat cat dog" should return true.
pattern = "abba", str = "dog cat cat fish" should return false.
pattern = "aaaa", str = "dog cat cat dog" should return false.
pattern = "abba", str = "dog dog dog dog" should return false.

Notes:

  1. You may assume pattern contains only lowercase letters,
  2. str contains lowercase letters separated by a single space.

Word Pattern

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public class Solution {
    public boolean wordPattern(String pattern, String str) {
        Map<Character, String> hm = new HashMap<>();
        Map<String, Character> hm_r = new HashMap<>();
        String[] words = str.split(" ");
        if (pattern.length() != words.length) {
            return false;
        }
        for (int i = 0; i < pattern.length(); i++) {
            char a = pattern.charAt(i);
            String b = words[i];
            if (!hm.containsKey(a)) {
                hm.put(a, b);
            } else if (!hm.get(a).equals(b)){
                return false;
            }
            if (!hm_r.containsKey(b)) {
                hm_r.put(b, a);
            } else if (hm_r.get(b) != a) {
                return false;
            }
        }
        return true;
    }
}

🤔

word

&

char

Word Pattern

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public boolean wordPattern(String pattern, String str) {
    Map<Character, Integer> hm = new HashMap<>();
    Map<String, Integer> hm_r = new HashMap<>();
    String[] words = str.split(" ");
    if (pattern.length() != words.length) {
        return false;
    }
    for (int i = 0; i < pattern.length(); i++) {
        if (!Objects.equals(hm.put(pattern.charAt(i), i), hm_r.put(words[i], i))) {
            return false;
        }
    }
    return true;
}
  1. Use the return value of put method
  2. Character - index - String

Word Pattern

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public boolean wordPattern(String pattern, String str) {
    Map<Object, Object> hm = new HashMap<>();
    String[] words = str.split(" ");
    if (words.length != pattern.length())
        return false;
    for (int i=0; i<words.length; i++)
        if (!Objects.equals(hm.put(pattern.charAt(i), i), hm.put(words[i], i)))
            return false;
    return true;
}

public boolean wordPattern(String pattern, String str) {
    Map hm = new HashMap();
    String[] words = str.split(" ");
    if (words.length != pattern.length())
        return false;
    for (int i=0; i<words.length; i++)
        if (!Objects.equals(hm.put(pattern.charAt(i), i), hm.put(words[i], i)))
            return false;
    return true;
}

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given an array of strings, group anagrams together.

For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"] 

  Note

  1. For the return value, each inner list's elements must follow the lexicographic order.
  2. All inputs will be in lower-case.
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

Group Anagrams

Copyright © 直通硅谷

http://www.zhitongguigu.com/

How to know two words are anagrams?

 

"ate" and "eat" -> 

when we sort them, ate and eat both become "aet"

So we need a hashmap to tell if we already have the list or not. 

Group Anagrams

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public List<List<String>> groupAnagrams(String[] strs) {
    HashMap<String, List<String>> stringmap = new HashMap();
    List<List<String>> result = new ArrayList();
    for(String str: strs) {
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        String sorted = new String(chars);
        if(stringmap.containsKey(sorted)) {
            stringmap.get(sorted).add(str);
        } else {
            ArrayList<String> stringList = new ArrayList();
            stringList.add(str);
            stringmap.put(sorted, stringList);
        }    
    }
    for(Map.Entry<String, List<String>> entry: stringmap.entrySet()) {
        List stringresult = entry.getValue();
        Collections.sort(stringresult);
        result.add(stringresult);
    }
    return result;
}

🤔

Group Anagrams

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public List<List<String>> groupAnagrams(String[] strs) {
    HashMap<String, List<String>> hm = new HashMap();
    List<List<String>> result = new ArrayList();
    for(String s: strs) {
        char[] chars = s.toCharArray();
        Arrays.sort(chars);
        String sorted = new String(chars);
        if(!hm.containsKey(sorted)) {
            hm.put(sorted, new ArrayList());
        }
        hm.get(sorted).add(s);   
    }
    // keySet -> list and sort will make outer list lexicographic
    for (List<String> list : hm.values()) {
        Collections.sort(list);
        result.add(list);
    }
    // for (String key : hm.keySet()) {
    //     Collections.sort(hm.get(key));
    //     result.add(hm.get(key));
    // }
    return result;
}

Group Anagrams

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given an array of strings, group anagrams together.

For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"] 

  Note

  1. For the return value, each inner list's elements must follow the lexicographic order.
  2. All inputs will be in lower-case.
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

What about no sorting?

🤔

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", the answer is "wke", with the length of 3. 

Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given "abbcdefdgh", the answer is "bcdef", which the length is 5.

0 1 2 3 4 5 6 7 8 9
a b b c d e f d g h
_ _ i _ _ _ _ j _ _

// Sliding Window
// Two Pointers

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int lengthOfLongestSubstring(String s) {
    int result = 0;
    if (s == null || s.length() == 0) {
        return 0;
    }
    char[] arr = s.toCharArray();
    Map<Character, Integer> hm = new HashMap<>();
    for (int i = 0, j = 0; i < arr.length; i++) {
        char c = arr[i];
        if (hm.containsKey(c)) {
            j = Math.max(j, hm.get(c) + 1);
        }
        hm.put(c,i);
        result = Math.max(result, i - j + 1);
    }
    return result;
}

🤔You may not use Map

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int lengthOfLongestSubstring(String s) {
    int result = 0;
    int[] cache = new int[256];
    for (int i = 0, j = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (cache[c] > 0) {
            j = Math.max(j, cache[c]);
        }
        cache[c] = i + 1;
        result = Math.max(result, i - j + 1);
    }
    return result;
}

The idea of Hashing

🤔Is cache[c] > 0 necessary?

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int lengthOfLongestSubstring(String s) {
    int result = 0;
    int[] cache = new int[256];
    for (int i = 0, j = 0; i < s.length(); i++) {
        j = Math.max(j, cache[s.charAt(i)]);
        cache[s.charAt(i)] = i + 1;
        result = Math.max(result, i - j + 1);
    }
    return result;
}

🤔You may not use 256 space

Copyright © 直通硅谷

http://www.zhitongguigu.com/

public int lengthOfLongestSubstring(String s) {
    int result = 0;
    int[] cache = new int[128];
    for (int i = 0, j = 0; i < s.length(); i++) {
        j = Math.max(j, cache[s.charAt(i)]);
        cache[s.charAt(i)] = i + 1;
        result = Math.max(result, i - j + 1);
    }
    return result;
}

// If only low case is used
public int lengthOfLongestSubstring(String s) {
    int result = 0;
    int[] cache = new int[26];
    for (int i = 0, j = 0; i < s.length(); i++) {
        j = Math.max(j, cache[s.charAt(i) - 'a']);
        cache[s.charAt(i) - 'a'] = i + 1;
        result = Math.max(result, i - j + 1);
    }
    return result;
}

HashSet

HashMap HashSet
put(key, value) add(element)
get(key)
remove(key) remove(object)
containsKey(key) contains(object)
containsValue(value)
entrySet()
keySet()
values()
size(); isEmpty(); putAll(Map<>); size(); isEmpty(); addAll(Collection);

Java Map & Set

Copyright © 直通硅谷

http://www.zhitongguigu.com/

╔══════════════╦═════════════════════╦═══════════════════╦══════════════════════╗
║   Property   ║       HashMap       ║      TreeMap      ║     LinkedHashMap    ║
╠══════════════╬═════════════════════╬═══════════════════╬══════════════════════╣
║              ║  no guarantee order ║ sorted according  ║                      ║
║   Order      ║ will remain constant║ to the natural    ║    insertion-order   ║
║              ║      over time      ║    ordering       ║                      ║
╠══════════════╬═════════════════════╬═══════════════════╬══════════════════════╣
║  Get/put     ║                     ║                   ║                      ║
║   remove     ║         O(1)        ║      O(log(n))    ║         O(1)         ║
║ containsKey  ║                     ║                   ║                      ║
╠══════════════╬═════════════════════╬═══════════════════╬══════════════════════╣
║              ║                     ║   NavigableMap    ║                      ║
║  Interfaces  ║         Map         ║       Map         ║         Map          ║
║              ║                     ║    SortedMap      ║                      ║
╠══════════════╬═════════════════════╬═══════════════════╬══════════════════════╣
║              ║                     ║                   ║                      ║
║     Null     ║       allowed       ║    only values    ║       allowed        ║
║ values/keys  ║                     ║                   ║                      ║
╠══════════════╬═════════════════════╩═══════════════════╩══════════════════════╣
║              ║   Fail-fast behavior of an iterator cannot be guaranteed       ║
║   Fail-fast  ║ impossible to make any hard guarantees in the presence of      ║
║   behavior   ║           unsynchronized concurrent modification               ║
╠══════════════╬═════════════════════╦═══════════════════╦══════════════════════╣
║              ║                     ║                   ║                      ║
║Implementation║      buckets        ║   Red-Black Tree  ║    double-linked     ║
║              ║                     ║                   ║       buckets        ║
╠══════════════╬═════════════════════╩═══════════════════╩══════════════════════╣
║      Is      ║                                                                ║
║ synchronized ║              implementation is not synchronized                ║
╚══════════════╩════════════════════════════════════════════════════════════════╝

Copyright © 直通硅谷

http://www.zhitongguigu.com/

Homework 7:

Copyright © 直通硅谷

http://www.zhitongguigu.com/

[GoValley-201612] HashMap

By govalley201612

[GoValley-201612] HashMap

20161230 HashMap

  • 982