Use of Java collections

Posted by stitchmedia on Sun, 17 Oct 2021 19:56:56 +0200

Overview of the collection framework

Collection and array are structures that store multiple data, which are referred to as Java containers for short.

Storage at this time mainly refers to memory level storage and does not involve persistent storage (such as. txt,. jpg, database, etc.)

The characteristics of arrays in storing multiple data

  • Once initialized, the length is determined
  • When defining an array, you need to specify the element type of the array (you must store what type of data you declare)

For example: String[] arr; int[] arri;

Array has the disadvantage of storing multiple data

  1. Once the length is determined, it cannot be modified
  2. Only a single type of data can be stored (advantages (more strict control of data) and disadvantages (inconvenient when there are multiple types of data), depending on the demand)
  3. The methods provided in the array are relatively limited. It is very inconvenient and inefficient to add, delete and insert data
  4. The requirement to obtain the number of actual elements in the array (for example, there are several available elements in an array with a length of 10). The array has no ready-made properties or methods to use
  5. The characteristics of array storage data: orderly and repeatable; For requirements that are out of order and cannot be repeated, arrays cannot be implemented

Java collection classes can be used to store multiple objects with different numbers, and can also be used to store associative arrays with mapping relationships

Java collections can be divided into Collection and Map systems

Collection interface

Single column data, single column collection, used to store objects one by one

  • List: store ordered and repeatable data (common: ArrayList, LinkedList, Vector)

  • Set: store unordered and non repeatable data (common: HashSet, LinkedHashSet, ThreeSet)

Map interface

A two column set is used to store a pair of key value and a pair of key value data

Common: HashMap, LinkedHashMap, ThreeMap, Hashtable, Properties

Use of Collection interface

explain

  • The Collection interface is the parent interface of the List, Set and Queue interfaces. The methods defined in this interface can be used to operate both Set sets and List and Queue sets.
  • JDK does not provide any direct implementation of this interface, but provides more specific implementation of sub interfaces (such as Set and List).
  • Before JDK5, Java collections will lose the data types of all objects in the container and treat all objects as Object types; After adding generics to JDK 5.0, Java collections can remember the data types of objects in containers.

common method

methodeffect
add(Object obj)Adds the specified element to the specified collection (using auto boxing)
addAll(Collection coll)Adds elements from the specified collection to the current collection
int size()Gets the number of elements in the specified collection
void clear()Clears all elements in the specified collection
boolean isEmpty()Judge whether the current collection is empty
boolean contains(Object obj)Determine whether the current collection contains an element
boolean containsAll(Collection coll)Judge whether all elements in the formal parameter coll exist in the current collection
boolean remove(Object obj)To delete obj elements from the current collection, return true for success and false for failure
boolean removeAll(Collection coll)Removes all elements in the coll from the current collection
boolean retainAll(Collection coll)Intersection, get the intersection of the current set and the coll set, and return it to the current set
boolean equals(Object obj)To return true, the elements of the current collection and the parameter collection need to be the same
Object[] toArray()Convert a collection to an array of objects
hashCode()Gets the hash value of the specified collection object
iterator()Returns an instance of the Iterator interface for traversing collection elements
  • When you call contains, containsAll, remove and removeAll, you call the equals method to find the corresponding value

  • Array to collection: Arrays.asList();

  • Collection to array: toArray()

Case 1

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @ClassName CollectionTest
 * @Description:  Use of collection framework
 * @Author Laoyang
 * @Date 2021/9/28 15:42
 */
public class CollectionTest {
    /**
     * Collection Method usage in interface
     */
    @Test
    public void testOne() {
        Collection collection = new ArrayList();
        // add(Object e): adds the specified element to the corresponding collection (auto boxing)
        collection.add("Xiaobai");
        collection.add(123);

        // addAll(Collection coll): adds the elements in the specified collection to the current collection (it can be used to integrate multiple collections in some requirements)
        Collection coll = new ArrayList();
        coll.add("AAa");
        coll.addAll(collection);
        //Collection collection1 = new ArrayList();
        //collection1.addAll(coll);     //  Xiaobai 123 AAA

        // size(): gets the number of elements in the specified collection
        System.out.println(collection.size());  // 2

        // isEmpty(): judge whether the current collection is empty
        System.out.println(coll.isEmpty());     // false

        // clear(): clear all elements in the specified collection
        System.out.println(collection);  // [Xiaobai, 123]
        collection.clear();
        System.out.println(collection);  // []
    }
}

Case 2

Note: when adding obj data to the object of the implementation class of the Collection interface, the class of obj is required to override the equals method

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName CollectionMethod
 * @Description:  Collection common method
 * @Author Laoyang
 * @Date 2021/9/30 10:15
 */
public class CollectionMethod {
    @Test
    public void testOne() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add("ABC");
        collection.add(new String("QQ"));
        Person person = new Person();
        collection.add(person);
        collection.add(new Person("Laoyang", 20));

        // contains(Object obj): determines whether the current collection contains an element
        System.out.println(collection.contains("ABC"));     // true
        System.out.println(collection.contains("CCC"));     // false
        /*
        be careful
        String The equals method is rewritten internally, so when using the contains method, the rewritten equals method will be called automatically for judgment. Therefore, the comparison of two classes with the same value is true
        If the equals method is not overridden in some classes, the equals method in Object will be called by default, and the equals method in Object class uses the = = sign to judge (that is, to judge the address value). Therefore, the comparison between two classes with the same value is false
        Ps: If you want two classes with the same value to be true, you need to override equals manually
         */
        System.out.println(collection.contains(new String("QQ")));     // true
        System.out.println(collection.contains(person));     // true
        System.out.println(collection.contains(new Person("Laoyang", 20)));     // false (true after overriding the equals method)

        System.out.println("----------------------------------");

        // containsAll(Collection coll): judge whether all elements in the formal parameter coll exist in the current collection
        Collection coll = Arrays.asList("QQ", 123, "Hey, hey, hey");  // As long as there is a different one, it will return false
        System.out.println(coll.containsAll(collection));   // false
    }

    @Test
    public void testTwo() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add("ABC");
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        // remove(Object obj): deletes an obj element from the current collection. Returns true on success and false on failure
        System.out.println(collection);  // [AAA, 123, ABC, QQ, Person{name='Laoyang', age=20}]
        System.out.println(collection.remove("AAA"));   // true
        System.out.println(collection);  // [123, ABC, QQ, Person{name='Laoyang', age=20}]

        System.out.println("----------------------------------");

        // Remove all (collection Coll): removes all elements in the coll from the current collection
        Collection coll = Arrays.asList(123, "QQ", "WW");
        System.out.println(collection.removeAll(coll)); // true
        System.out.println(coll);       // [123, QQ, WW]
        System.out.println(collection);     // [ABC, Person{name='Laoyang', age=20}]
    }

    @Test
    public void testThree() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add("ABC");
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        // Retain all (collection Coll): intersection. Get the intersection of the current collection and coll collection and return it to the current collection
        Collection coll = Arrays.asList(123, "QQ", "WW");
        System.out.println(collection.retainAll(coll)); // true
        System.out.println(collection);     // [123, QQ]

        System.out.println("----------------------------------");

        // equals(Object obj): to return true, the elements of the current set and the parameter set must be the same
        Collection collA = new ArrayList();
        collA.add("AAA");
        collA.add(123);

        Collection collB = new ArrayList();
        collB.add("AAA");
        collB.add(123);

        Collection collC = new ArrayList();
        collC.add(123);
        collC.add("AAA");

        System.out.println(collA.equals(collB)); // true
        System.out.println(collA.equals(collC)); // False (because the List stores ordered and repeatable data, false will be returned if the values are the same and the order is different)
    }

    @Test
    public void testFour() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add("ABC");
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        // hashCode(): returns the hash value of the current object
        System.out.println(collection.hashCode());

        /*
        Collection to array: toArray()
        Array to collection: Arrays.asList()
         */
        Object[] objects = collection.toArray();
        System.out.println(Arrays.toString(objects));   // [AAA, 123, ABC, QQ, Person{name='Laoyang', age=20}]

        List<Object> list = Arrays.asList(objects);
        System.out.println(list);   // [AAA, 123, ABC, QQ, Person{name='Laoyang', age=20}]

        /*
         Note: when using an array of basic data types, List will only recognize one element!!!
         If necessary, you can use the wrapper class for transmission
         */
        List<int[]> ints = Arrays.asList(new int[]{1, 2, 3, 4});
        System.out.println(ints.size());    // 1
        System.out.println(ints);   // [[I@22927a81]

        List<char[]> chars = Arrays.asList(new char[]{'q', 'a'});
        System.out.println(chars);  // [[C@78e03bb5]

        // How to package class parameters
        List<Integer> integers = Arrays.asList(new Integer[]{1, 2, 3, 4});
        System.out.println(integers.size());    // 4
        System.out.println(integers);   // [1, 2, 3, 4]

        List<Double> doubles = Arrays.asList(new Double[]{1.2, 2.3, 3.4});
        System.out.println(doubles.size());    // 3
        System.out.println(doubles);    // [1.2, 2.3, 3.4]
    }
}

class Person {
    private String name;
    private int age;

