Container [Double Case Collection, Use of TreeMap Container, Iterator Interface, Collections Tool Class] (4) - Comprehensive Detailed Explanation (Learning Summary --- From Getting Started to Deepening)

 

 

Table of contents

Implement comparison rules through the elements themselves

Implementation of comparison rules through comparators

set of doubles

Use of TreeMap container

 Iterator interface

Collections tool class


 

Implement comparison rules through the elements themselves

 

 When the element itself implements the comparison rules, it needs to implement the compareTo method in the Comparable interface, which is used to define the comparison rules. TreeSet finishes sorting the elements by calling this method.

 Create the Users class

public class Users implements Comparable<Users>{
    private String username;
    private int userage;
    public Users(String username, intuserage) {
        this.username = username;
        this.userage = userage;
   }
   public Users() { }
  
  @Override
    public boolean equals(Object o) {
        System.out.println("equals...");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Users users = (Users) o;
        if (userage != users.userage) return false;
        return username != null ? username.equals(users.username) : users.username == null;
   }
    @Override
    public int hashCode() {
        int result = username != null ? username.hashCode() : 0;
        result = 31 * result + userage;
        return result;
   }
    public String getUsername() {
        return username;
   }

   public void setUsername(String username)
    {
        this.username = username;
   }
   
   public int getUserage() {
        return userage;
   }
   
   public void setUserage(int userage) {
        this.userage = userage;
   }
   
 @Override
  public String toString() {
        return "Users{" +
                "username='" + username + '\'' +
                ", userage=" + userage +
                '}';
   }
    //定义比较规则
    //正数:大,负数:小,0:相等
    @Override
    public int compareTo(Users o) {
        if(this.userage > o.getUserage()){
            return 1;
       }
        if(this.userage == o.getUserage()){
           return this.username.compareTo(o.getUsername());
      }
        return -1;
   }
}
Set<Users> set1 = new TreeSet<>();
Users u = new Users("oldlu",18);
Users u1 = new Users("admin",22);
Users u2 = new Users("sxt",22);
set1.add(u);
set1.add(u1);
set1.add(u2);
for(Users users:set1){
    System.out.println(users);
}

Implementation of comparison rules through comparators

 When defining comparison rules through a comparator, we need to create a comparator separately, and the comparator needs to implement the compare method in the Comparator interface to define the comparison rules. When the TreeSet is instantiated, the comparator object is handed over to the TreeSet to complete the sorting of the elements. In this case, the element itself does not need to implement the comparison rules.

Create the Student class

public class Student {
    private String name;
    private int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
   }
    public Student() { }
    
  @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
  
   }
    public String getName() {
        return name;
   }
  
   public void setName(String name) {
        this.name = name;
   }
    public int getAge() {
        return age;
   }
    public void setAge(int age) {
        this.age = age;
   }
    @Override
   public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
   }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
   }
}

Create a comparator

public class StudentComparator implements Comparator<Student> {
    //定义比较规则
    @Override
    public int compare(Student o1, Student o2) {
        if(o1.getAge() > o2.getAge()){
            return 1;
       }
       
        if(o1.getAge() == o2.getAge()){
            return o1.getName().compareTo(o2.getName());
       }
      return -1;
   }
}
public class TreeSetTest3 {
    public static void main(String[] args) {
        //创建TreeSet容器,并给定比较器对象
        Set<Student> set = new TreeSet<>(new StudentComparator());
        Student s = new Student("oldlu",18);
        Student s1 = new Student("admin",22);
        Student s2 = new Student("sxt",22);
        set.add(s);
        set.add(s1);
        set.add(s2);
        for(Student student:set){
            System.out.println(student);
       }
   }
}

TreeSet underlying source code analysis

Member variables

/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

Construction method

public TreeSet() {
    this(new TreeMap<E,Object>());
}

add element

/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null&nbsp;? &nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

Singleton Collection Use Cases

