Detailed Explanation of Java Collection and Map

Articles and codes have been archived in [Github warehouse: https://github.com/timerring/java-tutorial] or the public account [AIShareLab] can also be obtained by replying to java .

Article directory

Collection understanding and benefits

We used an array to save multiple data before, so the array has deficiencies, let’s analyze it

array

1) The length must be specified at the beginning, and once specified, it cannot be changed

2) The saved elements must be of the same type

3) Adding/deleting elements using an array is cumbersome

eg: Write out the schematic code for expanding the Person array.

Person[] pers = new Person[1]; //大小是1
per[0] = new Person();  
//增加新的Person对象?
Person[] pers2 = new Person[pers.length+1];//新创建数组
for(){
    
    } //拷贝pers数组的元素到per2
pers2[pers2.length-1] = new Person()://添加新的对象

gather

1) Any number of objects can be dynamically saved, which is more convenient to use

2) Provides a series of convenient methods for manipulating objects: add, remove, set, get, etc.

3) Adding/removing new elements using collections is concise

collection framework

There are many collection classes in Java, which are mainly divided into two categories, as shown in the figure!

  1. Collections are mainly two groups (single-column collections, double-column collections)
  2. The Collection interface has two important sub-interfaces List Set, and their implementation subclasses are all single-column collections (single-column data)
  3. The implementation subclass of the Map interface is a double-column collection, and the stored KV (double-column data)
package com.hspedu.collection_;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class Collection_ {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    
        //Collection
        //Map
        ArrayList arrayList = new ArrayList();
        arrayList.add("jack");
        arrayList.add("tom");

        HashMap hashMap = new HashMap();
        hashMap.put("NO1", "北京");
        hashMap.put("NO2", "上海");
    }
}

Collection interface and common methods

The characteristics of the Collection interface implementation class

public interface Collection <E> extends lterable <E>
  1. The collection implementation subclass can store multiple elements, and each element can be an Object
  2. Some Collection implementation classes can store duplicate elements, and some cannot.
  3. Some Collection implementation classes, some are ordered (List), and some are not ordered (Set)
  4. The Collection interface does not directly implement subclasses, but is implemented through its subinterfaces Set and List
package com.hspedu.collection_;

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

public class CollectionMethod {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    
        List list = new ArrayList();
//        add:添加单个元素
        list.add("jack");
        list.add(10);//list.add(new Integer(10)) 本质是对象
        list.add(true);
        System.out.println("list=" + list);
//        remove:删除指定元素
        list.remove(0);//删除第一个元素 返回boolen
        list.remove(true);//指定删除某个元素 返回该obj
        System.out.println("list=" + list);
//        contains:查找元素是否存在
        System.out.println(list.contains("jack"));//T
//        size:获取元素个数
        System.out.println(list.size());//2
//        isEmpty:判断是否为空
        System.out.println(list.isEmpty());//F
//        clear:清空
        list.clear();
        System.out.println("list=" + list);
//        addAll:添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("红楼梦");
        list2.add("三国演义");
        list.addAll(list2);
        System.out.println("list=" + list);
//        containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list2));//T
//        removeAll:删除多个元素
        list.add("聊斋");
        list2.add("时间是金");
        list.removeAll(list2);
        System.out.println("list=" + list);//[聊斋]
//        说明:以ArrayList实现类来演示.
    }
}

Collection interface traversal element method 1 - use Iterator

  1. The Iterator object is called an iterator, which is mainly used to traverse the elements in the Collection collection.

  2. All collection classes that implement the Collection interface have an iterator() method to return an object that implements the iterator interface, that is, an iterator can be returned.

  3. Iterator structure

    How iterators work

    lterator iterator = coll.iterator(); //得到一个集合的迭代器
    //hasNext():判断是否还有下一个元素
    while(iterator.hasNext()){
          
          
    //next()作用:1.下移2.将下移以后集合位置上的元素返回
    System.out.println(iterator.next());
    }
    

    Methods of the iterator interface

    Before calling the iterator.next() method, iterator.hasNext() must be called for detection. If not called, and the next record is invalid, calling iterator.next() directly will throw NoSuchElementException.

  1. Iterator is only used to traverse collections, and Iterator itself does not store objects.
package com.hspedu.collection_;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionIterator {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    

        Collection col = new ArrayList();

        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("小李飞刀", "古龙", 5.1));
        col.add(new Book("红楼梦", "曹雪芹", 34.6));


        //System.out.println("col=" + col);
        //现在老师希望能够遍历 col集合
        //1. 先得到 col 对应的 迭代器
        Iterator iterator = col.iterator();
        //2. 使用while循环遍历
//        while (iterator.hasNext()) {//判断是否还有数据
//            //返回下一个元素,类型是Object
//            Object obj = iterator.next();
//            System.out.println("obj=" + obj);
//        }
        //教大家一个快捷键,快速生成 while => itit
        //显示所有的快捷键的的快捷键 ctrl + j
        while (iterator.hasNext()) {
    
    
            Object obj = iterator.next();
            System.out.println("obj=" + obj);

        }
        //3. 当退出while循环后 , 这时iterator迭代器,指向最后的元素
        //   iterator.next();// 此时如果再取则会报错 NoSuchElementException
        //4. 如果希望再次遍历,需要重置我们的迭代器
        iterator = col.iterator(); // 重置迭代器
        System.out.println("===第二次遍历===");
        while (iterator.hasNext()) {
    
    
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }
    }
}

class Book {
    
    
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
    
    
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
    
    
        return name;
    }

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

    public String getAuthor() {
    
    
        return author;
    }

    public void setAuthor(String author) {
    
    
        this.author = author;
    }

    public double getPrice() {
    
    
        return price;
    }

    public void setPrice(double price) {
    
    
        this.price = price;
    }

    @Override
    public String toString() {
    
    
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

Collection interface traversal object method 2-for loop enhancement

Enhanced for loop can replace iterator iterator, features: enhanced for is a simplified version of iterator, the essence is the same. Can only be used to iterate over collections or arrays.

basic grammar

for(元素类型 元素名:集合名或数组名){
    
    
}

Example:

package com.hspedu.collection_;

import java.util.ArrayList;
import java.util.Collection;

public class CollectionFor {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    
        Collection col = new ArrayList();

        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("小李飞刀", "古龙", 5.1));
        col.add(new Book("红楼梦", "曹雪芹", 34.6));

        //1. 使用增强for, 在Collection集合
        //2. 增强for, 底层仍然是迭代器
        //3. 增强for可以理解成就是简化版本的 迭代器遍历
        //4. 快捷键方式 I
//        for (Object book : col) {
    
    
//            System.out.println("book=" + book);
//        }
        for (Object o : col) {
    
    
            System.out.println("book=" + o);
        }

        //增强for,也可以直接在数组使用
//        int[] nums = {1, 8, 10, 90};
//        for (int i : nums) {
    
    
//            System.out.println("i=" + i);
//        }
    }
}

List interface and common methods

Basic introduction to List interface

The List interface is a subinterface of the Collection interface

  1. The elements in the List collection class are in order (that is, the order of adding is the same as the order of taking out), and can be repeated

  2. Each element in the List collection has its corresponding sequential index, which is the backing index.

  3. The elements in the List container correspond to an integer serial number to record its position in the container, and the elements in the container can be accessed according to the serial number.

  4. There are many implementation classes of the List interface in the JDK API:

Common methods of the List interface

This part of the method is unique to List, and set cannot be used.

  • 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): Get the element at the specified index position
  • 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): Remove the element at the specified index position and return this element
  • Object set(int index, Object ele): Set the element at the specified index position to ele, which is equivalent to replacement.
  • List subList(int fromIndex, int toIndex): Returns the sub-collection from fromIndex to toIndex. (fromIndex <= subList < toIndex)
package com.hspedu.list_;

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

public class ListMethod {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    
        List list = new ArrayList();
        list.add("张三丰");
        list.add("贾宝玉");
//        void add(int index, Object ele):在index位置插入ele元素
        //在index = 1的位置插入一个对象(index 从0开始)
        list.add(1, "韩顺平");
        System.out.println("list=" + list);
//        boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
        List list2 = new ArrayList();
        list2.add("jack");
        list2.add("tom");
        list.addAll(1, list2);
        System.out.println("list=" + list);
//        Object get(int index):获取指定index位置的元素
        //说过
//        int indexOf(Object obj):返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("tom"));//2
//        int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
        list.add("韩顺平");
        System.out.println("list=" + list);
        System.out.println(list.lastIndexOf("韩顺平"));
//        Object remove(int index):移除指定index位置的元素,并返回此元素
        list.remove(0);
        System.out.println("list=" + list);
//        Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换.
        list.set(1, "玛丽");
        System.out.println("list=" + list);
//        List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
        // 注意返回的子集合 fromIndex <= subList < toIndex
        List returnlist = list.subList(0, 2);
        System.out.println("returnlist=" + returnlist);
    }
}

List interface class exercises

Add more than 10 elements (such as String "hello"), insert an element "Han Shunping Education" in the 2nd position, get the 5th element, delete the 6th element, modify the 7th element, and use the iterator to traverse the collection , Requirement: use the implementation class ArrayList of List to complete.