    /**
     * Override the equals method
     */
    @Override
    public boolean equals(Object o) {
        System.out.println("get into Person Class equals method");
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "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;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }
}

be careful

  • String internally rewrites the equals method, so when using the contains method, the rewritten equals method will be called automatically for judgment. Therefore, two classes with the same value are true.

  • If the equals method is not overridden in some classes, the equals method in Object will be called by default, and the equals method in Object class uses the = = sign to judge (that is, to judge the address value). Therefore, the comparison between two classes with the same value is false.

    If you want two classes with the same value to be true, you need to override equals manually

  • When using an array of basic data types, List will only recognize one element!!! If necessary, you can use the wrapper class for transmission.

Iterator iterator

explain

  • The Iterator object is called an Iterator (a kind of design pattern) and is mainly used to traverse the elements in the Collection.

  • GOF defines the iterator pattern as providing a way to access each element in a container object without exposing the internal details of the object. The iterator pattern is born for containers.

    Similar to "conductor on the bus", "stewardess on the train" and "stewardess".

  • Every time a collection object calls the iterator() method, it will get a new iterator object. The default cursor is before the first element of the collection.

Internally defined methods in Iterator

  • hasNext(): judge whether there is another element
  • next(): the pointer moves down, and the element at the collection position will be returned after moving down
  • Remove(): when traversing, delete the specified elements in the collection. This method is different from the direct call to remove() in the collection

case

Here is a simple demonstration of three traversal methods

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * @ClassName IteratorTest
 * @Description:  Ierator Use of iterators
 * @Author Laoyang
 * @Date 2021/9/30 11:18
 */
public class IteratorTest {
    @Test
    public void testOne() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        Iterator iterator = collection.iterator();
        // Mode 1
//        System.out.println(iterator.next());  // AAA
//        System.out.println(iterator.next());  // 123
//        System.out.println(iterator.next());  // QQ
//        System.out.println(iterator.next());  // Person{name='Laoyang', age=20}
//        System.out.println(iterator.next());  //  Error reported: NoSuchElementException

        // Method 2: not recommended
//        for (int i = 0; i < collection.size(); i++) {
//            System.out.println(iterator.next());
//        }

        // Method 3: recommend (hasNext(): judge whether there is another element)
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Iterator execution principle

  1. A pointer is generated when the Iterator object is created
  2. The hashNext method determines whether there is a next element
  3. If hashNext returns true, the pointer will move down to the first element in the collection. If it is false, it will end directly... And so on

Two wrong ways of Iterator traversing a set

Error 1: directly use next() to judge, resulting in skip traversal

Error 2: using Iterator anonymous object causes an endless loop

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * @ClassName IteratorTest
 * @Description:  Ierator Use of iterators
 * @Author Laoyang
 * @Date 2021/9/30 11:18
 */
public class IteratorTest {
    /**
     * Iterator Two wrong ways to write traversal sets
     */
    @Test
    public void testTwo() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        /*
         Error 1:
         AAA is obtained during the first execution, and then AAA is used for judgment
         After the judgment is successful, the value 123 pointed to by the next pointer is returned
         The final effect is to jump to the output set element, and then jump to the last one. If there is no next one, it will report NoSuchElementException
         */
//        Iterator iterator = collection.iterator();
//        While ((iterator. Next())! = null) {/ / AAA is obtained during the first execution, and then AAA is used for judgment
//            System.out.println(iterator.next()); / / after judging success, return the value 123 pointed to by the next pointer
//        }

        /*
        Error 2: using Iterator anonymous object - causes an endless loop
         */
        while (collection.iterator().hasNext()) {
            System.out.println(collection.iterator().next());
        }
    }
}

Use of the remove method

be careful

  • Iterator can delete the elements of the collection, but it is the remove method of the iterator object during traversal, not the remove method of the collection object.

  • If you call remove() before calling next(), you will report IllegalStateException

  • After calling the next method two times after the last call, the remove method also reported IllegalStateException.

case

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * @ClassName IteratorTest
 * @Description:  Ierator Use of iterators
 * @Author Laoyang
 * @Date 2021/9/30 11:18
 */
public class IteratorTest {
    /**
     * remove Use of methods
     * Case 1: if you call remove() before calling next(), you will report IllegalStateException
     * Situation two: after the last call to the next method, two times the remove method also called IllegalStateException.
     */
    @Test
    public void testTherr() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        // Deletes the specified element in the collection
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            // Case 1: the remove method cannot be executed immediately after the hasNext() method is executed, because the pointer does not point to any value at this time
            //iterator.remove(); / / error: IllegalStateException

            Object next = iterator.next();
            if ("QQ".equals(next)) {
                iterator.remove();
                // Case 2: you cannot call remove() twice after the next() method, but you can call it outside, for example, you can call remove() outside the current while structure
                //iterator.remove(); / / error: IllegalStateException
            }
        }
        // Traverse the collection, because the above has been traversed, so we need to re assign the value here, and then traverse
        iterator = collection.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Traversing collection elements in foreach mode

explain

  • foreach is a new for loop feature added in JDK5, which is used to traverse sets and arrays.
  • The traversal operation does not need to get the length of the Collection or array, and does not need to use an index to access elements.
  • Traverse the bottom layer of the collection and call Iterator to complete the operation.

case

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @ClassName ForTest
 * @Description:  JDK5 New features of for loop
 * @Author Laoyang
 * @Date 2021/9/30 15:52
 */
public class ForTest {
    /**
     * Traverse collection: for (element type local variable in collection: collection object) {}
     */
    @Test
    public void testOne() {
        Collection collection = new ArrayList();
        collection.add("AAA");
        collection.add(123);
        collection.add(new String("QQ"));
        collection.add(new Person("Laoyang", 20));

        // This traversal method still calls the iterator internally
        for (Object o : collection) {
            System.out.println(o);
        }
    }

    /**
     * Traversal array: for (element type of array, local variable: array object) {}
     */
    @Test
    public void testTwo() {
        int[] arr = new int[]{1, 5, 3, 2};
        for (int i : arr) {
            System.out.println(i);
        }
    }
}

Little practice

Using two different for loop assignment methods, what are the final printed results?

package com.laoyang.test.day1;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @ClassName ForTest
 * @Description:  JDK5 New features of for loop
 * @Author Laoyang
 * @Date 2021/9/30 15:52
 */
public class ForTest {
    /**
     * Small exercise: is the final printed result WW or MM?
     */
    @Test
    public void testThree() {
        String[] strs = new String[]{"WW", "WW", "WW"};
        /*
        Method 1: ordinary for assignment
        This assignment operation is to replace the element at the specified subscript position in the strs array with a new value, so the final traversal result is the replaced element
         */
//        for (int i = 0; i < strs.length; i++) {
//            strs[i] = "MM";
//        }

        /*
         Method 2: enhance for assignment
         This assignment operation is not saved in strs, because it first obtains each value from strs, and then assigns it to str for use
         However, str is a new local variable, and its value will not affect the elements in strs, so the final printed result will still be WW
         */
        for (String str : strs) {
            str = "MM";
        }

        for (String str : strs) {
            System.out.println(str);
        }
    }
}

You can open / close the commented code for testing

Use of List

explain

  1. For the limitation that arrays are used to store data in Java, we usually use List instead of arrays.
  2. The elements in the List collection class are ordered and repeatable, and each element in the collection has its corresponding sequential index.
  3. The elements in the List container correspond to an integer serial number. The position recorded in the container can access the elements in the container according to the serial number.
  4. The implementation classes of List interface in JDK API are ArrayList, LinkedList and Vector.

Common interview questions: what are the similarities and differences of ArrayList, LinkedList and Vector?

Similarities: the three classes implement the List interface, and the characteristics of storing data are the same (they are ordered and repeatable data)

difference:

ArrayList: as the main implementation class of the List interface (JDK1.2); high execution efficiency (thread unsafe); the underlying layer uses Object[] elementData for storage;

LinkedList: JDK1.2 appears; for frequent insert and delete operations, the efficiency is higher than ArrayList; the bottom layer uses two-way linked list storage;

Vector: as an ancient implementation class of the List interface (JDK1.0); low execution efficiency (thread safe); the underlying layer uses Object[] elementData for storage;

The List interface also appeared in JDK1.2

ArrayList source code analysis (JDK7 & jdk8)

ArrayList in JDK7

ArrayList list = new ArrayList();   // The bottom layer creates an array with a length of 10
list.add(111);   // elementData[0] = new Integer(111);
   ...
list.add(999);   // If the capacity of the underlying elementData array is insufficient due to this addition operation, the capacity will be expanded
> By default, the capacity will be expanded to 1% of the original capacity.5 At the same time, you need to assign the data in the original array to the new array!
> It is recommended to use a constructor with parameters in actual development: ArrayList list = new ArrayList(int initialCapacity);

Partial source code

package java.util;

import java.util.*;

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	// The array buffer in which the elements of ArrayList are stored. The capacity of ArrayList is the length of the array buffer
	private transient Object[] elementData;
	
	// The size of the ArrayList (the number of elements it contains).
	private int size;
	