Requirements: Generate random numbers between 1-10 ([1,10] closed interval), and put 10 non-repeating random numbers into the container.

 Implemented using List type container

public class ListDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
       while(true){
           //产生随机数
           int num = (int) (Math.random()*10+1);
            //判断当前元素在容器中是否存在
           if(!list.contains(num)){
                list.add(num);
           }
           //结束循环
           if(list.size() == 10){
               break;
           }
       }
       for(Integer i:list){
           System.out.println(i);
       }
   }
}

Implemented using Set type container

public class SetDemo {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        while(true){
            int num = (int)(Math.random()*10+1);
            //将元素添加容器中,由于Set类型容器是、不允许有重复元素的,所以不需要判断。
            set.add(num);
            //结束循环
            if(set.size() == 10){
                break;
           }
       }
        for(Integer i:set){
            System.out.println(i);
       }
   }
}

set of doubles

 Introduction to the Map interface

The Map interface defines the storage characteristics of a double-instance collection, and it is not a sub-interface of the Collection interface. The storage feature of the double instance set is stored in units of key and value structures. It embodies the concept of the function y=f(x) in mathematics.

The difference between Map and Collecton:

 1. For the container in the Collection, the elements exist in isolation (understood as a single), and the elements are stored in the collection one by one.

2. In the container in Map, elements exist in pairs (understood as husband and wife in modern society). Each element consists of a key and a value, and the corresponding value can be found through the key.

3. The container in the Collection is called a single-column collection, and the container in the Map is called a double-column collection.

4. The collection in the Map cannot contain repeated keys, and the values ​​can be repeated; each key can only correspond to one value.

5. Commonly used containers in Map are HashMap, TreeMap, etc.

Method table commonly used in the Map interface

 Use of HashMap container

HashMap is implemented by hash algorithm and is the most commonly used implementation class of Map interface. Since the bottom layer uses a hash table to store data, we require that the key cannot be repeated. If there is a duplication, the new key-value pair will replace the old key-value pair. HashMap has very high efficiency in searching, deleting and modifying.

public class HashMapTest {
    public static void main(String[] args) {
        //实例化HashMap容器
        Map<String,String> map = new HashMap<>();
        //添加元素
        map.put("a","A");
        map.put("b","B");
        map.put("c","C");
        map.put("a","D");
        //获取容器中元素数量
        int size = map.size();
        System.out.println(size);
        System.out.println("---------------");
    
        //获取元素
        //方式一
        String v = map.get("a");
        System.out.println(v);
        System.out.println("---------------");
        
        //方式二
        Set<String> keys = map.keySet();
        for(String key:keys){
            String v1 = map.get(key);
            System.out.println(key+" ----"+v1);
       }
        System.out.println("-------------------");
        
        //方式三
        Set<Map.Entry<String,String>> entrySet = map.entrySet();
        for(Map.Entry<String,String> entry:entrySet){
            String key = entry.getKey();
            String v2 = entry.getValue();
            System.out.println(key+" ---------- "+v2);
       }
        System.out.println("--------------------");
        //Map容器的并集操作
        Map<String,String> map2 = new HashMap<>();
        map2.put("f","F");
        map2.put("c","CC");
        map.putAll(map2);
        Set<String> keys2 = map.keySet();
        for(String key:keys2){
            System.out.println("key: "+key+" Value: "+map.get(key));
       }
        System.out.println("---------------");
        //删除元素
        String v3 = map.remove("a");
        System.out.println(v3);
        Set<String> keys3 = map.keySet();
        for(String key:keys3){
            System.out.println("key: "+key+" Value: "+map.get(key));
       }
        System.out.println("-------------------");
        //判断Key是否存在
        boolean b = map.containsKey("b");
        System.out.println(b);
        //判断Value是否存在
        boolean cc = map.containsValue("CC");
        System.out.println(cc);

    }
}