package com.hspedu.list_;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListExercise {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    
        /*
        添加10个以上的元素(比如String "hello" ),在2号位插入一个元素"韩顺平教育",
        获得第5个元素,删除第6个元素,修改第7个元素,在使用迭代器遍历集合,
        要求:使用List的实现类ArrayList完成。
         */
        List list = new ArrayList();
        for (int i = 0; i < 12; i++) {
    
    
            list.add("hello" + i);
        }
        System.out.println("list=" + list);

        //在2号位插入一个元素"韩顺平教育"
        list.add(1, "韩顺平教育");
        System.out.println("list=" + list);
        //获得第5个元素
        System.out.println("第五个元素=" + list.get(4));
        //删除第6个元素
        list.remove(5);
        System.out.println("list=" + list);
        //修改第7个元素
        list.set(6, "三国演义");
        System.out.println("list=" + list);

        //在使用迭代器遍历集合
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
    
    
            Object obj =  iterator.next();
            System.out.println("obj=" + obj);
        }
    }
}

Three traversal methods of List [ArrayList, LinkedList, Vector]

package com.hspedu.list_;

import java.util.*;

public class ListFor {
    
    
    @SuppressWarnings({
    
    "all"})
    public static void main(String[] args) {
    
    

        //List 接口的实现子类 Vector LinkedList
        //List list = new ArrayList();
        //List list = new Vector();
        List list = new LinkedList();
        list.add("jack");
        list.add("tom");
        list.add("鱼香肉丝");
        list.add("北京烤鸭子");

        //遍历
        //1. 迭代器
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
    
    
            Object obj =  iterator.next();
            System.out.println(obj);
        }
        
        System.out.println("=====增强for=====");
        //2. 增强for
        for (Object o : list) {
    
    
            System.out.println("o=" + o);
        }

        System.out.println("=====普通for====");
        //3. 使用普通for
        for (int i = 0; i < list.size(); i++) {
    
    
            System.out.println("对象=" + list.get(i));
        }
    }
}

Implementation Class Exercise 2

package com.hspedu.list_;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

@SuppressWarnings({
    
    "all"})
public class ListExercise02 {
    
    

    public static void main(String[] args) {
    
    

        //List list = new ArrayList();
        List list = new LinkedList();
        //List list = new Vector();
        list.add(new Book("红楼梦", "曹雪芹", 100));
        list.add(new Book("西游记", "吴承恩", 10));
        list.add(new Book("水浒传", "施耐庵", 19));
        list.add(new Book("三国", "罗贯中", 80));
        //list.add(new Book("西游记", "吴承恩", 10));

        //如何对集合进行排序


        //遍历

        for (Object o : list) {
    
    
            System.out.println(o);
        }

        //冒泡排序
        sort(list);

        System.out.println("==排序后==");

        for (Object o : list) {
    
    
            System.out.println(o);
        }

    }

    //静态方法
    //价格要求是从小到大
    public static void sort(List list) {
    
    

        int listSize = list.size();
        for (int i = 0; i < listSize - 1; i++) {
    
    
            for (int j = 0; j < listSize - 1 - i; j++) {
    
    
                //取出对象Book
                Book book1 = (Book) list.get(j);
                Book book2 = (Book) list.get(j + 1);
                if (book1.getPrice() > book2.getPrice()) {
    
    //交换
                    list.set(j, book2);
                    list.set(j + 1, book1);
                }
            }
        }

    }
}

ArrayList underlying structure and source code analysis

Considerations for ArrayLists

  1. permits all elements, including null , ArrayList can add null (empty value), and can be multiple.

  2. ArrayList is an array to achieve data storage [source code]

  3. ArrayList is basically equivalent to Vector, except that ArrayList is not thread-safe (high execution efficiency). See the source code. It is not recommended to use ArrayList in multi-threaded situations .

Source code analysis of the underlying operating mechanism of ArrayList

  1. ArrayList maintains an array elementData of Object type.

    transient Object[] elementData; //transient表示瞬间,短暂的,表示该属性不会被序列号
    
  2. When creating an ArrayList object, if the no-argument constructor is used, the initial elementData capacity is 0. When adding for the first time, expand the elementData to 10. If you need to expand again, expand the elementData to 1.5 times .

  3. If the constructor with a specified size is used, the initial elementData capacity is the specified size. If expansion is required, the elementData will be expanded directly by 1.5 times .

Suggestion: debug the process of creating and expanding the ArrayList by yourself.

package com.hspedu.list_;

import java.util.ArrayList;

@SuppressWarnings({
    
    "all"})
public class ArrayListSource {
    
    
    public static void main(String[] args) {
    
    

        //Idea 默认情况下,Debug 显示的数据是简化后的,如果希望看到完整的数据需要做设置。
        //使用无参构造器创建ArrayList对象
        //ArrayList list = new ArrayList();
        ArrayList list = new ArrayList(8);
        //使用for给list集合添加 1-10数据
        for (int i = 1; i <= 10; i++) {
    
    
            list.add(i);
        }
        //使用for给list集合添加 11-15数据
        for (int i = 11; i <= 15; i++) {
    
    
            list.add(i);
        }
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

Idea By default, the data displayed by Debug is simplified. If you want to see the complete data, you need to make settings.

Vector underlying structure and source code analysis

Basic introduction to Vector

  1. Definition of Vector class
public class vector<E>extends AbstractList<E>
implements List<E>RandomAccess,cloneable,Serializable
  1. The bottom layer of Vector is also an array of objects,protected Object[] elementData;

  2. Vector is thread-synchronized, that is, thread-safe, and the operation method of the Vector class has

synchronizedpublic synchronized E get(int index){
    
    
if (index >= elementCount)
  throw new ArraylndexOutOfBoundsException(index);
return elementData(index);
}
  1. In development, when thread synchronization safety is required, consider using Vector
package com.hspedu.list_;

import java.util.Vector;

@SuppressWarnings({
    
    "all"})
public class Vector_ {
    
    
    public static void main(String[] args) {
    
    
        //无参构造器
        //有参数的构造
        Vector vector = new Vector(8);
        for (int i = 0; i < 10; i++) {
    
    
            vector.add(i);
        }
        vector.add(100);
        System.out.println("vector=" + vector);
        //1. new Vector() 底层
        /*
            public Vector() {
                this(10);
            }
         补充:如果是  Vector vector = new Vector(8);
            走的方法:
            public Vector(int initialCapacity) {
                this(initialCapacity, 0);
            }
         2. vector.add(i)
         2.1  //下面这个方法就添加数据到vector集合
            public synchronized boolean add(E e) {
                modCount++;
                ensureCapacityHelper(elementCount + 1);
                elementData[elementCount++] = e;
                return true;
            }
          2.2  //确定是否需要扩容 条件 : minCapacity - elementData.length>0
            private void ensureCapacityHelper(int minCapacity) {
                // overflow-conscious code
                if (minCapacity - elementData.length > 0)
                    grow(minCapacity);
            }
          2.3 //如果 需要的数组大小 不够用,就扩容 , 扩容的算法
              //newCapacity = oldCapacity + ((capacityIncrement > 0) ?
              //                             capacityIncrement : oldCapacity);
              //就是扩容两倍.
            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);
            }
         */

    }
}

Comparison of Vector and ArrayList

LinkedList underlying structure

Comprehensive description of LinkedList

  1. The bottom layer of LinkedList implements the characteristics of doubly linked list and double-ended queue
  2. Any element can be added (elements can be repeated), including null
  3. Thread is not safe, no synchronization is implemented

The underlying operation mechanism of LinkedList

  1. The bottom layer of LinkedList maintains a doubly linked list.
  2. Two attributes first and last are maintained in LinkedList to point to the first node and the last node respectively
  3. Each node (Node object) maintains three attributes, prev, next, and item, among which
    prev points to the previous one, and next points to the next node. Finally, a doubly linked list is implemented.
  4. Therefore, the addition and deletion of elements of LinkedList is not done through arrays, which is relatively efficient.
  5. Simulate a simple doubly linked list

package com.hspedu.list_;

public class LinkedList01 {
    
    
    public static void main(String[] args) {
    
    
        //模拟一个简单的双向链表

        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node hsp = new Node("老韩");

        //连接三个结点,形成双向链表
        //jack -> tom -> hsp
        jack.next = tom;
        tom.next = hsp;
        //hsp -> tom -> jack
        hsp.pre = tom;
        tom.pre = jack;

        Node first = jack;//让first引用指向jack,就是双向链表的头结点
        Node last = hsp; //让last引用指向hsp,就是双向链表的尾结点


        //演示,从头到尾进行遍历
        System.out.println("===从头到尾进行遍历===");
        while (true) {
    
    
            if(first == null) {
    
    
                break;
            }
            //输出first 信息
            System.out.println(first);
            first = first.next;
        }

        //演示,从尾到头的遍历
        System.out.println("====从尾到头的遍历====");
        while (true) {
    
    
            if(last == null) {
    
    
                break;
            }
            //输出last 信息
            System.out.println(last);
            last = last.pre;
        }

        //演示链表的添加对象/数据,是多么的方便
        //要求,是在 tom --------- 插入一个对象 smith

        //1. 先创建一个 Node 结点,name 就是 smith
        Node smith = new Node("smith");
        //下面就把 smith 加入到双向链表了
        smith.next = hsp;
        smith.pre = tom;
        hsp.pre = smith;
        tom.next = smith;

        //让first 再次指向jack
        first = jack;//让first引用指向jack,就是双向链表的头结点

        System.out.println("===从头到尾进行遍历===");
        while (true) {
    
    
            if(first == null) {
    
    
                break;
            }
            //输出first 信息
            System.out.println(first);
            first = first.next;
        }

        last = hsp; //让last 重新指向最后一个结点
        //演示,从尾到头的遍历
        System.out.println("====从尾到头的遍历====");
        while (true) {
    
    
            if(last == null) {
    
    
                break;
            }
            //输出last 信息
            System.out.println(last);
            last = last.pre;
        }


    }
}

//定义一个Node 类,Node 对象 表示双向链表的一个结点
class Node {
    
    
    public Object item; //真正存放数据
    public Node next; //指向后一个结点
    public Node pre; //指向前一个结点
    public Node(Object name) {
    
    
        this.item = name;
    }
    public String toString() {
    
    
        return "Node name=" + item;
    }
}

Addition, deletion, modification and query of LinkedList

package com.hspedu.list_;

import java.util.Iterator;
import java.util.LinkedList;

@SuppressWarnings({
    
    "all"})
public class LinkedListCRUD {
    
    
    public static void main(String[] args) {
    
    

        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        System.out.println("linkedList=" + linkedList);

        //演示一个删除结点的
        linkedList.remove(); // 这里默认删除的是第一个结点
        //linkedList.remove(2);

        System.out.println("linkedList=" + linkedList);

        //修改某个结点对象
        linkedList.set(1, 999);
        System.out.println("linkedList=" + linkedList);

        //得到某个结点对象
        //get(1) 是得到双向链表的第二个对象
        Object o = linkedList.get(1);
        System.out.println(o);//999

        //因为LinkedList 是 实现了List接口, 遍历方式
        System.out.println("===LinkeList遍历迭代器====");
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
    
    
            Object next =  iterator.next();
            System.out.println("next=" + next);

        }

        System.out.println("===LinkeList遍历增强for====");
        for (Object o1 : linkedList) {
    
    
            System.out.println("o1=" + o1);
        }
        System.out.println("===LinkeList遍历普通for====");
        for (int i = 0; i < linkedList.size(); i++) {
    
    
            System.out.println(linkedList.get(i));
        }


        //源码阅读.
        /* 1. LinkedList linkedList = new LinkedList();
              public LinkedList() {}
           2. 这时 linkeList 的属性 first = null  last = null
           3. 执行 添加
               public boolean add(E e) {
                    linkLast(e);
                    return true;
                }
            4.将新的结点,加入到双向链表的最后
             void linkLast(E e) {
                final Node<E> l = last;
                final Node<E> newNode = new Node<>(l, e, null);
                last = newNode;
                if (l == null)
                    first = newNode;
                else
                    l.next = newNode;
                size++;
                modCount++;
            }

         */

        /*
          读源码 linkedList.remove(); // 这里默认删除的是第一个结点
          1. 执行 removeFirst
            public E remove() {
                return removeFirst();
            }
         2. 执行
            public E removeFirst() {
                final Node<E> f = first;
                if (f == null)
                    throw new NoSuchElementException();
                return unlinkFirst(f);
            }
          3. 执行 unlinkFirst, 将 f 指向的双向链表的第一个结点拿掉
            private E unlinkFirst(Node<E> f) {
                // assert f == first && f != null;
                final E element = f.item;
                final Node<E> next = f.next;
                f.item = null;
                f.next = null; // help GC
                first = next;
                if (next == null)
                    last = null;
                else
                    next.prev = null;
                size--;
                modCount++;
                return element;
            }
         */
    }
}

Comparison of ArrayList and LinkedList

Comparison of ArrayList and LinkedList

How to choose ArrayList and LinkedList:

  1. If we have many operations to check, choose ArrayList
  2. If we have many additions and deletions, choose LinkedList
  3. Generally speaking, in the program, 80%-90% are queries, so in most cases, ArrayList will be selected
  4. In a project, it is possible to choose flexibly according to the business. One module uses ArrayList, and the other module uses LinkedList. That is to say, it needs to be selected according to the business.

Set interface and common methods

Basic introduction to Set interface

  1. Unordered (inconsistent order of addition and removal), no index
  2. Duplicate elements are not allowed, so contain at most one null
  3. The implementation classes of the Set interface in the JDK API are:

Common methods of the Set interface

Like the List interface, the Set interface is also a subinterface of Collection, so the common methods are the same as Collection interface.

The traversal method of the Set interface

It is the same as the traversal method of Collection, because the Set interface is a sub-interface of the Collection interface.

  1. Iterators can be used
  2. enhanced for
  3. Cannot use index to get

Examples of common methods of the Set interface

package com.hspedu.set_;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

@SuppressWarnings({
    
    "all"})
public class SetMethod {
    
    
    public static void main(String[] args) {
    
    
        //1. 以Set 接口的实现类 HashSet 来讲解Set 接口的方法
        //2. set 接口的实现类的对象(Set接口对象), 不能存放重复的元素, 可以添加一个null
        //3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
        //4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定.
        Set set = new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");//重复
        set.add("jack");
        set.add("hsp");
        set.add("mary");
        set.add(null);//
        set.add(null);//再次添加null
        for(int i = 0; i <10;i ++) {
    
    
            System.out.println("set=" + set);
        }

        //遍历
        //方式1: 使用迭代器
        System.out.println("=====使用迭代器====");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
    
    
            Object obj =  iterator.next();
            System.out.println("obj=" + obj);

        }

        set.remove(null);

        //方式2: 增强for
        System.out.println("=====增强for====");

        for (Object o : set) {
    
    
            System.out.println("o=" + o);
        }

        //set 接口对象,不能通过索引来获取


    }
}

Set interface implementation class - HashSet

A comprehensive description of HashSet

  1. HashSet implements the Set interface

  2. HashSet is actually HashMap, look at the source code

public Hashset() {
    
    
  map = new HashMap<>();
}
  1. Null values ​​can be stored, but there can only be one null

  2. HashSet does not guarantee that the elements are in order, it depends on the result of the index after hashing. (That is, there is no guarantee that the order in which elements are stored is consistent with the order in which they are retrieved)

  3. There can be no repeated elements/objects, which has already been mentioned in the use of the Set interface.

package com.hspedu.set_;

import java.util.HashSet;
import java.util.Set;

@SuppressWarnings({
    
    "all"})
public class HashSet_ {
    
    
    public static void main(String[] args) {
    
    
        //1. 构造器走的源码
        /*
            public HashSet() {
                map = new HashMap<>();
            }
         2. HashSet 可以存放null ,但是只能有一个null,即元素不能重复
         */
        Set hashSet = new HashSet();
        hashSet.add(null);
        hashSet.add(null);
        System.out.println("hashSet=" + hashSet);

    }
}

HashSet Case Description

package com.hspedu.set_;



import java.util.HashSet;

@SuppressWarnings({
    
    "all"})
public class HashSet01 {
    
    
    public static void main(String[] args) {
    
    
        HashSet set = new HashSet();

        //说明
        //1. 在执行add方法后,会返回一个boolean值
        //2. 如果添加成功,返回 true, 否则返回false
        //3. 可以通过 remove 指定删除哪个对象
        System.out.println(set.add("john"));//T
        System.out.println(set.add("lucy"));//T
        System.out.println(set.add("john"));//F
        System.out.println(set.add("jack"));//T
        System.out.println(set.add("Rose"));//T


        set.remove("john");
        System.out.println("set=" + set);//3个

        //
        set  = new HashSet();
        System.out.println("set=" + set);//0
        //4 Hashset 不能添加相同的元素/数据?
        set.add("lucy");//添加成功
        set.add("lucy");//加入不了
        set.add(new Dog("tom"));//OK 不同的对象
        set.add(new Dog("tom"));//Ok
        System.out.println("set=" + set);

        //在加深一下. 非常经典的面试题.
        //看源码,做分析, 先给小伙伴留一个坑,以后讲完源码,你就了然
        //去看他的源码,即 add 到底发生了什么? => 底层机制.
        set.add(new String("hsp"));//ok
        set.add(new String("hsp"));//加入不了.
        System.out.println("set=" + set);


    }
}
class Dog {
    
     //定义了Dog类
    private String name;

    public Dog(String name) {
    
    
        this.name = name;
    }

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

Description of the underlying mechanism of HashSet

package com.hspedu.set_;

@SuppressWarnings({
    
    "all"})
public class HashSetStructure {
    
    
    public static void main(String[] args) {
    
    
        //模拟一个HashSet的底层 (HashMap 的底层结构)

        //1. 创建一个数组,数组的类型是 Node[]
        //2. 有些人,直接把 Node[] 数组称为 表
        Node[] table = new Node[16];

        //3. 创建结点
        Node john = new Node("john", null);

        table[2] = john;
        Node jack = new Node("jack", null);
        john.next = jack;// 将jack 结点挂载到john
        Node rose = new Node("Rose", null);
        jack.next = rose;// 将rose 结点挂载到jack

        Node lucy = new Node("lucy", null);
        table[3] = lucy; // 把lucy 放到 table表的索引为3的位置.
        System.out.println("table=" + table);


    }
}
class Node {
    
     //结点, 存储数据, 可以指向下一个结点,从而形成链表
    Object item; //存放数据
    Node next; // 指向下一个结点

    public Node(Object item, Node next) {
    
    
        this.item = item;
        this.next = next;
    }
}

Analyze the underlying implementation:

  1. The bottom layer of HashSet is HashMap
  2. When adding an element, first get the hash value - it will be converted into -> index value
  3. Find the storage data table table to see if there are elements stored in this index position
  4. If not, just join
  5. If there is, call equals to compare, if it is the same, give up adding, if not, add it to the end
  6. In Java8, if the number of elements in a linked list reaches TREEIFY_THRESHOLD (default is 8), and the size of the table >= MIN TREEIFY CAPACITY (default 64), it will be tree (red-black tree), otherwise it will still use array expansion mechanism.
package com.hspedu.set_;

import java.util.HashSet;

@SuppressWarnings({
    
    "all"})
public class HashSetSource {
    
    
    public static void main(String[] args) {
    
    

        HashSet hashSet = new HashSet();
        hashSet.add("java");//到此位置,第1次add分析完毕.
        hashSet.add("php");//到此位置,第2次add分析完毕
        hashSet.add("java");
        System.out.println("set=" + hashSet);

        /*
        对HashSet 的源码解读
        1. 执行 HashSet()
            public HashSet() {
                map = new HashMap<>();
            }
        2. 执行 add()
           public boolean add(E e) {//e = "java"
                return map.put(e, PRESENT)==null;// (static) PRESENT = new Object();
           }
         3.执行 put() , 该方法会执行 hash(key) 得到key对应的hash值 算法:h = key.hashCode()) ^ (h >>> 16) 可见这个hash的值并不是hashcode,而是做了一定的处理 >>> 16.
             public V put(K key, V value) {//key = "java" value = PRESENT 共享
                return putVal(hash(key), key, value, false, true);
            }
         4.执行 putVal !!!!!!!!!!!!!!!!!
         final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量
                //table 就是 HashMap 的一个数组,类型是 Node[] (前面我们也模拟过)
                //if 语句表示如果当前table 是null, 或者 大小=0
                //就是第一次扩容,到16个空间.
                if ((tab = table) == null || (n = tab.length) == 0)
                    n = (tab = resize()).length;

                //(1)根据key,得到hash 去计算该key应该存放到table表的哪个索引位置并把这个位置的对象,赋给 p
                //(2)判断 p 是否为null
                //(2.1) 如果 p 为null, 表示还没有存放元素, 就创建一个 Node (key="java",value=PRESENT)
                //(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)
                if ((p = tab[i = (n - 1) & hash]) == null)
                    tab[i] = newNode(hash, key, value, null); // 这个null类似与模拟节点的null,其后面还没有挂载节点
                else {
                    //一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建
                    Node<K,V> e; K k; //
                    //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
                    //并且满足 下面两个条件之一:
                    //  (1) 准备加入的key 和 p 指向的Node 结点的 key 是同一个对象
                    //  (2)  p 指向的Node 结点的 key 的equals() 和准备加入的key比较后相同
                    //就不能加入
                    if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                        e = p;
                    //再判断 p 是不是一颗红黑树,
                    //如果是一颗红黑树,就调用 putTreeVal , 来进行添加
                    else if (p instanceof TreeNode)
                        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                    else {//如果table对应索引位置,已经是一个链表, 就使用for循环比较
                          //(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后
                          //    注意在把元素添加到链表后,立即判断 该链表是否已经达到8个结点
                          //    , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
                          //    注意,在转成红黑树时,要进行判断, 判断条件
                          //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                          //            resize();
                          //    如果上面条件成立,先table扩容.
                          //    只有上面条件不成立时,才进行转成红黑树
                          //(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break

                        for (int binCount = 0; ; ++binCount) {
                            if ((e = p.next) == null) {
                                p.next = newNode(hash, key, value, null);
                                if (binCount >= TREEIFY_THRESHOLD(8) - 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;
                //size 就是我们每加入一个结点Node(k,v,h,next), size++
                if (++size > threshold)
                    resize();//扩容
                afterNodeInsertion(evict);
                return null;
            }
         */

    }
}
  1. The bottom layer of HashSet is HashMap. When it is added for the first time, the table array is expanded to 16, the threshold (threshold) is 16* and the load factor (loadFactor) is 0.75 = 12
  2. If the table array uses a critical value of 12, it will be expanded to 16*2=32, and the new critical value is 32*0.75=24. And so on

example test

package com.hspedu.set_;

import java.util.HashSet;
import java.util.Objects;

@SuppressWarnings({
    
    "all"})
public class HashSetIncrement {
    
    
    public static void main(String[] args) {
    
    
        /*
        HashSet底层是HashMap, 第一次添加时,table 数组扩容到 16,
        临界值(threshold)是 16*加载因子(loadFactor)是0.75 = 12
        如果table 数组使用到了临界值 12,就会扩容到 16 * 2 = 32,
        新的临界值就是 32*0.75 = 24, 依次类推

         */
        HashSet hashSet = new HashSet();
//        for(int i = 1; i <= 100; i++) {
    
    
//            hashSet.add(i);//1,2,3,4,5...100
//        }
        /*
        在Java8中, 如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是 8 ),
        并且table的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树),
        否则仍然采用数组扩容机制

         */

//        for(int i = 1; i <= 12; i++) {
    
    
//            hashSet.add(new A(i));//
//        }


        /*
            当我们向hashset增加一个元素,-> Node -> 加入table , 就算是增加了一个size++

         */

        for(int i = 1; i <= 7; i++) {
    
    //在table的某一条链表上添加了 7个A对象
            hashSet.add(new A(i));//
        }

        for(int i = 1; i <= 7; i++) {
    
    //在table的另外一条链表上添加了 7个B对象
            hashSet.add(new B(i));//
        }



    }
}

class B {
    
    
    private int n;

    public B(int n) {
    
    
        this.n = n;
    }
    @Override
    public int hashCode() {
    
    
        return 200;
    }
}

class A {
    
    
    private int n;

    public A(int n) {
    
    
        this.n = n;
    }
    @Override
    public int hashCode() {
    
    
        return 100;
    }
}

HashSet Class Exercise 1

Define an Employee class that contains: private member attribute name.age

Require:

1. Create 3 Employee objects and put them into HashSet

2. When the values ​​of name and age are the same, it is considered to be the same employee and cannot be added to the HashSet collection

package com.hspedu.set_;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

@SuppressWarnings({
    
    "all"})
public class HashSetExercise {
    
    
    public static void main(String[] args) {
    
    


        /**
         定义一个Employee类,该类包含:private成员属性name,age 要求:
         创建3个Employee 对象放入 HashSet中
         当 name和age的值相同时,认为是相同员工, 不能添加到HashSet集合中

         */
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("milan", 18));//ok
        hashSet.add(new Employee("smith", 28));//ok
        hashSet.add(new Employee("milan", 18));//加入不成功.

        //回答,加入了几个? 3个
        System.out.println("hashSet=" + hashSet);
    }
}

//创建Employee
class Employee {
    
    
    private String name;
    private int age;

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

    public String getName() {
    
    
        return name;
    }

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

    public int getAge() {
    
    
        return age;
    }

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

    public void setAge(int age) {
    
    
        this.age = age;
    }
    //如果name 和 age 值相同,则返回相同的hash值

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

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

Set interface implementation class-LinkedHashSet

Comprehensive description of LinkedHashSet

  1. LinkedHashSet is a subclass of HashSet
  2. The bottom layer of LinkedHashSet is a LinkedHashMap, which maintains an array + doubly linked list
  3. LinkedHashSet determines the storage location of the element according to the hashCode value of the element, and uses
    the linked list to maintain the order of the elements (graph), which makes the elements seem to be saved in the order of insertion.
  4. LinkedHashSet does not allow adding duplicate elements

package com.hspedu.set_;

import java.util.LinkedHashSet;
import java.util.Set;

@SuppressWarnings({
    
    "all"})
public class LinkedHashSetSource {
    
    
    public static void main(String[] args) {
    
    
        //分析一下LinkedHashSet的底层机制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("刘", 1001));
        set.add(123);
        set.add("HSP");

        System.out.println("set=" + set);
        //1. LinkedHashSet 加入顺序和取出元素/数据的顺序一致
        //2. LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
        //3. LinkedHashSet 底层结构 (数组table+双向链表)
        //4. 添加第一次时,直接将 数组table 扩容到 16 ,存放的结点类型是 LinkedHashMap$Entry
        //5. 数组是 HashMap$Node[] 存放的元素/数据是 LinkedHashMap$Entry类型
        /*
                //继承关系是在内部类完成.
                static class Entry<K,V> extends HashMap.Node<K,V> {
                    Entry<K,V> before, after;
                    Entry(int hash, K key, V value, Node<K,V> next) {
                        super(hash, key, value, next);
                    }
                }

         */

    }
}
class Customer {
    
    
    private String name;
    private int no;

    public Customer(String name, int no) {
    
    
        this.name = name;
        this.no = no;
    }
}

illustrate

  1. A hash table and a doubly linked list are maintained in LinkedHastSet (LinkedHashSet has head and tail)

  2. Each node has before and after attributes, which can form a doubly linked list

  3. When adding an element, first find the hash value, then find the index, determine the position of the element in the table, and then add the added element to the doubly linked list (if it already exists, do not add it [the principle is the same as hashset])

    tail.next = newElement //示意代码
    newElement.pre = tail
    tail = newEelment;
    