	/**
	 * The maximum size of the array to allocate. Some VM S keep some headers in the array.
	 * Attempting to allocate a larger array may result in OutOfMemoryError: the requested array size exceeds the VM limit
	 */
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

	// The null parameter constructor automatically sets the original size of elementData
	public ArrayList() {
        this(10);
    }
	
	// With parameter constructor, manually set the original size of elementData
	public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Cap" +
                    "acity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
	
	// Returns the total number of current elements
	public int size() {
        return size;
    }
	
	// Add element
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
	
	// Ensure that the content can be put into the current collection normally. If the currently added content exceeds the storage length of the collection, the underlying capacity expansion mechanism will be used
	private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	// Increase the capacity to ensure that it can accommodate at least the number of elements specified by the minimum capacity parameter
	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // The default expansion is 1.5 times the original length
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            // If it is not enough, the length of the current value is directly regarded as the length of the set
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
			// If it is not enough, the maximum value of int will be taken as the length
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
		// Copy all the elements in the current elementData array to the new array
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
	
	private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
}

I added the Chinese Notes myself, which can be understood in combination or ignored

ArrayList in JDK8

ArrayList list = new ArrayList();   // The underlying Object[] elementData is initialized to {}, and an array with a length of 10 is not created
list.add(111);     // When add() is called for the first time, the underlying layer creates an array with a length of 10 and adds data 111 to elementDate[0]
  ...
list.add(999);  // If the capacity of the underlying elementData array is insufficient due to this addition operation, the capacity will be expanded
... Capacity expansion operation and JDK7 No difference

Partial source code

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	// Shared empty array instance for empty instances of default size. We compare it with empty_ Element data to see how much expansion is required when the first element is added
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	
	// The size of the ArrayList (the number of elements it contains).
    private int size;
	
	// The maximum size of the array to allocate. Some VMS keep some headers in the array. Attempting to allocate a larger array may result in OutOfMemoryError: the requested array size exceeds the VM limit
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	// Construct an empty list with an initial capacity of 10.
	public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
	
	// Appends the specified element to the end of this list
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
	
	// Ensure internal capacity
	private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

	// Ensure explicit capacity
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	// Increase the capacity to ensure that it can accommodate at least the number of elements specified by the minimum capacity parameter.
	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

	// Huge capacity
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
}

I added the Chinese Notes myself, which can be understood in combination or ignored

Summary

The creation of ArrayList in JDK7 is similar to the hungry man in singleton mode, and the creation of ArrayList object in JDK8 is similar to the lazy man in singleton mode, which delays the creation of array and saves memory.

LinkedList source code analysis

LinkedList list = new LinkedList();  // The first and last attributes of Node type are declared internally, and the default value is null
list.add(111);     // Encapsulate 111 into Node and create Node object

among Node(Reflects LinkedList Is defined as:
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

Partial source code

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	// Shared empty array instance for empty instances of default size. We compare it with empty_ Element data to see how much expansion is required when the first element is added
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	
	// The size of the ArrayList (the number of elements it contains).
    private int size;
	
	// The maximum size of the array to allocate. Some VMS keep some headers in the array. Attempting to allocate a larger array may result in OutOfMemoryError: the requested array size exceeds the VM limit
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	// Construct an empty list with an initial capacity of 10.
	public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
	
	// Appends the specified element to the end of this list
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
	
	// Ensure internal capacity
	private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

	// Ensure explicit capacity
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	// Increase the capacity to ensure that it can accommodate at least the number of elements specified by the minimum capacity parameter.
	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

	// Huge capacity
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
}

I added the Chinese Notes myself, which can be understood in combination or ignored

Vector source code analysis

Since Vector was a long time ago, this implementation class will not be used now, so let's briefly explain it here

  • When creating objects through the Vector() constructor in JDK7 and JDK8, the underlying layer creates an array with a length of 10

  • In terms of capacity expansion, the default capacity expansion is twice the original array

  • Vector is thread safe

Partial source code

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
	// An array buffer that stores Vector components. The capacity of the Vector is the length of the array buffer and is at least sufficient to contain all the elements of the Vector. Any array element after the last element in the Vector is empty
	protected Object[] elementData;
	
	// The number of valid components in this Vector object
	protected int elementCount;
	
	// When the size of the vector becomes larger than its capacity, the capacity of the vector increases automatically. If the capacity increment is less than or equal to zero, the capacity of the vector will double each time it needs to grow
	protected int capacityIncrement;
	
	// Construct an empty vector so that the size of its internal data array is 10 and its standard capacity increment is zero.
	public Vector() {
        this(10);
    }
	
	// Appends the specified element to the end of this Vector.
	public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
	
	// This implements the asynchronous semantics of ensureCapacity. The synchronization method in this class can call this method internally to ensure capacity without incurring the cost of additional synchronization.
	private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	// The maximum size of the array to allocate. Some VMS keep some headers in the array. Attempting to allocate a larger array may result in OutOfMemoryError: the requested array size exceeds the VM limit
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
	
	private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
}

I added the Chinese Notes myself, which can be understood in combination or ignored

common method

methodeffect
void add(int index, Object ele)Insert the ele element at the index position
boolean addAll(int index, Collection eles)Add all elements in eles from the index position
Object get(int index)Gets the element at the specified index location
int indexOf(Object obj)Returns the position where obj first appears in the collection
int lastIndexOf(Object obj)Returns the last occurrence of obj in the current collection
Object remove(int index)Removes the element at the specified index location and returns this element
Object set(int index, Object ele)Set the element at the specified index location to ele
List subList(int fromIndex, int toIndex)Returns a subset from fromIndex to toIndex

Some methods are not listed here. You can see the use in the following cases

case

package com.laoyang.test.day2;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @ClassName ListMethod
 * @Description:  List Common interface methods
 * @Author Laoyang
 * @Date 2021/10/2 9:41
 */
public class ListMethod {
    @Test
    public void testOne() {
        ArrayList list = new ArrayList();
        // add(Object obj)
        list.add("Laoyang");
        list.add(20);
        list.add(new Person("Xiaobai", 18));
        list.add(20);

        System.out.println(list);   // [Laoyang, 20, Person{name='Xiaobai', age=18}, 20]

        System.out.println("-----------------------------");

        // add(int index, Object ele): inserts an ele element at the index position
        list.add(2, "QQ");
        System.out.println(list);   // [Laoyang, 20, QQ, Person{name='Xiaobai', age=18}, 20]

        System.out.println("-----------------------------");

        // Add all (int index, collection ELES): add all elements in eles from the index position
        List strings = Arrays.asList("WW", "EE", "RR");
        list.addAll(4, strings);
        // If the collection is passed in by using the add method, the list will take it as a whole by default and add it to the corresponding list
        //list.add(strings);
        System.out.println(list);  // [Laoyang, 20, QQ, Person{name='Xiaobai', age=18}, WW, EE, RR, 20]
        System.out.println(list.size());    // Use addAll() to return 9 and add() to return 6

        System.out.println("-----------------------------");

        // get(int index): gets the element at the specified index position
        Object o = list.get(3);
        System.out.println(o);  // Person{name='Xiaobai', age=18}

        System.out.println("-----------------------------");

        // indexOf(Object obj): returns the position where obj first appears in the collection. If the corresponding value cannot be found, - 1 is returned
        System.out.println(list.indexOf(20));     // 1
        System.out.println(list.indexOf(200));    // -1. Return - 1 when the corresponding value cannot be found

        System.out.println("-----------------------------");

        // lastIndexOf(Object obj): returns the last occurrence of obj in the current collection. If the corresponding value cannot be found, - 1 is returned
        System.out.println(list.lastIndexOf(20));   // 7
        System.out.println(list.lastIndexOf(205));   // -1. Return - 1 when the corresponding value cannot be found

        System.out.println("-----------------------------");

        /*
         remove(int index):Removes the element at the specified index location and returns this element
         remove(Object o):Remove the element specified in the corresponding array (Note: the element to be removed will not be returned in this way)
         */
        Object remove = list.remove(3);// Delete by index
        System.out.println(remove);     // Person{name='Xiaobai', age=18}

        list.remove("EE");   // Delete according to element
        System.out.println(list);   // [Laoyang, 20, QQ, WW, RR, 20]

        System.out.println("-----------------------------");

        // Set (int index, opbject, ELE): sets the element at the specified index position to ele
        list.set(3, "QWER");
        System.out.println(list);   // [Laoyang, 20, QQ, QWER, RR, 20]

        System.out.println("-----------------------------");

        // subList(int fromIndex, int toIndex): returns the subset from fromIndex to toIndex, which will not affect the original collection elements
        List list1 = list.subList(0, 3);
        System.out.println(list1);  // [Laoyang, 20, QQ]
        System.out.println(list);   // [Laoyang, 20, QQ, QWER, RR, 20]
    }
}

class Person {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "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;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }
}

Three ways to traverse the List collection

Iterator iterator, foreach(), for()

package com.laoyang.test.day2;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @ClassName ListMethod
 * @Description:  List Common interface methods
 * @Author Laoyang
 * @Date 2021/10/2 9:41
 */