The usage of the HashTable class is almost the same as that of HashMap, and the underlying implementation is almost the same, except that the method of HashTable adds the synchronized keyword to ensure thread synchronization check, which is less efficient.

The difference between HashMap and HashTable

1 HashMap: thread insecure, high efficiency. Allow key or value to be null

2 HashTable: thread safe, low efficiency. The key or value is not allowed to be null

 Analysis of the underlying source code of HashMap

Introduction to underlying storage

The underlying implementation of HashMap uses a hash table, which is a very important data structure. It will be very helpful for us to understand many technologies in the future. In the data structure, arrays and linked lists are used to store data, and they have their own characteristics.

(1) Array: occupies a continuous space. Addressing is easy and query speed is fast. However, addition and deletion are very inefficient.

(2) Linked list: the occupied space is not continuous. Addressing is difficult and query speed is slow. However, additions and deletions are very efficient. So, can we combine the advantages of arrays and linked lists (that is, fast query, high addition and deletion efficiency)? The answer is "hash table". The essence of the hash table is "array + linked list".

 

 Oldlu suggested

For the explanation of "lower level implementation" that frequently appears in this chapter, it is recommended that children's shoes with enough energy to learn it. If you find it difficult for children's shoes just getting started, you can skip it temporarily. During the entry period, you only need to master how to use it. The underlying principle is solid internal skills, which is convenient for everyone to deal with the written test interviews of some large companies.

 Member variables in HashMap

/**
* The default initial capacity - MUST be a power of two.
*/

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/

static final int MAXIMUM_CAPACITY = 1 << 30;

/**
* The load factor used when none specified in constructor.
*/

static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/

static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/

static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* The number of key-value mappings contained in this map.
*/

transient int size;
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/

transient Node<K,V>[] table;

Node type for storing elements in HashMap

 Node class

static class Node<K,V> implements
Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
   }
    
public final K getKey()       { return key; }
    
public final V getValue()     { return value; }
    
public final String toString() { return key + "=" + value; }
    
public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); }
    
public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
   }
    
public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                return true;
       }
        return false;
   }
}

TreeNode class

/**
* Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
* extends Node) so can be used as extension of either regular or
* linked node.
*/
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
   }
    /**
     * Returns root of tree containing thisnode.
     */
    final TreeNode<K,V> root() {
        for (TreeNode<K,V> r = this, p;;) {
            if ((p = r.parent) == null)
                return r;
            r = p;
       }
   }

their inheritance

 Array initialization in HashMap

In the HashMap of JDK1.8, the lazy initialization method is adopted for the initialization of the array. The initialization process is realized through the resize method. The resize method not only implements array initialization, but also implements array expansion processing.

/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-oftwo expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
       }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
   }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initialthreshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
   }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                 (int)ft : Integer.MAX_VALUE);
   }
    threshold = newThr;
  
@SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
        table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                   
            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null,loTail = null;
                    Node<K,V> hiHead = null,hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail ==null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                       }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                       }
                   } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                   }
                   if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                   }
               }
           }
       }
   }
    return newTab;
}

Calculate Hash value in HashMap

1 Get the hashcode of the key object

First call the hashcode() method of the key object to obtain the hashcode value of the key.

2 Calculate the hash value according to the hashcode (required to be in the range of [0, array length-1]). The hashcode is an integer, and we need to convert it into the range of [0, array length-1]. We require the converted hash values ​​to be distributed as evenly as possible in the interval [0, array length - 1] to reduce "hash conflicts"

     2.1 An extremely simple and low-level algorithm is: hash value = hashcode/hashcode; that is, the hash value is always 1. It means that the key-value pair object will be stored in the index 1 position of the array, thus forming a very long linked list. It is equivalent to "hash conflict" every time an object is stored, and HashMap degenerates into a "linked list".

     2.2 A simple and commonly used algorithm is (division and remainder algorithm): hash value = hashcode% array length;

     This algorithm allows hash values ​​to be evenly distributed in the interval [0, array length - 1]. However, this algorithm is inefficient due to the use of "division". JDK later improved the algorithm. First, it is stipulated that the length of the array must be an integer power of 2, so that the effect of taking the remainder can be achieved by using bit operations: hash value = hashcode&(array length-1).