  4. In this way, we can also ensure that the insertion order and the traversal order are consistent when traversing the LinkedHashSet

LinkedHashSet practice questions

Car class (attribute: name, price), if the name and price are the same, it is considered to be the same element and cannot be added.

package com.hspedu.set_;

import java.util.LinkedHashSet;
import java.util.Objects;

@SuppressWarnings({
    
    "all"})
public class LinkedHashSetExercise {
    
    
    public static void main(String[] args) {
    
    

        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥拓", 1000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//OK
        linkedHashSet.add(new Car("法拉利", 10000000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//加入不了
        linkedHashSet.add(new Car("保时捷", 70000000));//OK
        linkedHashSet.add(new Car("奥迪", 300000));//加入不了

        System.out.println("linkedHashSet=" + linkedHashSet);

    }
}

/**
 * Car 类(属性:name,price),  如果 name 和 price 一样,
 * 则认为是相同元素,就不能添加。 5min
 */

class Car {
    
    
    private String name;
    private double price;

    public Car(String name, double price) {
    
    
        this.name = name;
        this.price = price;
    }

    public String getName() {
    
    
        return name;
    }

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

    public double getPrice() {
    
    
        return price;
    }

    public void setPrice(double price) {
    
    
        this.price = price;
    }

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

    //重写equals 方法 和 hashCode
    //当 name 和 price 相同时, 就返回相同的 hashCode 值, equals返回t

    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 &&
                Objects.equals(name, car.name);
    }

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

Map interface and common methods

Features of the Map interface implementation class [practical]

Note: This is about the characteristics of the Map interface of JDK8

  1. Map and Collection exist side by side. Used to save data with a mapping relationship: Key-Value
  2. The key and value in the Map can be any reference type of data, which will be encapsulated into the HashMap$Node object.
  3. The key in the Map is not allowed to be repeated, the reason is the same as that of the HashSet, the source code has been analyzed earlier.
  4. The value in the Map can be repeated.
  5. The key of the Map can be null, and the value can also be null. Note that if the key is null, there can only be one, and if the value is null, there can be multiple:.
  6. The String class is often used as the key of the Map
  7. There is a one-way one-to-one relationship between key and value, that is, the corresponding value can always be found through the specified key
package com.hspedu.map_;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings({
    
    "all"})
public class Map_ {
    
    
    public static void main(String[] args) {
    
    
        //Map 接口实现类的特点, 使用实现类HashMap
        //1. Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
        //2. Map 中的 key 和  value 可以是任何引用类型的数据,会封装到HashMap$Node 对象中
        //3. Map 中的 key 不允许重复,原因和HashSet 一样,前面分析过源码.
        //4. Map 中的 value 可以重复
        //5. Map 的key 可以为 null, value 也可以为null ,注意 key 为null,
        //   只能有一个,value 为null ,可以多个
        //6. 常用String类作为Map的 key
        //7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
        Map map = new HashMap();
        map.put("no1", "韩顺平");//k-v
        map.put("no2", "张无忌");//k-v
        map.put("no1", "张三丰");//当有相同的k , 就等价于替换.
        map.put("no3", "张三丰");//k-v
        map.put(null, null); //k-v
        map.put(null, "abc"); //等价替换
        map.put("no4", null); //k-v
        map.put("no5", null); //k-v
        map.put(1, "赵敏");//k-v
        map.put(new Object(), "金毛狮王");//k-v
        // 通过get 方法,传入 key ,会返回对应的value
        System.out.println(map.get("no2"));//张无忌

        System.out.println("map=" + map);
    }
}
  1. The key-value schematic diagram of Map storing data, a pair of kv is placed HashMap$Nodein one, because Node implements the Entry interface, some books also say that a pair of kv is an Entry (as shown in the figure). But in essence, Entry still points to the elements in HashMap$Node, not a copy storage.

package com.hspedu.map_;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


@SuppressWarnings({
    
    "all"})
public class MapSource_ {
    
    
    public static void main(String[] args) {
    
    
        Map map = new HashMap();
        map.put("no1", "韩顺平");//k-v
        map.put("no2", "张无忌");//k-v
        map.put(new Car(), new Person());//k-v

        //老韩解读
        //1. k-v 最后是 HashMap$Node node = newNode(hash, key, value, null)
        //2. k-v 为了方便程序员的遍历,还会 创建 EntrySet 集合 ,该集合存放的元素的类型 Entry, 而一个Entry
        //   对象就有k,v EntrySet<Entry<K,V>> 即: transient Set<Map.Entry<K,V>> entrySet;
        //3. entrySet 中, 定义的类型是 Map.Entry ,但是实际上存放的还是 HashMap$Node
        //   这时因为 static class Node<K,V> implements Map.Entry<K,V>
        //4. 当把 HashMap$Node 对象 存放到 entrySet 就方便我们的遍历, 因为 Map.Entry 提供了重要方法
        //   K getKey(); V getValue();

        Set set = map.entrySet();
        System.out.println(set.getClass());// HashMap$EntrySet
        for (Object obj : set) {
    
    

            //System.out.println(obj.getClass()); //HashMap$Node
            //为了从 HashMap$Node 取出k-v
            //1. 先做一个向下转型
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "-" + entry.getValue() );
        }

        Set set1 = map.keySet();
        System.out.println(set1.getClass());
        Collection values = map.values();
        System.out.println(values.getClass());


    }
}

class Car {
    
    

}

class Person{
    
    

}

Common methods of the Map interface

package com.hspedu.map_;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings({
    
    "all"})
public class MapMethod {
    
    
    public static void main(String[] args) {
    
    
        //演示map接口常用方法

        Map map = new HashMap();
        map.put("fh", new Book("", 100));//OK
        map.put("fh", "bnd");//替换-> 一会分析源码
        map.put("fgd", "mr");//OK
        map.put("sgfh", "mr");//OK
        map.put("vcbhhd", null);//OK
        map.put(null, "dfhdfg");//OK
        map.put("lh", "gct");//OK
        map.put("hsp", "hspd");

        System.out.println("map=" + map);

//        remove:根据键删除映射关系
        map.remove(null);
        System.out.println("map=" + map);
//        get:根据键获取值
        Object val = map.get("lh");
        System.out.println("val=" + val);
//        size:获取元素个数
        System.out.println("k-v=" + map.size());
//        isEmpty:判断个数是否为0
        System.out.println(map.isEmpty());//F
//        clear:清除k-v
        //map.clear();
        System.out.println("map=" + map);
//        containsKey:查找键是否存在
        System.out.println("结果=" + map.containsKey("hsp"));//T


    }
}

class Book {
    
    
    private String name;
    private int num;

    public Book(String name, int num) {
    
    
        this.name = name;
        this.num = num;
    }
}

Map interface traversal method

Schematic diagram of Map traversal (more complicated than List, and Set but the same basic principle)

  1. containsKey: Find whether the key exists
  2. keySet: get all the keys
  3. entrySet: Get all relationship kv
  4. values: get all the values
package com.hspedu.map_;

import java.util.*;

@SuppressWarnings({
    
    "all"})
public class MapFor {
    
    
    public static void main(String[] args) {
    
    

        Map map = new HashMap();
        map.put("邓超", "孙俪");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘令博", null);
        map.put(null, "刘亦菲");
        map.put("鹿晗", "关晓彤");

        //第一组: 先取出 所有的Key , 通过Key 取出对应的Value
        Set keyset = map.keySet();
        //(1) 增强for
        System.out.println("-----第一种方式-------");
        for (Object key : keyset) {
    
    
            System.out.println(key + "-" + map.get(key));
        }
        //(2) 迭代器
        System.out.println("----第二种方式--------");
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
    
    
            Object key =  iterator.next();
            System.out.println(key + "-" + map.get(key));
        }

        //第二组: 把所有的values取出
        Collection values = map.values();
        //这里可以使用所有的Collections使用的遍历方法
        //(1) 增强for
        System.out.println("---取出所有的value 增强for----");
        for (Object value : values) {
    
    
            System.out.println(value);
        }
        //(2) 迭代器
        System.out.println("---取出所有的value 迭代器----");
        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
    
    
            Object value =  iterator2.next();
            System.out.println(value);

        }

        //第三组: 通过EntrySet 来获取 k-v
        Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
        //(1) 增强for
        System.out.println("----使用EntrySet 的 for增强(第3种)----");
        for (Object entry : entrySet) {
    
    
            //将entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2) 迭代器
        System.out.println("----使用EntrySet 的 迭代器(第4种)----");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()) {
    
    
            Object entry =  iterator3.next();
            //System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
            //向下转型 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}

Map interface class exercises

Use HashMap to add 3 employee objects, requiring

key: employee id

Value: Employee object

And traverse the employees whose salary is >18000 (there are at least two ways to traverse)

Employee class: name, salary, employee id

package com.hspedu.map_;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({
    
    "all"})
public class MapExercise {
    
    
    public static void main(String[] args) {
    
    
        //完成代码
        Map hashMap = new HashMap();
        //添加对象
        hashMap.put(1, new Emp("jack", 300000, 1));
        hashMap.put(2, new Emp("tom", 21000, 2));
        hashMap.put(3, new Emp("milan", 12000, 3));


        //遍历2种方式
        //并遍历显示工资>18000的员工(遍历方式最少两种)
        //1. 使用keySet  -> 增强for
        Set keySet = hashMap.keySet();
        System.out.println("====第一种遍历方式====");
        for (Object key : keySet) {
    
    
            //先获取value
            Emp emp = (Emp) hashMap.get(key);
            if(emp.getSal() >18000) {
    
    
                System.out.println(emp);
            }
        }

        //2. 使用EntrySet -> 迭代器
        Set entrySet = hashMap.entrySet();
        System.out.println("======迭代器======");
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
    
    
            // 真正的运行类型是Node,可以getClass看一下
            Map.Entry entry =  (Map.Entry)iterator.next();
            //通过entry 取得key 和 value
            Emp emp = (Emp) entry.getValue();
            if(emp.getSal() > 18000) {
    
    
                System.out.println(emp);
            }
        }
    }
}

/**
 * 使用HashMap添加3个员工对象,要求
 * 键:员工id
 * 值:员工对象
 * 并遍历显示工资>18000的员工(遍历方式最少两种)
 * 员工类:姓名、工资、员工id
 */
class Emp {
    
    
    private String name;
    private double sal;
    private int id;