public class ListMethod {
    /**
     * List Three ways of traversal of sets
     * Iterator Iterator, foreach(), for()
     */
    @Test
    public void testTwo() {
        ArrayList list = new ArrayList();
        list.add("Laoyang");
        list.add(20);
        list.add(new Person("Xiaobai", 18));
        list.add(20);

        // Method 1: Iterator iterator
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("------------------------------");

        // Method 2: foreach()
        for (Object o : list) {
            System.out.println(o);
        }

        System.out.println("------------------------------");

        // Method 3: for()
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

class Person {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "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;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }
}

Summary

Add: add(Object ele), addAll(Collection eles)
Delete: remove(int index), remove(Object obj)
Change: set(int index, Object ele)
Query: get(int index)
Insert: add(int index, Object ele)
Length: size()
Traversal: Iterator iterator, foreach(), for()

Use of Set

explain

  1. The set interface is a sub interface of the Collection. The set interface does not provide additional methods.

  2. The Set set cannot contain the same elements. If you try to add two identical elements to the same Set set, the add operation fails.

  3. Set determines whether two objects are the same, not using the = = operator, but according to the equals() method.

Features: store unordered and unrepeatable data

Use requirements

  1. The class of the data added to the Set must override hashCode() and equals()
  2. Overridden hashCode() and equals() are as consistent as possible: equal objects must have equal hash codes (hash values)
  3. Tip for rewriting two methods: the properties in the object used for the equals() method comparison should be used to calculate the hashCode() value

Generally, the properties of the objects that participate in the calculation of hashCode() should also participate in the calculation of equals().

Suggestion: there are automatically generated equals() and hashCode() in the IDEA. It is recommended to directly use the automatically generated in the development to avoid some unnecessary trouble.

IDEA shortcut: ALT + INS

understand

There are no new methods defined in the Set interface, but all the methods declared in the Collection interface are used

Set implementation class

  • HashSet: as the main implementation class of Set interface; Thread unsafe; null values can be stored.

    HashSet underlying structure: array + linked list (JDK7)

  • LinkedHashSet: as a subclass of HashSet; When traversing internal data, it can be traversed in the order of addition.

  • ThreeSet: you can sort by the specified attribute of the added object.

Disorder and non repeatability of Set (taking HashSet as an example)

  • Disorder: not equal to randomness; The stored data is not added in the order of array index in the underlying array, but determined according to the hash value of the data.
  • Non repeatability: ensure that when the added element is judged according to equals(), it cannot return true (because returning true means that the two elements are the same); Only one of the same elements can be added.

You can refer to the following case code to understand the use of HashSet and the disorder and non repeatability in HsahSet

case

Entity class

package com.laoyang.test.day3;

import java.util.Objects;

/**
 * @ClassName User
 * @Description: 
 * @Author Laoyang
 * @Date 2021/10/8 11:02
 */
public class User implements Comparable {
    private String name;
    private int age;

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "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;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * Override the equals() method
     */
    @Override
    public boolean equals(Object o) {
        System.out.println("Called User of equals method");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }

    /**
     * Override hashCode() method
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Test class

package com.laoyang.test.day3;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName SetTest
 * @Description:  Set Use of interfaces
 * @Author Laoyang
 * @Date 2021/10/2 10:52
 */
public class SetTest {
    /**
     * Disorder
     * Not equal to randomness; The stored data is not added in the order of array index in the underlying array, but determined according to the hash value of the data.
     */
    @Test
    public void testOne() {
        // HashSet will create an array with a length of 16 by default
        Set set = new HashSet();
        set.add(123);
        set.add("QQ");
        set.add(new String("ABC"));
        set.add(12.5);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    /**
     * Non repeatability
     * Ensure that when the added element is judged according to equals(), it cannot return true (because returning true means that the two elements are the same); Only one of the same elements can be added.
     */
    @Test
    public void testTwo() {
        Set set = new HashSet();
        set.add(123);
        set.add("QQ");
        set.add(new String("ABC"));
        set.add(12.5);
        /*
        Override the hashCode() method in the User class to determine whether the hash values of two objects are the same (if the hash values are the same, the equals() method will be called to determine)
        If the hashCode() method is not overridden, the hashCode() method in the Object class will be called by default, and the hashCode() in the Object will be judged randomly
        Therefore, in the end, it may not be judged with the same value as the existing value, but directly inserted in different positions, resulting in the final acquisition of two identical objects
         */
        set.add(new User("Xiaobai", 18));
        set.add(new User("Xiaobai", 18));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            // There will be only one User object in the last printed result
            System.out.println(iterator.next());
        }
    }
}

If you want to see the effect of printing the last two identical elements, you only need to annotate the hashCode() method in the User class

Use of LinkedHashSet

explain

  • LinkedHashSet is a subclass of HashSet. While adding data, each data also maintains two references to record the previous data and the latter data

  • Advantages: for frequent traversal operations, LinkedHashSet is more efficient than HashSet

case

package com.laoyang.test.day3;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName SetTest
 * @Description:  Set Use of interfaces
 * @Author Laoyang
 * @Date 2021/10/2 10:52
 */
public class SetTest {
    /**
     * LinkedHashSet Use of
     */
    @Test
    public void testThree() {
        Set set = new LinkedHashSet();
        set.add(123);
        set.add("QQ");
        set.add(new String("ABC"));
        set.add(12.5);
        set.add(new User("Xiaobai", 18));
        set.add(new User("Xiaobai", 18));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Use of TreeSet

explain

  1. The data added to TreeSet must be objects of the same class.
  2. There are two sorts: natural sort (to achieve Comparable) and custom sort (Comparator)
  3. In natural sorting, the criteria for comparing whether two objects are the same is: compareTo() returns 0 and doesn't care about equals()
  4. In custom sorting, the criteria for comparing whether two objects are the same is: compare() returns 0 and doesn't care about equals()

Features: orderly, faster query speed than List

The underlying TreeSet uses a red black tree structure to store data

case

Entity class User

package com.laoyang.test.day3;

import java.util.Objects;

/**
 * @ClassName User
 * @Description: 
 * @Author Laoyang
 * @Date 2021/10/8 11:02
 */
public class User implements Comparable {
    // Omit some code - this class is the User class above

    /**
     * Sort by name from small to large
     */
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User user = (User) o;
            //return this.name.compareTo(user.name); 	//  Only judge whether the name is the same
            int i = this.name.compareTo(user.name);
            if (i != 0) {
                return i;
            } else {
                return Integer.compare(this.age, user.age);
            }
        } else {
            throw new RuntimeException("Inconsistent type");
        }
    }
}

Test class

package com.laoyang.test.day3;

import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

/**
 * @ClassName TreeSetTest
 * @Description:  TreeSet Use of
 * @Author Laoyang
 * @Date 2021/10/8 10:56
 */
public class TreeSetTest {
    /**
     * Natural sorting
     */
    @Test
    public void testOne() {
        TreeSet set = new TreeSet();
        // Cannot add objects of different classes: ClassCastException
//        set.add(123);
//        set.add("QQ");
//        set.add(12.5);

        // Example 1
//        set.add(123);
//        set.add(-1);
//        set.add(233);
//        set.add(99);

        // Example 2
        set.add(new User("Tom", 22));
        set.add(new User("Jerry", 18));
        set.add(new User("Jim", 35));
        // If you only judge the name, only one Mike will appear at the end of printing
        set.add(new User("Mike", 6));
        set.add(new User("Mike", 15));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    /**
     * Custom sorting
     */
    @Test
    public void testTwo() {
        Comparator comparator = new Comparator() {
            // Sorted by age
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User) {
                    User userA = (User) o1;
                    User userB = (User) o2;
                    return Integer.compare(userA.getAge(), userB.getAge());
                } else {
                    throw new RuntimeException("Inconsistent type");
                }
            }
        };
        TreeSet set = new TreeSet(comparator);
        set.add(new User("Tom", 18));
        set.add(new User("Jerry", 18));
        set.add(new User("Jim", 35));
        set.add(new User("Mike", 6));
        set.add(new User("Mike", 15));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

The process of adding elements to a HashSet (taking HashSet as an example)

  1. We add element A to the HashSet. First, we call the hashCode() method of the class where element A is located to calculate the hash value of element A.

  2. Then, the hash value calculates the storage location (i.e. index location) in the underlying array of HashSet through some algorithm.

  3. Determine whether the element already exists in the current position of the array:

    If there are no other elements in this position, element A is added successfully (case 1)
    If there are other elements B (or multiple elements in the form of A linked list), compare the hash values of elements A and B

    If the hash values are different, element A is added successfully (case 2)
    If the hash values are the same, you need to call the equals() method in the class where element A is located

    If the equals() method returns true, it means that element A and element B are the same, and the addition fails
    If the equals() method returns false, it indicates that element A is different from element B, and the addition is successful (case 3)

    For cases 2 and 3 where the addition is successful, element A and the data already existing at the specified index position are stored in A linked list

Specific storage mode

JDK7: element A is placed in the array and points to the original element

JDK8: the original element is placed in the array and points to element A

Conclusion: seven up and eight down

practice

Exercise one

Create five objects of the Employee class, put them into the TreeSet collection, sort the elements in the collection in the following two ways, and traverse the output:

  1. Make the Employee class implement the Comparable interface and sort by name.
  2. When creating a TreeSet, the Comparator object is passed in and sorted by birthday date.

Employee birthday class

package com.laoyang.test.day4;

/**
 * @ClassName MyDate
 * @Description:  Birthday class
 * @Author Laoyang
 * @Date 2021/10/10 10:16
 */
public class MyDate implements Comparable {
    private int year;
    private int month;
    private int day;

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public MyDate() {
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if (o instanceof MyDate) {
            MyDate myDate = (MyDate) o;
            // Judgment year
            int compareA = Integer.compare(this.getYear(), myDate.getYear());
            if (compareA != 0) {
                return compareA;
            }
            // Judgment month
            int compareB = Integer.compare(this.getMonth(), myDate.getMonth());
            if (compareB != 0) {
                return compareB;
            } else {
                // Judgment day
                return Integer.compare(this.getDay(), myDate.getDay());
            }
        }
        throw new RuntimeException("Inconsistent type");
    }
}

Employee category

package com.laoyang.test.day4;

/**
 * @ClassName Employee
 * @Description:  Employee category
 * @Author Laoyang
 * @Date 2021/10/10 10:15
 */
public class Employee implements Comparable {
    private String name;
    private int age;
    private MyDate birthday;

    public Employee() {
    }

    public Employee(String name, int age, MyDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    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;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if (o instanceof  Employee) {
            Employee employee = (Employee) o;
            return this.name.compareTo(employee.name);
        }
        throw new RuntimeException("Inconsistent type");
    }
}

Test class

package com.laoyang.test.day4;

import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

/**
 * @ClassName EmployeeTest
 * @Description:  Test class
 * @Author Laoyang
 * @Date 2021/10/10 10:21
 */
public class EmployeeTest {
    /**
     * Natural sorting
     * Sort by employee name
     */
    @Test
    public void testOne() {
        Employee employeeA = new Employee("David",18,new MyDate(2003,2,20));
        Employee employeeB = new Employee("Jerome",32,new MyDate(1988,5,18));
        Employee employeeC = new Employee("Aaron",21,new MyDate(2000,3,11));
        Employee employeeD = new Employee("Dennis",15,new MyDate(2006,6,1));
        Employee employeeE = new Employee("Robinson",19,new MyDate(2002,10,6));
        TreeSet set = new TreeSet();
        set.add(employeeA);
        set.add(employeeB);
        set.add(employeeC);
        set.add(employeeD);
        set.add(employeeE);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    /**
     * Custom sorting
     * Sort according to employee birthday information
     */
    @Test
    public void testTwo() {
        Employee employeeA = new Employee("David",18,new MyDate(2003,2,20));
        Employee employeeB = new Employee("Jerome",32,new MyDate(1988,5,18));
        Employee employeeC = new Employee("Aaron",21,new MyDate(2000,3,11));
        Employee employeeD = new Employee("Dennis",15,new MyDate(2006,6,1));
        Employee employeeE = new Employee("Robinson",19,new MyDate(2003,10,6));

        Comparator comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Employee && o2 instanceof Employee) {
                    Employee employeeA = (Employee) o1;
                    Employee employeeB = (Employee) o2;

                    MyDate myDateA = employeeA.getBirthday();
                    MyDate myDateB = employeeB.getBirthday();

                    // Method 1: write corresponding judgment in corresponding method
//                    //Judgment year
//                    int compareA = Integer.compare(myDateA.getYear(), myDateB.getYear());
//                    if (compareA != 0) {
//                        return compareA;
//                    }
//                    //Judgment month
//                    int compareB = Integer.compare(myDateA.getMonth(), myDateB.getMonth());
//                    if (compareB != 0) {
//                        return compareB;
//                    } else {
//                        //Judgment day
//                        return Integer.compare(myDateA.getDay(), myDateB.getDay());
//                    }

                    // Method 2: judge in MyDate class, and then call directly
                    return myDateA.compareTo(myDateB);
                } else {
                    throw new RuntimeException("Inconsistent type");
                }
            }
        };

        TreeSet set = new TreeSet(comparator);
        set.add(employeeA);
        set.add(employeeB);
        set.add(employeeC);
        set.add(employeeD);
        set.add(employeeE);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Exercise 2

Remove duplicate numeric values in the List, which is required to be as simple as possible

package com.laoyang.test.day4;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * @ClassName ListDeduplication
 * @Description:  Exercise 2: remove duplicate numeric values in the List. It is required to be as simple as possible
 * @Author Laoyang
 * @Date 2021/10/10 11:06
 */
public class ListDeduplication {
    public static List duplicateList(List list) {
        HashSet set = new HashSet();
        set.addAll(list);
        return new ArrayList(set);
    }

    public static void main(String[] args) {
        List list = new ArrayList();
        /*
          If a custom class is added to the list, the hashCode() and equals() methods need to be overridden in the custom class
         */
        list.add(new Integer(1));
        list.add(new Integer(2));
        list.add(new Integer(2));
        list.add(new Integer(4));
        list.add(new Integer(4));
        List list2 = duplicateList(list);
        for (Object integer : list2) {
            System.out.println(integer);
        }
    }
}

Use of Map

introduce

Map: double column data, which stores the data of key value pairs

  • Map and Collection exist side by side. Used to save data with mapping relationship: key value
  • The key and value in the Map can be data of any reference type
  • The key s in the Map are stored in a Set and cannot be repeated. That is, the classes corresponding to the same Map object must override the hashCode() and equals() methods
  • String class is often used as the "key" of Map
  • There is a one-way one-to-one relationship between key and value, that is, a unique and definite value can always be found through the specified key

common method

Add, delete and modify

methodeffect
Object put(Object key,Object value)Add (or modify) the specified key value to the current map object
void putAll(Map m)Store all key value pairs in m into the current map
Object remove(Object key)Remove the key value pair of the specified key and return value
void clear()Clear all data in the current map

Operation of element query

methodeffect
Object get(Object key)Gets the value corresponding to the specified key
boolean containsKey(Object key)Whether to include the specified key
boolean containsValue(Object value)Whether to include the specified value
int size()Returns the number of key value pairs in the map
boolean isEmpty()Judge whether the current map is empty
boolean equals(Object obj)Judge whether the current map and parameter object obj are equal

Methods of meta view operation

methodeffect
Set keySet()Returns the Set set composed of all key s
Collection values()Returns a Collection of all value s
Set entrySet()Returns the Set set composed of all key value pairs

case

package com.laoyang.test.day5;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName MapMethod
 * @Description:  Map Common methods in
 * @Author Laoyang
 * @Date 2021/10/13 11:04
 */
public class MapMethod {
    /**
     Add, delete and modify:
     Object put(Object key,Object value): Add (or modify) the specified key value to the current map object
     void putAll(Map m):Store all key value pairs in m into the current map
     Object remove(Object key): Remove the key value pair of the specified key and return value
     void clear(): Clear all data in the current map
     */
    @Test
    public void testOne(){
        // put(Object key,Object value)
        Map map = new HashMap();
        // add to
        map.put("AA", 123);
        map.put(111, 321);
        map.put("bb", 56);
        // modify
        map.put("AA", 99);

        System.out.println(map);    // {AA=99, bb=56, 111=321}

        // putAll(Map m)
        Map maps = new HashMap();
        maps.put("CC", 123);
        maps.put("DD", 123);
        map.putAll(maps);
        System.out.println(map);    // {AA=99, bb=56, CC=123, DD=123, 111=321}

        // remove(Object key)
        map.remove("CC");
        System.out.println(map);    // {AA=99, bb=56, DD=123, 111=321}

        // clear(): Note: this method does not change all elements to null, but directly changes back to the newly created appearance (without any elements)
        map.clear();
        System.out.println(map.size());  // 0
        System.out.println(map);    // {}
    }

    /**
     Operation of element query:
     Object get(Object key): Gets the value corresponding to the specified key
     boolean containsKey(Object key): Whether to include the specified key
     boolean containsValue(Object value): Whether to include the specified value
     int size(): Returns the number of key value pairs in the map
     boolean isEmpty(): Judge whether the current map is empty
     boolean equals(Object obj): Judge whether the current map and parameter object obj are equal
     */
    @Test
    public void testTwo() {
        Map map = new HashMap();
        map.put("AA", 123);
        map.put(111, 321);
        map.put("bb", 56);

        // get(Object key): returns null if the corresponding key value cannot be found
        System.out.println(map.get("BB"));    // null
        System.out.println(map.get("bb"));    // 56

        // containsKey(Object key)
        boolean A1 = map.containsKey("AA");
        boolean A2 = map.containsKey("aa");
        System.out.println(A1);   // true
        System.out.println(A2);   // false

        // containsValue(Object value): if the same value exists, as long as you find the first one, you won't continue to find it
        boolean B1 = map.containsValue(123);
        boolean B2 = map.containsValue(345);
        System.out.println(B1);   // true
        System.out.println(B2);   // false

        // size()
        System.out.println(map.size());     // 3

        // isEmpty()
        boolean empty = map.isEmpty();
        System.out.println(empty);      // false

        Map map1 = new HashMap();
        System.out.println(map1.isEmpty());  // true

        // equals(Object obj)
        System.out.println(map.equals(map1));   // false
        Map maps = new HashMap();
        maps.put("AA", 123);
        maps.put(111, 321);
        maps.put("bb", 56);
        System.out.println(map.equals(maps));   // true
    }

    /**
     Method of metaview operation (traversal):
     Set keySet(): Returns the Set set composed of all key s
     Collection values(): Returns a Collection of all value s
     Set entrySet(): Returns the Set set composed of all key value pairs
     Ps: Because they are all kinds of sets, you can use for, foreach, iterator, and Lambda expressions in jkd8
     */
    @Test
    public void testThree() {
        Map map = new HashMap();
        map.put("AA", 123);
        map.put(111, 321);
        map.put("bb", 56);

        // map.iterator() / / iterator() cannot be called directly

        // keySet(): traverse all key values
        Iterator iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("-----------------------------");

        // values(): traverse all value values
        Collection values = map.values();
        for (Object value : values) {
            System.out.println(value);
        }

        System.out.println("-----------------------------");

        /*
        Traverse all key values
         */
        // Method 1: entrySet()
        Set set = map.entrySet();
        Iterator iterator1 = set.iterator();
        while (iterator1.hasNext()) {
            Object obj = iterator1.next();
            // All elements in the entry set collection are entries
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }

        System.out.println("-----------------------------");

        // Mode II
        Iterator key = map.keySet().iterator();
        while (key.hasNext()) {
            Object obj = key.next();
            Object value = map.get(obj);
            System.out.println(obj + " = " + value);
        }
    }
}

summary
Add: put(Object key, Object value), putAll(Map m)
Delete: remove(Object key)
Change: put(Object key, Object value)
Query: get(Object key)
Length: size()
Traversal: keySet(), values(), entrySet()

Map implementation class

  • HashMap: as the main implementation class of Map (released by JDK1.2); The thread is unsafe and the efficiency is high; Store null key and value;

    Underlying structure of JDK7 and before: array + linked list

    The underlying structure of JDK8: array + linked list + red black tree

  • LinkedHashMap: (JDK1.4 release); Subclass of HashMap; Ensure that when traversing Map elements, they can be traversed in the order of addition (on the basis of the original HashMap underlying structure, a pair of pointers are added to point to the previous and subsequent elements)

    For frequent traversal operations, this kind of execution is more efficient than HashMap

  • TreeMap: (JDK1.2 release); Ensure that the added key value pairs are sorted to realize sorting traversal (at this time, consider the natural sorting or customized sorting of keys); Red and black trees are used at the bottom;

  • Hashtable: as an ancient implementation class (released by JDK1.0); Thread safety and low efficiency; null key and value cannot be stored;

  • Properties: (JDK1.0 release); Subclass of Hashtable; Commonly used to process configuration files; Both key and value are String types;

Use of HashMap

Understanding of HashMap structure

Keys in Map: unordered and cannot be repeated. Use Set to store all keys (the class of the key needs to override equals() and hashCode(), taking HashMap as an example)
Value in Map: unordered and repeatable. Use Collection to store all values (the class where value belongs needs to override equals())

A key value pair: key value constitutes an Entry object
Entries in Map: unordered and non repeatable. Use Set to store all entries

Underlying implementation principle of HashMap

Take JDK7 as an example:

HashMap map = new HashMap();   // After instantiation, the bottom layer creates a one-dimensional array Entry[] table with a length of 16;
..Suppose we have put I have some data..
map.put(key1, value1);

You can see how JDK7 implements the process of adding elements

The process of adding elements to HashMap

JDK7

  1. First, call hashCode() of the class where key1 is located to calculate the hash value of key1;

  2. Then, the hash value is calculated by some algorithm to get the storage position in the Entry array;

  3. Judge whether there is data in the current position:

    If the data in this location is empty, key1-value1 is added successfully (case 1)
    If the data in this location is not empty (which means that there are one or more data in this location (in the form of linked list)), compare the hash values of key1 and one or more existing data

    If the hash values of key1 and existing data are different, key1-value1 is added successfully (case 2)
    If the hash value of key1 is the same as that of an existing data, call equals() of the class where key1 is located to compare the values of key1 and one or more existing data

    If equals() returns false, key1-value1 is added successfully (case 3)
    If equals() returns true, replace the value value of the same key with value1

    Such as like as two peas and A, and then add a new value C, C key (hash value and value) is exactly the same as A, which will give value value of C to A of B at this time, C.
    This is equivalent to a modification operation

    Supplement: for case 2 and case 3: at this time, key1-value1 and the original data are stored in a linked list

Capacity expansion: in the process of continuous addition, capacity expansion will be involved. When the critical value is exceeded (and the location to be stored is not empty), capacity expansion will begin.

By default, the capacity will be expanded to twice the original capacity, and the original data will be copied to the expanded array

JDK8 is different from JDK7 in the underlying implementation

  1. HashMap map = new HashMap(); // The underlying layer did not create an array with a length of 16

  2. The underlying array of JDK8 is Node [], not Entry

  3. When the put() method is called for the first time, the underlying layer will create an array with a length of 16

  4. The underlying structure of JDK7 is only: array + linked list; JDK8 underlying structure: array + linked list + red black tree

    When the number of data in the form of a linked list of elements at an index position of the array is > 8 and the current array length is > 64, all data at this index position is stored using a red black tree.

    This is to improve our query efficiency. Once there are more data, the efficiency of using array search will be relatively low (because we need to traverse it completely every time); The red black tree is orderly. The left side of the small row and the right side of the large row can exclude half of the data for searching, which can save more time.

Partial source code

Note: I added Chinese comments myself. Some were interpreted according to my own meaning, and some were translated according to the comments in the source code. So don't fight too hard

JDK7
package java.util;
import java.io.*;

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
	/**
     * Default initial capacity - must be a power of 2
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
	
	/**
     * The load factor to use when not specified in the constructor
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
	
	/**
     * Maximum capacity, used when two constructors with parameters implicitly specify a higher value. Must be a power of 2 < = 1 < < 30
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;
	
	/**
     * An array of data. The length must always be a power of 2
     */
    transient Entry<K,V>[] table;
	
	public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
	
	public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;   // Determines the length of the array created (for example, we manually set the length of the HashMap to 15, but actually created an array with the length of 16)
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
		 /*
         threshold: For the critical value (capacity * loadFactor), HashMap does not start to expand when the array cannot fit. Because it may be stored down in the form of a linked list, it usually starts to expand when the critical value is reached
         capacity * loadFactor = 16 * 0.75 = 12
         MAXIMUM_CAPACITY: Maximum capacity
         */
        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];
        useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        init();
    }
	
	/**
     * Associates the specified value with the specified key in this mapping. If the mapping previously contained a key, the old value is replaced
     *
     * @param key   The key associated with the specified value
     * @param value The value to associate with the specified key
     * @return The previous value associated with the key is null if the key has no mapping. (null return can also indicate that the mapping previously associated null with key.)
     */
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        // Get hash value
        int hash = hash(key);
        // Calculate the storage location of data at the bottom of HashMap through the corresponding algorithm
        int i = indexFor(hash, table.length);
        // Determine whether an element exists in the current position
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            // Determine whether the hash value is the same as the value itself
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                // If the hash value and key value are the same, replace the value value of the existing key
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        // If there is no element, the hash value is different, and the value itself is different, the addition method will be executed
        addEntry(hash, key, value, i);
        return null;
    }
	