/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
*         (A <tt>null</tt> return can also indicate that the map
*         previously associated <tt>null</tt> with <tt>key</tt>.)
*/

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value,
boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
               }
                if (e.hash == hash &&
                   ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
           }
       }
        if (e != null) { // existing mapping 
       for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;afterNodeAccess(e);
            return oldValue;
       }
   }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

Add elements to HashMap

/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
*         <tt>null</tt> if there was no mapping for <tt>key</tt>.
*         (A <tt>null</tt> return can also indicate that the map
*         previously associated <tt>null</tt> with <tt>key</tt>.)
*/

public V put(K key, V value) {
    return putVal(hash(key), key, value,false, true);
}

Array expansion in HashMap

/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change
existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
  tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
           ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab,hash);
                    break;
               }
                if (e.hash == hash &&
                   ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
           }
       }
if (e != null) { // existing mapping
       for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
       }
   }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-oftwo expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
     Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
       }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
   }
    else if (oldThr > 0) // initial capacity
        was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
   }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
        (int)ft : Integer.MAX_VALUE);
   }
    threshold = newThr;
  
@SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
        table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                   
                 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                          if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                       }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                       }
                   } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                   }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                   }
               }
           }
       }
   }
 return newTab;
}

Use of TreeMap container

 TreeMap and HashMap also implement the Map interface, so there is no difference in API usage. HashMap is more efficient than TreeMap; TreeMap is a container that can sort keys, and TreeMap can be used when keys need to be sorted. The bottom layer of TreeMap is implemented based on red-black tree.

The collation needs to be given when using TreeMap:

1. Elements themselves implement comparison rules

2. Implement comparison rules through comparators

 Elements themselves implement comparison rules

public class Users implements
Comparable<Users>{
    private String username;
    private int userage;
public Users(String username, int userage) {
        this.username = username;
        this.userage = userage;
   }
    public Users() { }
    @Override
    public boolean equals(Object o) {
        System.out.println("equals...");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Users users = (Users) o;
        if (userage != users.userage) return false;
        return username != null ? username.equals(users.username) : users.username == null;
   }
    @Override
    public int hashCode() {
        int result = username != null ? username.hashCode() : 0;
        result = 31 * result + userage;
        return result;
}
    public String getUsername() {
        return username;
   }
    public void setUsername(String username)
     {
        this.username = username;
     }
    public int getUserage() {
        return userage;
     }
    public void setUserage(int userage) {
        this.userage = userage;
   }
    @Override
    public String toString() {
        return "Users{" +
                "username='" + username + '\'' +
                ", userage=" + userage +
                '}';
   }
    //定义比较规则
    //正数:大,负数:小,0:相等
    @Override
    public int compareTo(Users o) {
       if(this.userage < o.getUserage()){
            return 1;
       }
        if(this.userage == o.getUserage()){
           return this.username.compareTo(o.getUsername());
       }
        return -1;
   }
}
public class TreeMapTest {
    public static void main(String[] args) {
        //实例化TreeMap
        Map<Users,String> map = new TreeMap<>();
        Users u1 = new Users("oldlu",18);
        Users u2 = new Users("admin",22);
        Users u3 = new Users("sxt",22);
        map.put(u1,"oldlu");
        map.put(u2,"admin");
        map.put(u3,"sxt");
        Set<Users> keys = map.keySet();
        for(Users key :keys){
            System.out.println(key+" --------- "+map.get(key));
       }
   }
}

 Implementation of comparison rules through comparators

