day20_ Generic, Set interface and common implementation classes

Posted by Ryoku on Sat, 04 Sep 2021 21:00:26 +0200

What is generics?

  • JDK1.5 designs the concept of generics. A generic type is a "type parameter", which represents an unknown generic type in the class, interface or method that declares it. It provides a compile time type safety detection mechanism. Generics can be used on methods, classes, and interfaces.

Benefits of generics

  • Advance the run-time problem to compile time
  • Casts are avoided

Generic definition format

  • < type >: specify a type format. Angle brackets can be written arbitrarily, usually only one letter. For example: < E > < T >
  • < type 1, type 2... >: specify mu lt iple types of formats separated by commas. For example: < e, t > < K, V >

Custom generic class

Syntax:

  Code example

package demo01;

// Custom generic class
public class Generic<T> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

Generic classes can be determined when creating objects

package demo01;

public class Test {
    public static void main(String[] args) {
        //The generic type is String
        Generic<String> g1 = new Generic<String>();
        g1.setT("Yang Mi");
        System.out.println(g1.getT());
        //The generic type is Integer
        Generic<Integer> g2 = new Generic<Integer>();
        g2.setT(30);
        System.out.println(g2.getT());
        //The generic type is Boolean
        Generic<Boolean> g3 = new Generic<Boolean>();
        g3.setT(true);
        System.out.println(g3.getT());
    }

}

Custom generic method

When declaring a method, we declare a type variable between the modifier and the return value type. We call a method that declares (or simply uses) a type variable as a generic method

Syntax:

  Sample code

 // Defining generic methods
    public static <T> ArrayList<T> addElement(ArrayList<T> list, T t1, T t2) {
        list.add(t1);
        list.add(t2);
        return list;
    }

Determine the type of the generic when the method is called

package demo01;

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        // When the method is called, the type of the generic type is determined
        ArrayList<String> strings = addElement(new ArrayList<String>(), "Zhang San", "Li Si");
        System.out.println(strings);//[Zhang San, Li Si]
    }

    // Defining generic methods
    public static <T> ArrayList<T> addElement(ArrayList<T> list, T t1, T t2) {
        list.add(t1);
        list.add(t2);
        return list;
    }
}

Custom generic interface

Syntax:

  Sample code

//generic interface 
public interface Generic<T> {
    void show(T t);
}

Case 1 of using interface generics: when defining an implementation class, define the same generics as the interface, and specify the specific types of generics when creating an implementation class object

//Implementation class without specifying a specific generic type
public class GenericImpl1<T> implements Generic<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

Case 2 of using interface generics: when defining implementation classes, directly specify the specific types of generics

 //Implementation class, specifying specific generics
public class GenericImpl2 implements Generic<Integer>{
     @Override
     public void show(Integer t) {
          System.out.println(t);
     }
}

Test class

public class GenericDemo3 {
    public static void main(String[] args) {
        GenericImpl1<String> g1 = new GenericImpl<String>();
        g1.show("Lin Qingxia");
        GenericImpl1<Integer> g2 = new GenericImpl<Integer>();
        g2.show(30);
      
        GenericImpl2 g3 = new GenericImpl2();
      	g3.show(10);
    }
}

Generic wildcard

When declaring a type variable, if you do not want this type variable to represent any reference data type, but a series of reference data types, we can use the type wildcard: <? >

  • Upper limit of type wildcard: <? Extensions type >
  • Type wildcard lower limit: <? Super type >

Sample code

public class GenericDemo4 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<Number> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();

        method(list1);
        method(list2);
        method(list3);
        method(list4);

        getElement1(list1);
        getElement1(list2);//report errors
        getElement1(list3);
        getElement1(list4);//report errors

        getElement2(list1);//report errors
        getElement2(list2);//report errors
        getElement2(list3);
        getElement2(list4);
    }
  
    // Generic wildcard: the generic type at this time?, Can be any type
    public static void method(ArrayList<?> list){}
    // Upper limit of generics: generics at this time?, Must be of type Number or a subclass of type Number
    public static void getElement1(ArrayList<? extends Number> list){}
    // Lower limit of generics: generics at this time?, Must be of type Number or a parent of type Number
    public static void getElement2(ArrayList<? super Number> list){}

}

Set set

The set interface is a sub interface of the Collection. The set interface does not provide additional methods. But it is more stringent than the Collection interface. The traversal mode supported by the set Collection is the same as that of the Collection collection: foreach and Iterator. Common implementation classes of set include HashSet and TreeSet.,