	/**
	 * DP Hash 
	 */
	final int hash(Object k) {
        int h = 0;
        if (useAltHashing) {
            if (k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            h = hashSeed;
        }

        h ^= k.hashCode();

        // This function ensures that hashcodes that differ only by a constant multiple at each bit position have a limited number of conflicts (about 8 at the default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
	
	/**
     * Find the corresponding storage location according to the hash value
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
	
	/**
	 * Capacity expansion method
	 * If you need to expand capacity, you can add data first. If you don't need to, you can directly execute the add method
	 */
	void addEntry(int hash, K key, V value, int bucketIndex) {
        // If the current critical value is less than or equal to the number of key value mappings and the current array position is not empty, the capacity expansion operation is performed
        if ((size >= threshold) && (null != table[bucketIndex])) {
            // Capacity expansion: when the critical value is reached and the current array position is not empty, the capacity of the array will be doubled by default
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }
	
	/**
	 * New method
	 */
	void createEntry(int hash, K key, V value, int bucketIndex) {
        // Take out the data in the original position
        Entry<K,V> e = table[bucketIndex];
        // Then, put the newly created Entry object in the current location and store it in a linked list (from top to bottom, similar to HashSet (JDK7))
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
	
	Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }
}
JDK8
package java.util;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
	/**
     * The load factor to use when not specified in the constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
	
	/**
     * Load factor of hash table
     * @serial
     */
    final float loadFactor;
	
	/**
	 * An array of data.
	 */
	transient Node<K,V>[] table;
	
	/**
     * constructor 
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
	
	/**
     * Associates the specified value with the specified key in this mapping. If the mapping previously contained a key, the old value is replaced
     *
     * @param key   The key associated with the specified value
     * @param value The value to associate with the specified key
     * @return The previous value associated with the key is null if the key has no mapping. (null return can also indicate that the mapping previously associated null with key.)
     */
	public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
	
	/**
     * Get hash value
     */
	static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
	
    /**
     * Implementation of Map.put and related methods
     *
     * @param hash Hash of key
     * @param key key
     * @param value Value to place
     * @param onlyIfAbsent If true, do not change the existing value
     * @param evict If false, the table is in create mode
     * @return Previous value, 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;
    }
	
	/**
     * Initialize or double the table size.
	 * If it is blank, it is allocated according to the initial capacity target held in the field threshold.
	 * Otherwise, because we use a power of 2 extension, the elements in each bin must maintain the same index or move at an offset of a power of 2 in the new 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;
    }
	
	/**
     * Replace all linked nodes in the bin at the given hash index unless the table is too small, in which case resize instead.
     */
	final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }
}

Important constants in HashMap source code