public class Student {
    private String name;
    private int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
   }
    public Student() { }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
   }
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
   }
    public int getAge() {
        return age;
   }
  public void setAge(int age) {
        this.age = age;
   }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
   }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
   }
}
public class StudentComparator implements Comparator<Student> {
    //定义比较规则
  @Override
    public int compare(Student o1, Student o2) {
        if(o1.getAge() > o2.getAge()){
            return 1;
       }
        if(o1.getAge() == o2.getAge()){
            return
           o1.getName().compareTo(o2.getName());
       }
        return -1;
   }
}
public class TreeMapTest {
    public static void main(String[] args) {
          Map<Student,String> treeMap = new TreeMap<>(new StudentComparator());
    Student s1 = new Student("oldlu",18);
    Student s2 = new Student("admin",22);
    Student s3 = new Student("sxt",22);
    treeMap.put(s1,"oldlu");
    treeMap.put(s2,"admin");
    treeMap.put(s3,"sxt");
    Set<Student> keys1 = treeMap.keySet();
    for(Student key :keys1){
        System.out.println(key+" ----"+treeMap.get(key));
     }
   }
}

Analysis of the underlying source code of TreeMap

TreeMap is a typical implementation of a red-black binary tree. We opened the source code of TreeMap and found that there is a line of core code in it:

private transient Entry<K,V> root = null;

 root is used to store the root node of the entire tree. We continue to trace the code of Entry (which is the inner class of TreeMap):

 

 You can see that it stores its own data, left node, right node, parent node, and node color. The put()/remove() methods of TreeMap make extensive use of the theory of red-black trees. In this lesson, no further expansion. If you need to know more in-depth, you can refer to special data structure books. TreeMap and HashMap implement the same interface Map, so there is no difference in usage for the caller. HashMap is more efficient than TreeMap; TreeMap is only used when a sorted Map is required.

 Iterator interface

 Iterator iterator interface introduction

The Collection interface inherits the Iterable interface, which contains an abstract method named iterator, and all container classes that implement the Collection interface implement this method concretely. The iterator method returns an iterator object of the Iterator interface type, which contains three methods for iterating over the singleton container.

 How Iterator Objects Work

 The Iterator interface defines the following methods:

1 boolean hasNext(); //Determine whether there are elements at the next position of the current position of the cursor that have not been traversed;

2 Object next(); //Return the next element at the current position of the cursor and move the cursor to the next position;

3 void remove(); //Delete the element at the current position of the cursor, this operation can only be performed once after executing next;

 The use of Iterator iterator

Iterate List interface type container

public class IteratorListTest {
    public static void main(String[] args) {
        //实例化容器
        List<String> list  = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        //获取元素
        //获取迭代器对象
        Iterator<String> iterator = list.iterator();
        //方式一:在迭代器中,通过while循环获取元素
        while(iterator.hasNext()){
            String value = iterator.next();
            System.out.println(value);
       }
        System.out.println("-------------------------------");
        //方法二:在迭代器中,通过for循环获取元素
        for(Iterator<String> it = list.iterator();it.hasNext();){
            String value = it.next();
            System.out.println(value);
       }
   }
}

Iterate Set interface type container

public class IteratorSetTest {
    public static void main(String[] args) {
        //实例化Set类型的容器
        Set<String> set  = new HashSet<>();
        set.add("a");
        set.add("b");
        set.add("c");
        //方式一:通过while循环
        //获取迭代器对象
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            String value = iterator.next();
            System.out.println(value);
           }
        System.out.println("-------------------------");
        //方式二:通过for循环
        for(Iterator<String> it = set.iterator();it.hasNext();){
            String value = it.next();
            System.out.println(value);
       }
   }
}

Iterate over the Map interface type container

public class IteratorMapTest {
    public static void main(String[] args) {
        //实例化HashMap容器
        Map<String, String> map = new HashMap<String, String>();
        //添加元素
        map.put("a", "A");
        map.put("b", "B");
        map.put("c", "C");
        //遍历Map容器方式一
        Set<String> keySet = map.keySet();
        for (Iterator<String> it = keySet.iterator(); it.hasNext();){
            String key = it.next();
            String value = map.get(key);
            System.out.println(key+" ------------- "+value);
       }
        System.out.println("------------------------");
        //遍历Map容器方式二
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
        while(iterator.hasNext()){
            Map.Entry entry = iterator.next();
          
         System.out.println(entry.getKey()+" ------------ "+ entry.getValue());
       }
   }
}