    public Emp(String name, double sal, int id) {
    
    
        this.name = name;
        this.sal = sal;
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

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

    public double getSal() {
    
    
        return sal;
    }

    public void setSal(double sal) {
    
    
        this.sal = sal;
    }

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

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

Map interface implementation class-HashMap

HashMap summary

  1. Common implementation classes of the Map interface: HashMap, Hashtable and Properties.
  2. HashMap is the most frequently used implementation class of the Map interface.
  3. HashMap stores data in the form of key-val pairs (HashMap$Node type) [Case Entry]
  4. The key cannot be repeated, but the value can be repeated, and null keys and null values ​​are allowed.
  5. If the same key is added, the original key-val will be overwritten, which is equivalent to modification. (key will not be replaced, val will be replaced)
  6. Like HashSet, the order of mapping is not guaranteed, because the underlying layer is stored in the form of a hash table. (Jdk8's hashMap underlying array + linked list + red-black tree)
  7. HashMap does not implement synchronization, so it is not thread-safe, the method does not perform synchronization and mutual exclusion operations, and is not synchronized

HashMap underlying mechanism and source code analysis

insert image description here

  1. The bottom layer of HashMap maintains an array table of Node type, which is null by default
  2. When creating the object, initialize the loading factor (Ioadfactor) to 0.75.
  3. When adding key-val, the index in the table is obtained through the hash value of the key. Then judge whether there is an element at the index, and if there is no element, add it directly. If there is an element at the index, continue to judge whether the key of the element is equal to the key to be added. If they are equal, replace val directly; if they are not equal, you need to judge whether it is a tree structure or a linked list structure, and make corresponding processing. If it is found that the capacity is not enough when adding, it needs to be expanded.
  4. For the first addition, you need to expand the table capacity to 16, and the threshold (threshold) is 12 (16*0.75)
  5. To expand the capacity in the future, it is necessary to expand the capacity of the table to twice the original capacity (32), and the critical value is twice the original, that is, 24, and so on.
  6. In Java8, if the number of elements in a linked list exceeds TREEIFY_THRESHOLD (default is 8), and the size of the table is >= MIN TREEIFY CAPACITY (default 64), it will be treed (red-black tree)
package com.hspedu.map_;

import java.util.HashMap;

@SuppressWarnings({
    
    "all"})
public class HashMapSource1 {
    
    
    public static void main(String[] args) {
    
    
        HashMap map = new HashMap();
        map.put("java", 10);//ok
        map.put("php", 10);//ok
        map.put("java", 20);//替换value

        System.out.println("map=" + map);//

        /*解读HashMap的源码+图解
        1. 执行构造器 new HashMap()
           初始化加载因子 loadfactor = 0.75
           HashMap$Node[] table = null
        2. 执行put 调用 hash方法,计算 key的 hash值 (h = key.hashCode()) ^ (h >>> 16)
            public V put(K key, V value) {//K = "java" value = 10
                return putVal(hash(key), key, value, false, true);
            }
         3. 执行 putVal
         final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
                //如果底层的table 数组为null, 或者 length =0 , 就扩容到16
                if ((tab = table) == null || (n = tab.length) == 0)
                    n = (tab = resize()).length;
                //取出hash值对应的table的索引位置的Node, 如果为null, 就直接把加入的k-v
                //, 创建成一个 Node ,加入该位置即可
                if ((p = tab[i = (n - 1) & hash]) == null)
                    tab[i] = newNode(hash, key, value, null);
                else {
                    Node<K,V> e; K k;//辅助变量
                // 如果table的索引位置的key的hash相同和新的key的hash值相同,
                 // 并 满足(table现有的结点的key和准备添加的key是同一个对象  || equals返回真)
                 // 就认为不能加入新的k-v
                    if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                        e = p;
                    else if (p instanceof TreeNode)//如果当前的table的已有的Node 是红黑树,就按照红黑树的方式处理
                        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);
                                //加入后,判断当前链表的个数,是否已经到8个,到8个,后
                                //就调用 treeifyBin 方法进行红黑树的转换
                                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                    treeifyBin(tab, hash);
                                break;
                            }
                            if (e.hash == hash && //如果在循环比较过程中,发现有相同,就break,就只是替换value
                                ((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; //替换,key对应value
                        afterNodeAccess(e);
                        return oldValue;
                    }
                }
                ++modCount;//每增加一个Node ,就size++
                if (++size > threshold[12-24-48])//如size > 临界值,就扩容
                    resize();
                afterNodeInsertion(evict);
                return null;
            }

              5. 关于树化(转成红黑树)
              //如果table 为null ,或者大小还没有到 64,暂时不树化,而是进行扩容.
              //否则才会真正的树化 -> 剪枝
              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();

            }
         */


    }
}

Simulate HashMap triggering expansion, treeing, and Debug verification.

package com.hspedu.map_;

import java.util.HashMap;
import java.util.Objects;

@SuppressWarnings({
    
    "all"})
public class HashMapSource2 {
    
    
    public static void main(String[] args) {
    
    


        HashMap hashMap = new HashMap();
        for(int i = 1; i <= 12; i++) {
    
    
            hashMap.put(i, "hello");
        }

        hashMap.put("aaa", "bbb");

        System.out.println("hashMap=" + hashMap);//12个 k-v

        //布置一个任务,自己设计代码去验证,table 的扩容
        //0 -> 16(12) -> 32(24) -> 64(64*0.75=48)-> 128 (96) ->
        //自己设计程序,验证-》 增强自己阅读源码能力. 看别人代码.
    }
}

class A  {
    
    
    private int num;

    public A(int num) {
    
    
        this.num = num;
    }

    //所有的A对象的hashCode都是100
//    @Override
//    public int hashCode() {
    
    
//        return 100;
//    }

    @Override
    public String toString() {
    
    
        return "\nA{" +
                "num=" + num +
                '}';
    }
}

Map interface implementation class-Hashtable

Basic introduction to HashTable

  1. The stored elements are key-value pairs: ie KV

  2. Neither the key nor the value of the hashtable can be null, otherwise a NullPointerException will be thrown

  3. The use of hashTable is basically the same as that of HashMap

  4. hashTable is thread-safe (synchronized), hashMap is thread-unsafe

package com.hspedu.map_;

import java.util.Hashtable;

@SuppressWarnings({
    
    "all"})
public class HashTableExercise {
    
    
    public static void main(String[] args) {
    
    
        Hashtable table = new Hashtable();//ok
        table.put("john", 100); //ok
        //table.put(null, 100); //异常 NullPointerException
        //table.put("john", null);//异常 NullPointerException
        table.put("lucy", 100);//ok
        table.put("lic", 100);//ok
        table.put("lic", 88);//替换
        table.put("hello1", 1);
        table.put("hello2", 1);
        table.put("hello3", 1);
        table.put("hello4", 1);
        table.put("hello5", 1);
        table.put("hello6", 1);
        System.out.println(table);

        //简单说明一下Hashtable的底层
        //1. 底层有数组 Hashtable$Entry[] 初始化大小为 11
        //2. 临界值 threshold 8 = 11 * 0.75
        //3. 扩容: 
        //4. 执行 方法 addEntry(hash, key, value, index); 添加K-V 封装到Entry
        //5. 当 if (count >= threshold) 满足时,就进行扩容
        //5. 按照 int newCapacity = (oldCapacity << 1) + 1; 的大小扩容. 两倍加一
    }
}

Comparison between Hashtable and HashMap

Map interface implementation class-Properties

basic introduction

  1. The Properties class inherits from the Hashtable class and implements the Map interface , and also uses a key-value pair to store data.
  2. His usage characteristics are similar to Hashtable.
  3. Properties can also be used to load data from the xxx.properties file to the Properties class object, and read and modify it.
  4. Explanation: After work, the xxx.properties file is usually used as a configuration file. This knowledge point is an example of IO flow. If you are interested, you can read the article first. cnblogs.com/xudong-bupt/p/3758136.html

basic use

package com.hspedu.map_;

import java.util.Properties;

@SuppressWarnings({
    
    "all"})
public class Properties_ {
    
    
    public static void main(String[] args) {
    
    

        //1. Properties 继承  Hashtable
        //2. 可以通过 k-v 存放数据,当然key 和 value 不能为 null
        //增加
        Properties properties = new Properties();
        //properties.put(null, "abc");//抛出 空指针异常
        //properties.put("abc", null); //抛出 空指针异常
        properties.put("john", 100);//k-v
        properties.put("lucy", 100);
        properties.put("lic", 100);
        properties.put("lic", 88);//如果有相同的key , value被替换

        System.out.println("properties=" + properties);

        //通过k 获取对应值
        System.out.println(properties.get("lic"));//88
        System.out.println(properties.getProperty("lic"));

        //删除
        properties.remove("lic");
        System.out.println("properties=" + properties);

        //修改
        properties.put("john", "约翰");
        System.out.println("properties=" + properties);
    }
}

Summary - how to choose a collection implementation class in development!

In development, the collection implementation class to choose mainly depends on the characteristics of business operations, and then the selection is made according to the characteristics of the collection implementation class. The analysis is as follows:

  1. First determine the type of storage (a set of objects [single column] or a set of key-value pairs [double column])

  2. A set of objects [single column]:Collection interface
    allows repetition:List

    • More additions and deletions: LinkedList [the bottom layer maintains a doubly linked list]
    • More changes and checks: ArrayList [the bottom layer maintains a variable array of Object type]

    No duplicates allowed: Set

    • Unordered: HashSet[The bottom layer is HashMap, which maintains a hash table (array + linked list + red-black tree)
    • Sorting: TreeSet
    • The order of insertion and removal is the same: LinkedHashSet. Maintenance array + doubly linked list
  3. A set of key-value pairs [double columns]:Map

    • The keys are unordered; HashMap [the bottom layer is; hash table jdk7: array + linked list, jdk8: array + linked list + red and black
    • Key sorting: TreeMap
    • Key insertion and retrieval are in the same order: LinkedHashMap
    • Read file Properties

TreeSet

package com.hspedu.set_;

import java.util.Comparator;
import java.util.TreeSet;
@SuppressWarnings({
    
    "all"})
public class TreeSet_ {
    
    
    public static void main(String[] args) {
    
    

        //1. 当我们使用无参构造器,创建TreeSet时,仍然是无序的
        //2. 希望添加的元素,按照字符串大小来排序
        //3. 使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)并指定排序规则
        //4. 简单看看源码
        /*
        1. 构造器把传入的比较器对象,赋给了 TreeSet的底层的 TreeMap 的属性 this.comparator

         public TreeMap(Comparator<? super K> comparator) {
                this.comparator = comparator;
            }
         2. 在 调用 treeSet.add("tom"), 在底层会执行到

             if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
                do {
                    parent = t;
                    //动态绑定到我们的匿名内部类(对象)compare
                    cmp = cpr.compare(key, t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else //如果相等,即返回0,这个Key就没有加入
                        return t.setValue(value);
                } while (t != null);
            }
         */

//        TreeSet treeSet = new TreeSet();
        TreeSet treeSet = new TreeSet(new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                //下面 调用String的 compareTo方法进行字符串大小比较
                //return ((String) o2).compareTo((String) o1);
                //如果要求加入的元素,按照长度大小排序
                return ((String) o1).length() - ((String) o2).length();
            }
        });
        //添加数据.
        treeSet.add("jack");
        treeSet.add("tom");// 3
        treeSet.add("sp");
        treeSet.add("a");
        treeSet.add("abc");// 长度为3,加不进去
        System.out.println("treeSet=" + treeSet);
    }
}

TreeMap

package com.hspedu.map_;

import java.util.Comparator;
import java.util.TreeMap;

@SuppressWarnings({
    
    "all"})
public class TreeMap_ {
    
    
    public static void main(String[] args) {
    
    

        //使用默认的构造器,创建TreeMap, 是无序的(也没有排序)
        /*
            要求:按照传入的 k(String) 的大小进行排序
         */
//        TreeMap treeMap = new TreeMap();
        TreeMap treeMap = new TreeMap(new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                //按照传入的 k(String) 的大小进行排序
                //按照K(String) 的长度大小排序
                //return ((String) o2).compareTo((String) o1);
                return ((String) o2).length() - ((String) o1).length();
            }
        });
        treeMap.put("jack", "杰克");
        treeMap.put("tom", "汤姆");
        treeMap.put("kristina", "克瑞斯提诺");
        treeMap.put("smith", "斯密斯");
        treeMap.put("hsp", "韩顺平");//加入不了

        System.out.println("treemap=" + treeMap);

        /*
            解读源码:
            1. 构造器. 把传入的实现了 Comparator接口的匿名内部类(对象),传给给TreeMap的comparator
             public TreeMap(Comparator<? super K> comparator) {
                this.comparator = comparator;
            }
            2. 调用put方法
            2.1 第一次添加, 把k-v 封装到 Entry对象,放入root
            Entry<K,V> t = root;
            if (t == null) {
                compare(key, key); // type (and possibly null) check

                root = new Entry<>(key, value, null);
                size = 1;
                modCount++;
                return null;
            }
            2.2 以后添加
            Comparator<? super K> cpr = comparator;
            if (cpr != null) {
                do { //遍历所有的key , 给当前key找到适当位置
                    parent = t;
                    cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的compare
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else  //如果遍历过程中,发现准备添加Key 和当前已有的Key 相等,就不添加
                        return t.setValue(value);
                } while (t != null);
            }
         */

    }
}

Collections tool class

Collections tool class introduction