  • DEFAULT_ INITIAL_ Capability: default capacity of HashMap: 16
  • DEFAULT_LOAD_FACTOR: default load factor of HashMap: 0.75
  • threshold: critical value of expansion, = capacity * filling factor: 16 * 0.75 = 12
  • TREEIFY_THRESHOLD: if the length of the linked list in the Bucket is greater than the default value, it will be converted into a red black tree: 8
  • MIN_ TREEIFY_ Capability: minimum hash table capacity when nodes in the bucket are trealized: 64

Here are just a few examples. There are others you can check the source code yourself

Use of LinkedHashMap

  • LinkedHashMap is a subclass of HashMap

  • Based on the HashMap storage structure, a pair of two-way linked lists are used to record the order of adding elements

  • Similar to LinkedHashSet

LinkedHashMap underlying principle (understand)

The underlying structure of LinkedHashMap is the same as that of HashMap, because LinkedHashMap inherits from HashMap
Difference: the LinkedHashMap internally provides an Entry to replace the Node in the HashMap

LinkedHashMap internal class:

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;      // Able to record the order of added elements
    Entry(int hash, K key, V value, Node<K,V> next) {
       super(hash, key, value, next);
   }
}

Internal classes in HashMap

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

Use of TreeMap

introduce

  1. When storing key value pairs in TreeMap, you need to sort them according to key value pairs. TreeMap can ensure that all key value pairs are in an orderly state.
  2. The underlying TreeSet uses a red black tree structure to store data.
  3. Adding key value to TreeMap requires that the key must be an object created by the same class, because it needs to be sorted by key: natural sorting and custom sorting.