remove elements from iterator

public class IteratorRemoveTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            //不要在一次循环中多次调用next方法。
            String value = iterator.next();
            iterator.remove();
       }
        System.out.println("----------------");
        for(Iterator<String> it = list.iterator();
           it.hasNext();){
            System.out.println(it.next());
            list.add("dddd");
       }
   }
}

Summary of methods for traversing collections

Traversing the List method one: ordinary for loop

for(int i=0;i<list.size();i++){//list为集合的对象名
 String temp = (String)list.get(i);
 System.out.println(temp);
}

Traversing List Method 2: Enhanced for loop (using generics!)

for (String temp : list) {
 System.out.println(temp);
}

Traversing the List method three: using the Iterator iterator (1)

for(Iterator iter=
list.iterator();iter.hasNext();){
 String temp = (String)iter.next();
 System.out.println(temp);
}

Traversing the List method four: using the Iterator iterator (2)

Iterator  iter =list.iterator();
while(iter.hasNext()){
 Object  obj =  iter.next();
 iter.remove();//如果要遍历时,删除集合中的元素,建议使用这种方式!
 System.out.println(obj);
}

Traversing Set Method 1: Enhanced for loop

for(String temp:set){
 System.out.println(temp);
}

Traversing the Set method two: using the Iterator iterator

for(Iterator iter =
set.iterator();iter.hasNext();){
 String temp = (String)iter.next();
 System.out.println(temp);
}

Traversing the Map Method 1: Get the value according to the key

Map<Integer, Man> maps = new HashMap<Integer,
Man>();
Set<Integer>  keySet =  maps.keySet();
for(Integer id : keySet){
 System.out.println(maps.get(id).name);
}

Traverse Map method two: use entrySet

Set<Map.Entry<Integer, Man>>  ss =
maps.entrySet();
for (Iterator<Map.Entry<Integer, Man>>
iterator = ss.iterator();
iterator.hasNext();) {
 Map.Entry e =  iterator.next();
 System.out.println(e.getKey()+"--
"+e.getValue());
}

Collections tool class

 Class java.util.Collections provides auxiliary methods for sorting, filling and finding elements of Set, List and Map.

 Common methods of Collections tool class

public class CollectionsTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("c");
        list.add("b");
        list.add("a");
        //对元素排序
        Collections.sort(list);
        for(String str:list){
            System.out.println(str);
       }
        System.out.println("-------------------");
        List<Users> list2 = new ArrayList<>();
        Users u = new Users("oldlu",18);
        Users u2 = new Users("sxt",22);
        Users u3 = new Users("admin",22);
        list2.add(u);
        list2.add(u2);
        list2.add(u3);
        //对元素排序
        Collections.sort(list2);
        for(Users user:list2){
            System.out.println(user);
       }
        System.out.println("-------------------");
        List<Student> list3 = new ArrayList<>();
        Student s = new Student("oldlu",18);
        Student s1 = new Student("sxt",20);
        Student s2 = new Student("admin",20);
        
        list3.add(s);
        list3.add(s1);
        list3.add(s2);
        
        Collections.sort(list3,new StudentComparator());
        for(Student student:list3){
            System.out.println(student);
       }
        System.out.println("-------------------");
        List<String> list4 = new ArrayList<>();
        list4.add("a");
        list4.add("b");
        list4.add("c");
        list4.add("d");
        //洗牌
        Collections.shuffle(list4);
        for(String str:list4){
            System.out.println(str);
       }
   }
}

Guess you like

Origin blog.csdn.net/m0_58719994/article/details/131670223