  1. Collections is a tool class for manipulating collections such as Set, List and Map.
  2. Collections provides a series of static methods to sort, query and modify collection elements.

Sorting operations (both static methods)

  1. reverse(List): Reverse the order of the elements in the List
  2. shuffle(List): Randomly sort the elements of the List collection
  3. sort(List): sorts the elements of the specified List collection in ascending order according to the natural order of the elements
  4. sort(List, Comparator): Sort the List collection elements according to the order generated by the specified Comparator
  5. swap(List, int, int): exchange the element at i and the element at j in the specified list collection

find, replace

  1. Object max(Collection): Returns the largest element in the given collection according to the natural order of the elements.

  2. Object max(Collection,Comparator): Returns the largest element in the given collection , in the order specified by the Comparator .
  3. Object min(Collection)
  4. Object min(Collection,Comparator)
  5. int frequency(Collection, Object): Returns the number of occurrences of the specified element in the specified collection.
  6. void copy(List dest, List src): Copy the content in src to dest.
  7. boolean replaceAll(List list, Object oldVal, Object newVal): Replaces
    all old values ​​of the List object with new values.
package com.hspedu.collections_;

import java.util.*;

@SuppressWarnings({
    
    "all"})
public class Collections_ {
    
    
    public static void main(String[] args) {
    
    

        //创建ArrayList 集合,用于测试.
        List list = new ArrayList();
        list.add("tom");
        list.add("smith");
        list.add("king");
        list.add("milan");
        list.add("tom");


//        reverse(List):反转 List 中元素的顺序
        Collections.reverse(list);
        System.out.println("list=" + list);
//        shuffle(List):对 List 集合元素进行随机排序
//        for (int i = 0; i < 5; i++) {
    
    
//            Collections.shuffle(list);
//            System.out.println("list=" + list);
//        }

//        sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
        Collections.sort(list);
        System.out.println("自然排序后");
        System.out.println("list=" + list);
//        sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
        //我们希望按照 字符串的长度大小排序
        Collections.sort(list, new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                //可以加入校验代码.
                return ((String) o2).length() - ((String) o1).length();
            }
        });
        System.out.println("字符串长度大小排序=" + list);
//        swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

        //比如
        Collections.swap(list, 0, 1);
        System.out.println("交换后的情况");
        System.out.println("list=" + list);

        //Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println("自然顺序最大元素=" + Collections.max(list));
        //Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
        //比如,我们要返回长度最大的元素
        Object maxObject = Collections.max(list, new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最大的元素=" + maxObject);


        //Object min(Collection)
        //Object min(Collection,Comparator)
        //上面的两个方法,参考max即可

        //int frequency(Collection,Object):返回指定集合中指定元素的出现次数
        System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));

        //void copy(List dest,List src):将src中的内容复制到dest中

        ArrayList dest = new ArrayList();
        //为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样
        for(int i = 0; i < list.size(); i++) {
    
    
            dest.add("");
        }
        //拷贝
        Collections.copy(dest, list);
        System.out.println("dest=" + dest);

        //boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
        //如果list中,有tom 就替换成 汤姆
        Collections.replaceAll(list, "tom", "汤姆");
        System.out.println("list替换后=" + list);
    }
}

Homework for this chapter

1. The programming question is implemented as required:
(1) Encapsulate a news class, including title and content attributes, provide get and set methods, rewrite the toString method, and only print the title when printing the object;

(2) Provide only one constructor with parameters. When instantiating an object, only initialize the title; and instantiate two objects: News 1: Over 10 million confirmed cases of the new crown, millions of Hindu believers went to the Ganges "holy bath" Arousing public concern
News 2: The man suddenly remembered that the fish he caught 2 months ago was still in the net pocket, he picked it up and released it quickly

(3) Add the news object to the ArrayList collection, and traverse in reverse order;

(4) In the process of traversing the collection, process the headlines of the news, and only keep the first 15 if it exceeds 15 characters, and then add "...."

(5) Print and traverse the processed news titles on the console;

package com.hspedu.homework;

import java.util.ArrayList;

@SuppressWarnings({
    
    "all"})