Note: you can only sort by key, not by value!!!

case

package com.laoyang.test.day5;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName TreeMapTest
 * @Description:  TreeMap Use of
 * @Author Laoyang
 * @Date 2021/10/13 15:08
 */
public class TreeMapTest {
    /**
     * Natural sorting
     */
    @Test
    public void testOne() {
        TreeMap map = new TreeMap();
        User userA = new User("Tom", 22);
        User userB = new User("Jerry", 18);
        User userC = new User("Jim", 35);
        User userD = new User("Mike", 6);
        map.put(userA, 60);
        map.put(userB, 76);
        map.put(userC, 32);
        map.put(userD, 100);
        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "The results are:" + entry.getValue());
        }
    }

    /**
     * Custom sorting
     */
    @Test
    public void testTwo() {
        Comparator comparator = new Comparator() {
            // Sort by age
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User) {
                    User userA = (User) o1;
                    User userB = (User) o2;
                    return Integer.compare(userA.getAge(), userB.getAge());
                }
                throw new RuntimeException("Inconsistent type");
            }
        };

        TreeMap map = new TreeMap(comparator);
        User userA = new User("Tom", 22);
        User userB = new User("Jerry", 18);
        User userC = new User("Jim", 35);
        User userD = new User("Mike", 6);
        map.put(userA, 60);
        map.put(userB, 76);
        map.put(userC, 32);
        map.put(userD, 100);
        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "The results are:" + entry.getValue());
        }
    }
}

class User implements Comparable {
    private String name;
    private int age;

    /**
     * Override the equals() method
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

    /**
     * Override hashCode() method
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, 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;
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public User() {
    }

    /**
     * Sort by name from small to large
     */
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User user = (User) o;
            int i = this.name.compareTo(user.name);
            if (i != 0) {
                return i;
            } else {
                return 0;
            }
        }
        throw new RuntimeException("Inconsistent type");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Use of Hashtable

  • Hashtable is an ancient Map implementation class, which is provided by JDK1.0. Unlike HashMap, hashtable is thread safe.
  • The implementation principle and function of Hashtable are the same as that of HashMap. The bottom layer uses hash table structure, which has fast query speed and can be used with each other in many cases. Unlike HashMap, Hashtable does not allow null as key and value.
  • Like HashMap, Hashtable cannot guarantee the order of key value pairs.
  • Hashtable judges that two key s are equal and two value s are equal, which is consistent with HashMap.

Use of Properties

introduce

  1. The Properties class is a subclass of Hashtable, which is used to process property files
  2. Since the key and value in the Properties file are of string type, the key and value in Properties are of string type
  3. When accessing data, it is recommended to use setProperty(String key,String value) method and getProperty(String key) method

case

Configuration file - jdbc.properties

username=root
password=123456

The file is created under the parent project (because this is just to test the effect, you can write anything after creation)

Note: spaces cannot appear on both sides of the equal sign (=) in the configuration file, such as name = "AAA"

Test class

package com.laoyang.test.day5;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * @ClassName PropertiesTest
 * @Description:  Properties Use of
 * @Author Laoyang
 * @Date 2021/10/13 15:28
 */