characteristic:

  • 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.
  • The elements in the Set interface are out of order, and the order in which they are stored is inconsistent with the order in which they are taken out.
  • Set collection has no method with index

Code example

public class MySet1 {
    public static void main(String[] args) {
      	//Create collection object
        Set<String> set = new TreeSet<>();
      	//Add element
        set.add("ccc");
        set.add("aaa");
        set.add("aaa");
        set.add("bbb");

//        for (int i = 0; i < set.size(); i++) {
//            //The Set collection has no index, so the method of getting elements by index cannot be used
//        }
      
      	//Traversal set
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------------------------");
        for (String s : set) {
            System.out.println(s);
        }
    }
}

Implementation class TreeSet of set set

Underlying structure: it maintains a TreeMap, which is based on red black tree! Characteristics of red black tree

  • Balanced binary B-tree
  • Each node can be red or black
  • The red black tree is not highly balanced. Its balance is realized through "its own red black rules"

characteristic:

  • Duplicate elements cannot be stored
  • No index
  • You can sort elements according to rules (NATURAL sorting or custom sorting).

Natural sort Comparable

If you try to add an object to a TreeSet, the object's class must implement the Comparable interface. The class implementing Comparable must implement the compareTo(Object obj) method. The size of the two objects is compared through the return value of the compareTo(Object obj) method. For the TreeSet set, the only criterion for judging whether two objects are equal is that the two objects are compared through the compareTo(Object obj) method, and the return value is 0.

Implementation steps:

  1. Create TreeSet set using null parameter Construction: use TreeSet set to store custom objects. The nonparametric construction method uses natural sorting to sort elements
  2. Custom classes implement the Comparable interface: natural sorting is to let the class to which the element belongs implement the Comparable interface and override the compareTo(T o) method
  3. Rewrite the compareTo method in the interface: when rewriting the method, be sure to note that the sorting rules must be written according to the required primary and secondary conditions

Case requirements

  • Store and traverse the student object, create a TreeSet collection, and use the parameterless construction method. Requirements: sort according to the age from small to large. If the age is the same, sort according to the alphabetical order of the name

Student class, code

package domain;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

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

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int compareTo(Student o) {
        //Sort by object's age
        //Main judgment conditions: sorted by age from small to large
        int result = this.age - o.age;
        //Secondary judgment condition: when the age is the same, sort according to the alphabetical order of the name
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}

Test class, code

package Demo02;

import domain.Student;

import java.util.TreeSet;

public class Demo02TreeSet {
    public static void main(String[] args) {
        //Create collection object
        TreeSet<Student> ts = new TreeSet<>();
        //Create student object
        Student s1 = new Student("zhangsan", 28);
        Student s2 = new Student("lisi", 27);
        Student s3 = new Student("wangwu", 29);
        Student s4 = new Student("zhaoliu", 28);
        Student s5 = new Student("qianqi", 30);
        //Add students to collection
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        //Traversal set
        for (Student student : ts) {
            System.out.println(student);
        }
    }
}

Custom sort Comparator

If the natural sorting rules of the elements placed in the TreeSet do not meet the current sorting requirements, or the type of the element does not implement the Comparable interface. When creating a TreeSet, you can specify a Comparator object separately

Implementation steps

  1. The user-defined objects are stored in the TreeSet collection. The construction method with parameters uses comparator sorting to sort the elements
  2. Comparator sorting is to let the collection construction method receive the implementation class object of comparator and rewrite the compare(T o1,T o2) method
  3. When rewriting a method, be sure to note that the collation must be written according to the required primary and secondary conditions

Case requirements

  • Store the teacher object and traverse it, create a TreeSet collection, and construct the method with parameters
  • Requirements: sort according to the age from small to large. If the age is the same, sort according to the alphabetical order of the name

