Java -- generics and collections

generic paradigm

A generic type is actually a type parameter used to specify a type.

In order to count students' scores, it is required to design a Score object, including course name, course number and course Score. However, there are two kinds of scores: one is to take excellent, good and qualified as the result, and the other is to use digital scores. The problem now is that grades may be String or Integer. How can we save the two possible types?

public class Score<T> {   //Convert Score to generic class < T >    
        String name;   
        String id;    
        T score;  //T is a generic type, which automatically becomes the corresponding type according to the type provided by the user   
     public Score(String name, String id, T score) {   
                    //The score type provided is the type represented by T                        
        this.name = name;        
        this.id = id;        
        this.score = score; }}
public static void main(String[] args) {    
            //Directly determine that the type of Score is a string type Score    
       Score<String> score = new Score<String>("Fundamentals of data structure and algorithm", "EP074512", "excellent");            
           Integer i = score.score;  
            //Compilation failed because the member variable score type is set to String!}

In essence, generics are also a syntax sugar (not the syntax supported by the JVM. After compilation, they will be converted to the syntax supported by the compiler, such as foreach). After compilation, they will be erased and changed back to the above Object type call, but the type conversion is completed by the compiler for us, not by ourselves (SAFE)

//Decompiled code
public static void main(String[] args) {        
    Score score = new Score("Fundamentals of data structure and algorithm", "EP074512", "excellent");        
            String i = (String)score.score;  
                 //In fact, it will still be cast, but this is done by the compiler}

In this way, the situation that the content of generic type disappears and turns into Object after compilation is called type erasure (important and needs to be fully understood). Therefore, generic type is just a syntax to facilitate us to determine the type at the compilation stage, which is not supported by the JVM.

Use of generics

Generic class

A generic class is an ordinary class. One more type parameter specifies the specific generic type.

public class Score<T> {   
                //Convert Score to generic class < T >    
        String name;    
        String id;    
        T score; 
                 //T is a generic type, which automatically becomes the corresponding type according to the type provided by the user    
    public Score(String name, String id, T score) {   
                //The score type provided is the type represented by T        
            this.name = name;        
            this.id = id;       
            this.score = score;    }}

Define a generic type in a common type. Generic type T is called a parameterized type. When defining the reference of a generic class, you need to specify the type:

 Score<String> score = new Score<String>("Fundamentals of data structure and algorithm", "EP074512", "excellent");

Note that generics can only be used for object attributes, that is, non static member variables:

Generic types cannot use basic types. If basic types are needed, they can only be replaced with wrapper classes of basic types;

Generic methods of classes

public T getScore() {    
                        //If the return value type of the method is generic, the compiler will automatically infer 
         return score;}
public void setScore(T score) {   
                        //If the formal parameter of a method is generic, the argument can only be the type at the time of definition  
            this.score = score;}
Score<String> score = new Score<String>("Fundamentals of data structure and algorithm", "EP074512", "excellent");score.setScore(10);  
 //Compilation failed because only String types are accepted

Similarly, static methods cannot directly use the generics defined by the class (note that they cannot be used directly, and static methods can use generics)

Custom generic method

So what if I want to use generics in static methods? First of all, we need to clarify why we can't use generics before, because our definition of generics is on the class. We can only start using them when we specify the specific type, that is, we can determine the type when we create the object, but the static method doesn't need to be attached to the object, so it can only be determined when we use it, so the static method can use generics, However, it needs to be defined separately:

public static <E> void test(E e){   
                //Declare generics before method definition  
        System.out.println(e);}

Similarly, member methods can also define generics by themselves, and then determine the type in actual use:

public <E> void test(E e){  System.out.println(e);}

In fact, whether it is a generic class or a generic method, you must be able to infer the type and clarify the type when you reuse it.

Note that you must distinguish between the generics defined before the classification and the generics defined before the method!

Generic reference

Score<Integer> score;  //Declare generic type as Integer type

When defining a reference to a generic class, you need to indicate this type later:

If you do not want to specify a type, or if you want this reference type to reference any generic Score class object, you can use? Wildcard to indicate automatic matching of any available types:

Score<?> score;   //Score can reference any score type object!

Because wildcards are used, the compiler cannot infer types, so it can only use original types.

Object o = score.getScore();   //Can only become Object

Boundaries of generics

Now there is a new requirement. There is no String score, but the score may still be integer or decimal. At this time, we don't want users to specify generics as other types except numeric types, so we need to use the upper bound definition of generics:

public class Score<T extends Number> {   
        //Set the Generic upper bound, which must be a subclass of Number    
        private final String name;   
        private final String id;    
        private T score;    
            public Score(String name, String id, T score) {        
                this.name = name;        
                this.id = id;        
                this.score = score;    }    
    public T getScore() {        
            return score;    }}

The upper bound is limited by the extends keyword. Only the specified type or subclass of the specified type can be used as a type parameter. And once we specify the upper bound, the compiler will promote the range from the original type Object to the upper bound Number we specify, but it is still unable to specify the specific type.

diamond operator

Every time we create a generic object, we need to indicate the type before and after, but in fact, the later type declaration can be removed, because we have defined the type when we pass in the parameter or define the reference of the generic class, so jdk1 7 provides diamond operators to simplify the code:

Score<Integer> score = new Score<Integer>("Fundamentals of data structure and algorithm", "EP074512", 10);  
               //Before 1.7





 Score<Integer> score = new Score<>("Fundamentals of data structure and algorithm", "EP074512", 10);  
               //After 1.7

Generic and polymorphic

Generics can be defined not only on classes, but also on interfaces:

public interface ScoreInterface<T> {    
            T getScore();   
             void setScore(T t);}
public class Score<T> implements ScoreInterface<T>{   
            //Convert Score to generic class < T >    
            private final String name;    
            private final String id;    
            private T score;    
public Score(String name, String id, T score) {         
            this.name = name;        
            this.id = id;        
            this.score = score;    }    
public T getScore() {        
                return score;    }    
@Override    
public void setScore(T score) {        
        this.score = score;    }}
    public class StringScore implements ScoreInterface<String>{   
            //Specify the type when implementing    
@Override    
    public String getScore() {        
        return null;    }    
@Override    
        public void setScore(String s) {    
                                            }}

data structure

Sequence table

This storage structure is called sequential storage structure, and the linear table realized in this way is called sequential table. Each individual in the table is called an element. The element on the left of the element (the previous element) is called a precursor. Similarly, the element on the right (the latter element) is called a successor.Linked list

Each node stores an element and a table pointing to the next node.

Sequence table:

  1. Fast access speed and high random access performance
  2. Insertion and deletion are inefficient, and in extreme cases, the entire table needs to be changed
  3. It is not easy to expand and needs to copy and recreate the array

Linked list:

  1. The insertion and deletion efficiency is high. You only need to change the direction of the connection point
  2. Dynamically expand capacity without worrying about capacity
  3. Access elements need to be searched in turn, and random access elements are inefficient

Stack

The stack follows the principle of first in first out, and elements can only be added and deleted at one end of the linear table..

When an element is inserted into the stack, it is called pushing into the stack, and removing the element at the top of the stack is called leaving the stack.

queue

You can only go in at the end of the team and out at the head of the team.

.

Keywords: Java Back-end JavaSE IDE

Added by saami123 on Tue, 01 Mar 2022 12:45:50 +0200