public class PropertiesTest {
    /**
     Properties: It is commonly used to process configuration files. Both key and value are String types
     */
    public static void main(String[] args) {
        FileInputStream stream = null;
        try {
            Properties properties = new Properties();
            stream = new FileInputStream("jdbc.properties");
            properties.load(stream);
            String name = properties.getProperty("username");
            String password = properties.getProperty("password");
            System.out.println("Account number:" + name + ",Password is:" + password);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Sometimes writing configuration files leads to garbled code, which may be because the coding format is not set properly

Extension: if you do not want the configuration file to be garbled, you need to set the corresponding encoding format before creating the configuration file (if the file has been created and garbled, you need to delete the corresponding configuration file first, and then make encoding settings before writing, otherwise the correct effect will not be achieved)
If you are using IDEA, you need to tick File > editor > file encodings > International native to ASCII conversion

Use of Collections tool class

Tool class for manipulating Arrays: Arrays

introduce

  1. Collections is a tool class that operates on collections such as Set, List, and Map
  2. Collections provides a series of static methods to sort, query and modify collection elements. It also provides methods to set immutable collection objects and realize synchronous control over collection objects

common method

Sort operation

methodeffect
reverse(List list)Inverts all elements in the collection
shuffle(List list)Randomly sort collection elements
sort(List list)Sort collection elements naturally
sort(List list, Comparator com)Custom sort collection elements
swap(List list,int i, int j)Swaps elements at a specified location in a collection

Find, replace

methodeffect
Object max(Collection)Returns the largest element in a given set according to the natural order of the elements
Object max(Collection,Comparator)Returns the largest element in a given collection according to the custom order of elements
Object min(Collection)Returns the smallest element in a given set according to the natural order of the elements
Object min(Collection,Comparator)Returns the smallest element in a given set according to the custom order of elements
int frequency(Collection,Object)Returns the number of occurrences of the specified element in the specified collection
void copy(List dest,List src)Copy the contents of src to dest
boolean replaceAll(List list,Object oldVal,Object newVal)Replace the old value (newVal) in the List with the new value (oldVal)

Note that copy()

Synchronous control

The Collections class provides multiple synchronizedXxx() methods, which can wrap the specified collection into a thread synchronized collection, so as to solve the thread safety problem when multiple threads access the collection concurrently

methodeffect
synchronizedCollection(Collection c)Returns a synchronized (thread safe) collection supported by the specified collection
synchronizedList(List list)Returns a list of synchronization (thread safe) supported by the specified list
synchronizedMap(Map map)Returns a synchronous (thread safe) Map supported by the specified Map
synchronizedSet(Set set)Returns the synchronized (thread safe) set supported by the specified set
synchronizedSortedMap(SortedMap m)Returns a synchronous (thread safe) sort map supported by the specified sort map
synchronizedSortedSet(SortedSet s)Returns the synchronous (thread safe) sort set supported by the specified sort set

case

package com.laoyang.test.day6;

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @ClassName CollectionUtil
 * @Description:  Collections Use of tool classes
 * @Author Laoyang
 * @Date 2021/10/13 15:59
 */
public class CollectionsUtil {
    /**
     Sorting operations: (all static methods)
     reverse(List): Reverse the order of elements in the List
     shuffle(List): Random sorting of List collection elements
     sort(List): Sorts the specified List collection elements in ascending order according to the natural order of the elements
     sort(List,Comparator): Sorts the List collection elements according to the order generated by the specified Comparator
     swap(List,int, int): Exchange the elements at i and j in the specified list collection
     */
    @Test
    public void testOne() {
        List list = new ArrayList();
        list.add(22);
        list.add(57);
        list.add(33);
        list.add(19);
        System.out.println(list);   // [22, 57, 33, 19]

//        Collections.reverse(list);      //  Invert collection elements
//        Collections.shuffle(list);      //  Randomly sort collection elements
//        Collections.sort(list);         //  Sort the collection elements naturally. If you want to use custom sorting, you need to add a Comparator parameter
        Collections.swap(list, 1, 3);   // Swaps elements at a specified location in a collection

        System.out.println(list);
    }

    /**
     Find, replace
     Object max(Collection): Returns the largest element in a given set according to the natural order of the elements
     Object max(Collection,Comparator): Returns the largest element in a given collection in the order specified by the Comparator
     Object min(Collection): Returns the smallest element in a given set according to the natural order of the elements
     Object min(Collection,Comparator): Returns the smallest element in a given set according to the custom order of elements
     int frequency(Collection,Object): Returns the number of occurrences of the specified element in the specified collection
     void copy(List dest,List src): Copy the contents of src to dest
     boolean replaceAll(List list,Object oldVal,Object newVal): Replace all old values of the List object with the new values
     */
    @Test
    public void testTwo() {
        List list = new ArrayList();
        list.add(22);
        list.add(57);
        list.add(57);
        list.add(33);
        list.add(19);
        System.out.println(list);   // [22, 57, 57, 33, 19]

        // max(Collection coll): find the maximum value in the collection according to the natural sorting. To customize the sorting, you need to add the Comparator parameter
        System.out.println(Collections.max(list));    // 57

        // min(Collection coll): find the minimum value in the collection according to the natural sorting, and add the Comparator parameter to customize the sorting
        System.out.println(Collections.min(list));    // 19

        // frequency(Collection coll, Object obj): 
        System.out.println(Collections.frequency(list, 57));    // 2

        System.out.println("------------------------");

        //copy(List dest, List src): copy, copy the values in src to dest
        /*
         // Exception: java.lang.IndexOutOfBoundsException: Source does not fit in dest
         List list1 = new ArrayList();
         Collections.copy(list1, list);
         */

        // Correct writing
        List list1 = Arrays.asList(new Object[list.size()]);
        System.out.println(list1.size());   // 5
        Collections.copy(list1, list);      // Successful execution

        System.out.println(list1);

        System.out.println("------------------------");

        //Replaceall (List, Object oldVal, Object newVal): replace the old value (newVal) in the List with the new value (oldVal)
        Collections.replaceAll(list, 22, 99);

        System.out.println(list);   // [99, 57, 57, 33, 19]
    }

    /**
     Collections Class, which enables you to wrap the specified collection into a thread synchronized collection,
     This can solve the thread safety problem when multithreading accesses the collection concurrently
     */
    @Test
    public void testThree() {
        List list = new ArrayList();
        list.add(22);
        list.add(57);
        list.add(57);
        list.add(33);
        list.add(19);
        System.out.println(list);   // [22, 57, 57, 33, 19]

        // The returned list1 is thread safe
        List list1 = Collections.synchronizedList(list);
    }
}

Exercise one

Please input 10 integers randomly from the keyboard, save them to the List, and display them in reverse order, from large to small

package com.laoyang.test.day6;

import java.util.*;

/**
 * @ClassName Practise
 * @Description:  Exercise one
 * @Author Laoyang
 * @Date 2021/10/13 17:15
 */
public class ExerciseOne {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        List list = new ArrayList();
        int j;
        for (int i = 0; i < 10; i++) {
            System.out.print("Please enter an integer:");
            j = input.nextInt();
            list.add(j);
        }
        Collections.sort(list);
        Collections.reverse(list);
        System.out.println(list);
    }
}

Exercise 2

Enter student names and test scores into the collection, and display the names of the top three students by score

package com.laoyang.test.day6;

import org.junit.Test;
import java.util.*;

/**
 * @ClassName ExerciseTwo
 * @Description:  Exercise 2
 * @Author Laoyang
 * @Date 2021/10/13 17:28
 */
public class ExerciseTwo {
    @Test
    public void testOne() {
        List list = new ArrayList();
        list.add(new Student("Tom", 60));
        list.add(new Student("Jerry", 80));
        list.add(new Student("Jim", 73));
        list.add(new Student("Mike", 52));
        list.add(new Student("Noob", 90));

        Collections.sort(list, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Student && o2 instanceof Student) {
                    Student stuA = (Student) o1;
                    Student stuB = (Student) o2;
                    return -Double.compare(stuA.getFraction(), stuB.getFraction());
                }
                throw new RuntimeException("Inconsistent type");
            }
        });

        for (int i = 0; i < 3; i++) {
            System.out.println(list.get(i));
        }
    }
}

class Student {
    private String name;
    private double fraction;

    public Student() {
    }

    public Student(String name, double fraction) {
        this.name = name;
        this.fraction = fraction;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getFraction() {
        return fraction;
    }

    public void setFraction(double fraction) {
        this.fraction = fraction;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", fraction=" + fraction +
                '}';
    }
}

Interview questions

1, What are the similarities and differences of ArrayList, LinkedList and Vector?

Similarities: the three classes implement the List interface, and the characteristics of storing data are the same (they are ordered and repeatable data)

difference:

ArrayList: as the main implementation class of the List interface (JDK1.2 appears); High execution efficiency (thread unsafe); The underlying layer uses Object[] elementData for storage;

LinkedList: JDK1.2 appears; For frequent insert and delete operations, the efficiency of using this type is higher than that of ArrayList; The bottom layer uses two-way linked list storage;

Vector: as an ancient implementation class of the List interface (JDK1.0 appears); Low execution efficiency (thread safe); The underlying layer uses Object[] elementData for storage;

The List interface also appears in JDK1.2

2, What is the final print result of the following code?

Question 1

package com.laoyang.test.day2;

import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ListInterviewQuestions
 * @Description:  Interview questions
 * @Author Laoyang
 * @Date 2021/10/2 10:43
 */
public class ListInterviewQuestions {
    @Test
    public void testOne() {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list);  //1,2
    }

    private static void updateList(List list) {
        // By default, when deleting integer elements, remove(int index) in the List will be called first instead of remove(Object obj)
        list.remove(2);    				   // Delete according to subscript, [1, 2]
        //list.remove(new Integer(2));     //  Delete according to element, [1, 3]
    }
}

Main considerations: distinguish between remove(int index) and remove(Object obj) in the list

Here are two ways. You can open the comments to test and see the final effect

Question 2

package com.laoyang.test.day4;

import org.junit.Test;
import java.util.HashSet;
import java.util.Objects;

/**
 * @ClassName Set
 * @Description:  Interview questions
 * @Author Laoyang
 * @Date 2021/10/10 11:09
 */
public class SetInterviewQuestions {
    @Test
    public void test() {
        HashSet set = new HashSet();
        Person p1 = new Person(1001,"AA");
        Person p2 = new Person(1002,"BB");
        set.add(p1);
        set.add(p2);

        // Modify the name value of p1 in set
        p1.name = "CC";
        /*
         At this time, remove finds 1001, the storage location of CC
         p1 is stored according to the hash value of 1001 and AA
         Therefore, remove cannot find p1 at this time, so it cannot be deleted successfully
         */
        set.remove(p1);
        System.out.println(set);    // 1002,BB;1001,CC

        /*
          Because p1 is stored according to the location found by 1001 and AA, even if it is later modified to 1001 and CC, the location has not changed
          Therefore, 1001 and CC here can be added successfully
         */
        set.add(new Person(1001,"CC"));
        System.out.println(set);     // 1002,BB;1001,CC;1001,CC

        /*
         Here, the hash value of AA is consistent with p1, but the value of p1 is now 1001, CC
         Therefore, after the hash values are the same, call equals() to determine whether the values of the two objects are the same, AA= CC, so it can be added successfully
         */
        set.add(new Person(1001,"AA"));
        System.out.println(set);    // 1002,BB;1001,CC;1001,CC;1001,AA
    }
}

class Person {
    public int id;
    public String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

3, The underlying implementation principle of HashMap?

If you can't understand the above, baidu on your own

4, What are the similarities and differences between HashMap and Hashtable?

If you can't understand the above, baidu on your own

5, What are the similarities and differences between CurrentHashMap and Hashtable?

Skip temporarily

6, What is the difference between Collections and Collections?

A Collection is a Collection interface that stores a single column interface
Collection s is a tool class that operates on Collections

7, What impact does the size of load factor have on HashMap?

  1. The load factor determines the data density of HashMap
  2. The larger the load factor, the higher the density, the higher the probability of collision, and the longer the linked list in the array, resulting in an increase in the number of comparisons during query or insertion and a decline in performance.
  3. The smaller the load factor, the easier it is to trigger capacity expansion, and the smaller the data density, which means that the smaller the probability of collision, the shorter the linked list in the array, the smaller the number of comparisons during query and insertion, and the higher the performance. However, a certain amount of content space will be wasted, and frequent capacity expansion will also affect the performance.
  4. According to the reference and research experience of other languages, it will be considered to set the load factor to 0.7 ~ 0.75, and the average retrieval length is close to a constant.

Topics: Java Back-end set