Teacher class

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(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;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

Test class

public class MyTreeSet4 {
    public static void main(String[] args) {
      	//Create collection object
        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                //o1 represents the element to be stored now
                //o2 represents the element that has been stored in the collection
              
                //Main conditions
                int result = o1.getAge() - o2.getAge();
                //minor criteria 
                result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
		//Create teacher object
        Teacher t1 = new Teacher("zhangsan",23);
        Teacher t2 = new Teacher("lisi",22);
        Teacher t3 = new Teacher("wangwu",24);
        Teacher t4 = new Teacher("zhaoliu",24);
		//Add teacher to collection
        ts.add(t1);
        ts.add(t2);
        ts.add(t3);
        ts.add(t4);
		//Traversal set
        for (Teacher teacher : ts) {
            System.out.println(teacher);
        }
    }
}

Summary of two comparison methods

  • Natural sorting: the user-defined class implements the Comparable interface, rewrites the compareTo method, and sorts according to the return value
  • Comparator sorting: when creating a TreeSet object, pass the comparator implementation class object, override the compare method, and sort according to the return value
  • When using, natural sorting is used by default. When natural sorting does not meet the current requirements, comparator sorting must be used

Rules on return values in two ways

  • If the return value is negative, it means that the element currently stored is a small value, which is stored on the left
  • If the return value is 0, it means that the currently stored element is the same as the element in the collection and will not be saved
  • If the return value is a positive number, it means that the element currently stored is a large value, which is stored on the right

Implementation class hashSet of set

HashSet is a typical implementation of the Set interface. This implementation class is used most of the time when using the Set set. The underlying implementation of java.util.HashSet is actually a java.util.HashMap support, and the underlying physical implementation of HashMap is a Hash table. HashSet stores the elements in the Set according to the Hash algorithm, so it has good access and search performance. HashSet sets the criteria for judging the equality of two elements: the two objects are equal through hashCode() method, and the return values of the equals() method of the two objects are also equal. Therefore, the elements stored in the HashSet override the hashCode and equals methods.

characteristic:

  • The underlying data structure is a hash table
  • Access disorder
  • Duplicate elements cannot be stored
  • Without an index, you cannot use a normal for loop to traverse

Case: storing strings and traversing

public class HashSetDemo {
    public static void main(String[] args) {
        //Create collection object
        HashSet<String> set = new HashSet<String>();

        //Add element
        set.add("hello");
        set.add("world");
        set.add("java");
        //A collection that does not contain duplicate elements
        set.add("world");

        //ergodic
        for(String s : set) {
            System.out.println(s);
        }
    }
}

Introduction to hash values

  • It is a numeric value of type int calculated by JDK according to the address or string or number of the object

How to get hash value

  • public int hashCode() in Object class: returns the hash code value of the Object

Characteristics of hash value

  • The hashCode() method is called multiple times for the same object, and the returned hash value is the same
  • By default, different objects have different hash values. Rewriting the hashCode() method can make the hash values of different objects the same

What is a hash table?

Before JDK1.8, the bottom layer of the hash table was implemented by array + linked list, that is, the linked list was used to deal with conflicts, and the linked list of the same hash value was stored in a linked list. However, when there are many elements in a bucket, that is, hash When there are many elements with equal values, the key It is inefficient to find values in turn. and In JDK1.8, the hash table storage is realized by array + linked list + red black tree. When the length of the linked list exceeds the threshold (8), the linked list is converted into a red black tree , which greatly reduces the lookup time.
In short, Hash table is implemented by array + linked list + red black tree (JDK1.8 adds red black tree).
Before JDK1.8

After JDK1.8

Case requirements

  • Create a collection to store student objects, store multiple student objects, and use the program to traverse the collection on the console
  • Requirement: if the member variable values of the student object are the same, we think it is the same object

Define student classes

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

Define test class

public class HashSetDemo02 {
    public static void main(String[] args) {
        //Create a HashSet collection object
        HashSet<Student> hs = new HashSet<Student>();

        //Create student object
        Student s1 = new Student("Lin Qingxia", 30);
        Student s2 = new Student("Zhang Manyu", 35);
        Student s3 = new Student("Wang Zuxian", 33);

        Student s4 = new Student("Wang Zuxian", 33);

        //Add students to collection
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //Traversal collection (enhanced for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

Conclusion:

For us, ensuring the uniqueness of HashSet collection elements is actually determined according to the hashCode and equals methods of the object. If we store custom objects in the collection, to ensure their uniqueness, we must duplicate the hashCode and equals methods to establish a comparison method belonging to the current object.

Implementation class LinkedHashSet of set

LinkedHashSet is a subclass of HashSet. It adds two attributes before and after to the node on the basis of HashSet, maintaining the order of adding nodes before and after. java.util.LinkedHashSet, which is a data storage structure combined with linked list and hash table. The insertion performance of LinkedHashSet is slightly lower than that of HashSet, but it has good performance when iteratively accessing all elements in the Set.

Topics: Java