public class Homework01 {
    
    
    public static void main(String[] args) {
    
    
        ArrayList arrayList = new ArrayList();
        arrayList.add(new News("新冠确诊病例超千万,数百万印度教信徒赴恒河\"圣浴\"引民众担忧"));
        arrayList.add(new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生"));

        int size = arrayList.size();
        for (int i = size - 1; i >= 0; i--) {
    
    
            //System.out.println(arrayList.get(i));
            News news = (News)arrayList.get(i);
            System.out.println(processTitle(news.getTitle()));
        }

    }
    //专门写一个方法,处理现实新闻标题 process处理
    public static String processTitle(String title) {
    
    

        if(title == null) {
    
    
            return "";
        }

        if(title.length() > 15) {
    
    
            return title.substring(0, 15) + "..."; //[0,15)
        } else {
    
    
            return title;
        }

    }
}

/**
 * 按要求实现:
 * (1) 封装一个新闻类,包含标题和内容属性,提供get、set方法,重写toString方法,打印对象时只打印标题;
 * (2) 只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
 * 新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河“圣浴”引民众担忧
 * 新闻二:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生
 * (3) 将新闻对象添加到ArrayList集合中,并且进行倒序遍历;
 * (4) 在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加“…”
 * (5) 在控制台打印遍历出经过处理的新闻标题;
 */
class News {
    
    
    private String title;
    private String content;

    public News(String title) {
    
    
        this.title = title;
    }

    public String getTitle() {
    
    
        return title;
    }

    public void setTitle(String title) {
    
    
        this.title = title;
    }

    public String getContent() {
    
    
        return content;
    }

    public void setContent(String content) {
    
    
        this.content = content;
    }

    @Override
    public String toString() {
    
    
        return "News{" +
                "title='" + title + '\'' +
                '}';
    }
}

2. Programming questions

Use ArrayList to complete various operations on the object Car {name, price}

1.add: add a single element

2.remove: delete the specified element

3.contains: Find whether the element exists

4.size: Get the number of elements

5.isEmpty: judge whether it is empty 6.clear: empty

7.addAll: Add multiple elements

8.containsAl: Find whether multiple elements exist

9. removeAll: delete multiple elements

Use enhanced for and iterator to traverse all cars, you need to rewrite Car's toString method

package com.hspedu.homework;

import java.util.ArrayList;
import java.util.Iterator;

@SuppressWarnings({
    
    "all"})
public class Homework02 {
    
    
    public static void main(String[] args) {
    
    

        ArrayList arrayList = new ArrayList();
        Car car = new Car("宝马", 400000);
        Car car2 = new Car("宾利",5000000);
        //1.add:添加单个元素
        arrayList.add(car);
        arrayList.add(car2);
        System.out.println(arrayList);
        //* 2.remove:删除指定元素
        arrayList.remove(car);
        System.out.println(arrayList);
        //* 3.contains:查找元素是否存在
        System.out.println(arrayList.contains(car));//F
        //* 4.size:获取元素个数
        System.out.println(arrayList.size());//1
        //* 5.isEmpty:判断是否为空
        System.out.println(arrayList.isEmpty());//F
        //* 6.clear:清空
        //System.out.println(arrayList.clear(););
        //* 7.addAll:添加多个元素
        System.out.println(arrayList);
        arrayList.addAll(arrayList);//2个宾利
        System.out.println(arrayList);
        //* 8.containsAll:查找多个元素是否都存在
        arrayList.containsAll(arrayList);//T
        //* 9.removeAll:删除多个元素
        //arrayList.removeAll(arrayList); //相当于清空
        //* 使用增强for和 迭代器来遍历所有的car , 需要重写 Car 的toString方法

        for (Object o : arrayList) {
    
    
            System.out.println(o);//
        }
        System.out.println("===迭代器===");
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
    
    
            Object next =  iterator.next();
            System.out.println(next);

        }

    }
}
/**
 * 使用ArrayList 完成对 对象 Car {name, price} 的各种操作
 * 1.add:添加单个元素
 * 2.remove:删除指定元素
 * 3.contains:查找元素是否存在
 * 4.size:获取元素个数
 * 5.isEmpty:判断是否为空
 * 6.clear:清空
 * 7.addAll:添加多个元素
 * 8.containsAll:查找多个元素是否都存在
 * 9.removeAll:删除多个元素
 * 使用增强for和 迭代器来遍历所有的car , 需要重写 Car 的toString方法
 */
class Car {
    
    
    private String name;
    private double price;

    public Car(String name, double price) {
    
    
        this.name = name;
        this.price = price;
    }

    public String getName() {
    
    
        return name;
    }

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

    public double getPrice() {
    
    
        return price;
    }

    public void setPrice(double price) {
    
    
        this.price = price;
    }

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

3. For programming questions, complete the following tasks as required

  1. Use the HashMap class to instantiate a Map type object m, the key (String) and value (int) are used to store the employee's name and salary respectively, and the stored data is as follows: jack—650 yuan; tom—1200 yuan; smith—2900 Yuan;
  2. Change jack's salary to 2600 yuan
  3. A salary increase of 100 yuan for all employees;
  4. Iterate over all employees in the collection
  5. All over the sale and listening to some work advice
package com.hspedu.homework;

import java.util.*;

@SuppressWarnings({
    
    "all"})
public class Homework03 {
    
    
    public static void main(String[] args) {
    
    

        Map m = new HashMap();
        m.put("jack", 650);//int->Integer
        m.put("tom", 1200);//int->Integer
        m.put("smith", 2900);//int->Integer
        System.out.println(m);

        m.put("jack", 2600);//替换,更新
        System.out.println(m);

        //为所有员工工资加薪100元;
        //keySet
        Set keySet = m.keySet();
        for (Object key : keySet) {
    
    
            //更新
            m.put(key, (Integer)m.get(key) + 100);
        }
        System.out.println(m);

        System.out.println("=============遍历=============");
        //遍历 EntrySet
        Set entrySet = m.entrySet();
        //迭代器
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
    
    
            Map.Entry entry =  (Map.Entry)iterator.next();
            System.out.println(entry.getKey() + "-" + entry.getValue());

        }

        System.out.println("====遍历所有的工资====");
        Collection values = m.values();
        for (Object value : values) {
    
    
            System.out.println("工资=" + value);
        }

    }
}
/**
 * 按要求完成下列任务
 * 1)使用HashMap类实例化一个Map类型的对象m,键(String)和值(int)分别用于存储员工的姓名和工资,
 * 存入数据如下: jack—650元;tom—1200元;smith——2900元;
 * 2)将jack的工资更改为2600元
 * 3)为所有员工工资加薪100元;
 * 4)遍历集合中所有的员工
 * 5)遍历集合中所有的工资
 */

4. Short answer questions

Try to analyze how HashSet and TreeSet implement deduplication

(1) The deduplication mechanism of HashSet: hashCode() + equals(), the bottom layer first saves the object, performs calculations to obtain a hash value, and obtains the corresponding index through the hash value. If there is no data at the location of the table index, Just store it directly. If there is data, perform equals comparison [traversal comparison]. If it is not the same after comparison, add it, otherwise don't add it.

(2) TreeSet's deduplication mechanism: If you pass in a Comparator anonymous object, use the implemented compare to deduplicate. If the method returns 0, it will be considered the same element/data and will not be added. If you do not pass in For a Comparator anonymous object, the compareTo of the Compareable interface implemented by the object you added is used to deduplicate.

5. Code analysis questions

Will the following code throw an exception when running, and explain the reason from the source code level. [Investigate and read source code + interface programming + dynamic binding]

TreeSet treeSet = new TreeSet();
treeSet.add(new Person());
package com.hspedu.homework;
import java.util.TreeSet;

@SuppressWarnings({
    
    "all"})
public class Homework05 {
    
    
    public static void main(String[] args) {
    
    
        TreeSet treeSet = new TreeSet();
        //分析源码
        //add 方法,因为 TreeSet() 构造器没有传入Comparator接口的匿名内部类
        //所以在底层 Comparable<? super K> k = (Comparable<? super K>) key;
        //即 把 Perosn转成 Comparable类型
        treeSet.add(new Person());//ClassCastException.
        treeSet.add(new Person());//ClassCastException.
        System.out.println(treeSet);

    }
}
class Person implements Comparable{
    
    

    @Override
    public int compareTo(Object o) {
    
    
        return 0; // 重写,永远都是0,此时就只能加一个对象
    }
}

6. What is the output of the following code? This question is very interesting, if you don’t pay attention, you will fall into the trap.

Known: The Person class rewrites the hashCode and equals methods according to the id and name (if the id and name are the same, it is considered the same object), what is the output of the following code?

package com.hspedu.homework;

import java.util.HashSet;
import java.util.Objects;

@SuppressWarnings({
    
    "all"})
public class Homework06 {
    
    
    public static void main(String[] args) {
    
    
        HashSet set = new HashSet();//ok
        Person p1 = new Person(1001,"AA");//ok
        Person p2 = new Person(1002,"BB");//ok
        set.add(p1);//ok
        set.add(p2);//ok
        p1.name = "CC"; // 修改了原p1的name为CC
        set.remove(p1); // 可能删除失败,这里删除p1按照当前的1001和CC计算hash,那么这对应的位置与原p1不同
        System.out.println(set);// 2
        set.add(new Person(1001,"CC")); // 可以添加成功,添加到本来要删除的p1(实际删除失败)的位置
        System.out.println(set);// 3
        set.add(new Person(1001,"AA")); // 可以添加,因为原p1已经修改了
        System.out.println(set);// 4
    }
}

class Person {
    
    
    public String name;
    public int id;

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

    @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(name, id);
    }

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

7. Try to write the comparison between Vector and ArrayList?

Guess you like

Origin blog.csdn.net/m0_52316372/article/details/130461136