精华内容
下载资源
问答
  • 一切语言基础都是数据结构,所以打好基础对于后面学习会有百利而无一害作用.python内置的常用数据类型有:数字、字符串、Bytes、列表、元组、字典、集合、布尔等数字类型用于存储数学上值,比如整数、浮点数...

    数据类型是一种值的集合以及定义在这种值上的一组操作。一切语言的基础都是数据结构,所以打好基础对于后面的学习会有百利而无一害的作用.

    python内置的常用数据类型有:数字、字符串、Bytes、列表、元组、字典、集合、布尔等

    数字类型

    用于存储数学上的值,比如整数、浮点数、复数等。数字类型在python中是不可变类型,意思是一个变量被赋予了一个不一样的数值之后就不再是原来的内存指向了,python是基于值得内存管理机制。

    1.整数(int)

    通常称为整型,包括正数和负数,python3不在长度上区分整数的类型了,就是不再有长整型。

    数字也有八进制和十六进制表示法:

    十六进制:前缀0x和0-9,a-f表示,例如:0xff00

    八进制:前缀0o和0-7表示,例如:0o17

    Python的整数长度为32位,通常是连续分配的内存空间。Python初始化时会自动建立一个小整数对象池,-5到256之间,方便调用,避免后期重复生成。

    除了小整数对象池,Python还有整数缓冲区,就是刚被删除的整数,不会立即被删除回收,而是在后台缓冲一段时间,等待下次可能的调用.

    例如a = 3453453

    print(id(a))---->内存编号33402576

    del a      #已经删除

    b = 3453453 #将3453453赋值给b

    print(id(b))----->内存编号33402576

    2.浮点数(float)

    浮点数就是小数,例如1.23,1.0等,一般很大或很小的浮点数,用科学计数法表示,把10用e表示,例如:1.23*10^9可以表示为1.23e10.

    3.复数(complex)

    复数由实数部分和虚数部分构成,例如a+bj,或者complex(a,b)。很少用到。

    4.数字类型的转换

    int(x):将x转换为整数,如果x是浮点数,保留整数部分,int()中默认使用十进制,可以指定进制,将指定进制的数转化为十进制数。

    例如:常用的2/8/16这三种进制,例如:int("0b10", 2)将二进数0吧0转化为十进制数输出,结果为2。

    float(x):将x转化为浮点数

    complex(x)或complex(x, y):很少用到

    5.计算

    除过+、-、*、/、**、//、%运算符之外,python还提供科学计算等库,例如math,导入math库后,常用的函数有:abs(x):返回x的绝对值,类型随x

    fabs(x):返回x的绝对值,类型是浮点数

    ceil(x):取x的上入整数,如math.ceil(4.1)返回5

    floor(x):取x的下入整数,如math.floor(4.9)返回4

    round(x [,n]):默认返回浮点数x的四舍五入值,如给出n值,则代表舍入到小数点后的n位。例如round(1.23456, 3)返回1.235

    exp(x):返回e的x次幂,e是自然常数

    sqrt(x):返回x的平方根,返回值是float类型

    modf(x):返回x的整数部分和小数部分,两部分的符号与x相同,整数部分以浮点型表示。例如math.modf(4.333),返回元组(0.3330000000000002, 4.0)

    log10(x):返回以10为基数的x的对数,返回值类型是浮点数

    log(x,y):返回以y为基数的x的对数,返回值类型是浮点数

    pow(x, y):返回x的y次幂,即x**y

    max(n1, n2, ...):返回最大值

    min(n1, n2, ...):返回最小值

    展开全文
  • 泛型常用的通配符有哪些?1 Java泛型1.1 泛型的使用-泛型类1.2 泛型的使用-泛型方法1.3 泛型的使用-泛型接口1.4 泛型定义和使用的注意事项2 泛型擦除2.1 通过两个例子证明Java类型类型擦除2.2 类型擦除后保留的...

    1 Java泛型

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

    1.1 泛型的使用-泛型类

    直接上代码:

    public class Pair <U,V>{
        U first;
        V second;
    
        public Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }
    
        public U getFirst() {
            return first;
        }
    
        public V getSecond() {
            return second;
        }
    }
    

    Pair就是一个泛型类,与普通的类的区别体现在:

    • 类名后面多了 一下
    • first和second的类型都是T
      T表示类型参数,泛型就是类型化参数,处理的数据类型不是固定的,而是可以作为参数传入

    如何使用上面的泛型Pair类:

    Pair<String, Integer> pair = new Pair<String, Integer>("ha", 1);
    

    1.2 泛型的使用-泛型方法

    依然直接上代码:

    public static <T> int indexOf(T[] arr, T elm) {
            for(int i=0; i<arr.size; i++) {
                if(arr.get(i).equals(elm)) {
                    return i;
                }
            }
            return -1;
        }
    }
    

    这个方法就是一个泛型方法,类型参数为T,放在返回值前面,有如下两种调用方式:

    indexOf(new Integer[]{1, 3, 5}, 10)
    
    indexOf(new String[]{"容器", "泛型"}, "类")
    

    1.3 泛型的使用-泛型接口

    接口也可以是泛型的,比如Comparable和Comparator接口,代码如下:

    public interface Comparable<T> {
    	public int compareTo(T o);
    }
    
    public interface Comparator<T> {
    	int compare(T o1, T o2);
    	boolean equals(Object obj);
    }
    

    1.4 泛型定义和使用的注意事项

    使用泛型类、方法和接口时,一些要注意的地方:

    • 基本类型不能用于实例化类型参数
    • 运行时类型信息不适用与泛型
    • 类型擦除可能会引发一些冲突

    在定义泛型类、方法和接口时也有一些需要注意的地方,比如:

    • 不能通过类型参数创建对象
    • 泛型类类型参数不能用于静态变量和方法
    • 了解多个类型限定的语法

    2 泛型擦除

    以下转载于 吴庆龙的技术轮子https://www.cnblogs.com/wuqinglong/p/9456193.html

    大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。

    如在代码中定义List<Object>List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法在运行时刻出现的类型转换异常的情况,类型擦除也是Java的泛型与C++模板机制实现方式之间的重要区别。

    2.1 通过两个例子证明Java类型的类型擦除

    例1.原始类型相等:

    public class Test {
    
        public static void main(String[] args) {
    
            ArrayList<String> list1 = new ArrayList<String>();
            list1.add("abc");
    
            ArrayList<Integer> list2 = new ArrayList<Integer>();
            list2.add(123);
    
            System.out.println(list1.getClass() == list2.getClass());
        }
    
    }
    

    在这个例子中,我们定义了两个ArrayList数组,不过一个是ArrayList<String>泛型类型的,只能存储字符串;一个是ArrayList<Integer>泛型类型的,只能存储整数,最后,我们通过list1对象和list2对象的getClass()方法获取他们的类的信息,最后发现结果为true。说明泛型类型StringInteger都被擦除掉了,只剩下原始类型。

    例2.通过反射添加其它类型元素:

    public class Test {
    
        public static void main(String[] args) throws Exception {
    
            ArrayList<Integer> list = new ArrayList<Integer>();
    
            list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
    
            list.getClass().getMethod("add", Object.class).invoke(list, "asd");
    
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
        }
    
    }
    

    在程序中定义了一个ArrayList泛型类型实例化为Integer对象,如果直接调用add()方法,那么只能存储整数数据,不过当我们利用反射调用add()方法的时候,却可以存储字符串,这说明了Integer泛型实例在编译之后被擦除掉了,只保留了原始类型。

    2.2 类型擦除后保留的原始类型

    在上面,两次提到了原始类型,什么是原始类型?

    原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

    例3.原始类型Object:

    class Pair<T> {  
        private T value;  
        public T getValue() {  
            return value;  
        }  
        public void setValue(T  value) {  
            this.value = value;  
        }  
    }  
    

    Pair的原始类型为:

    class Pair {  
        private Object value;  
        public Object getValue() {  
            return value;  
        }  
        public void setValue(Object  value) {  
            this.value = value;  
        }  
    }
    

    因为在Pair<T>中,T 是一个无限定的类型变量,所以用Object替换,其结果就是一个普通的类,如同泛型加入Java语言之前的已经实现的样子。在程序中可以包含不同类型的Pair,如Pair<String>Pair<Integer>,但是擦除类型后他们的就成为原始的Pair类型了,原始类型都是Object

    从上面的例2中,我们也可以明白ArrayList<Integer>被擦除类型后,原始类型也变为Object,所以通过反射我们就可以存储字符串了。

    如果类型变量有限定,那么原始类型就用第一个边界的类型变量类替换。

    比如: Pair这样声明的话

    public class Pair<T extends Comparable> {}
    

    那么原始类型就是Comparable

    要区分原始类型和泛型变量的类型。在调用泛型方法时,可以指定泛型,也可以不指定泛型。

    • 在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到Object
    • 在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类
    public class Test {  
        public static void main(String[] args) {  
    
            /**不指定泛型的时候*/  
            int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型  
            Number f = Test.add(1, 1.2); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number  
            Object o = Test.add(1, "asd"); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object  
    
            /**指定泛型的时候*/  
            int a = Test.<Integer>add(1, 2); //指定了Integer,所以只能为Integer类型或者其子类  
            int b = Test.<Integer>add(1, 2.2); //编译错误,指定了Integer,不能为Float  
            Number c = Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float  
        }  
    
        //这是一个简单的泛型方法  
        public static <T> T add(T x,T y){  
            return y;  
        }  
    }
    

    其实在泛型类中,不指定泛型的时候,也差不多,只不过这个时候的泛型为Object,就比如ArrayList中,如果不指定泛型,那么这个ArrayList可以存储任意的对象。

    例4.Object泛型:

    public static void main(String[] args) {  
        ArrayList list = new ArrayList();  
        list.add(1);  
        list.add("121");  
        list.add(new Date());  
    }  
    

    2.3 类型擦除引起的问题及解决方法

    因为种种原因,Java不能实现真正的泛型,只能使用类型擦除来实现伪泛型,这样虽然不会有类型膨胀问题,但是也引起来许多新问题,所以,SUN对这些问题做出了种种限制,避免我们发生各种错误。

    2.3.1 先检查,再编译以及编译的对象和引用传递问题

    Question:既然说类型变量会在编译的时候擦除掉,那为什么我们往 ArrayList 创建的对象中添加整数会报错呢?不是说泛型变量String会在编译的时候变为Object类型吗?为什么不能存别的类型呢?既然类型擦除了,如何保证我们只能使用泛型变量限定的类型呢?

    Answer: Java编译器是通过先检查代码中泛型的类型,然后在进行类型擦除,再进行编译。

    例如:

    public static  void main(String[] args) {  
    
        ArrayList<String> list = new ArrayList<String>();  
        list.add("123");  
        list.add(123);//编译错误  
    }
    

    在上面的程序中,使用add方法添加一个整型,在IDE中,直接会报错,说明这就是在编译之前的检查,因为如果是在编译之后检查,类型擦除后,原始类型为Object,是应该允许任意引用类型添加的。可实际上却不是这样的,这恰恰说明了关于泛型变量的使用,是会在编译之前检查的。

    那么,这个类型检查是针对谁的呢?我们先看看参数化类型和原始类型的兼容。以 ArrayList举例子,以前的写法:

    ArrayList list = new ArrayList();  
    

    现在的写法:

    ArrayList<String> list = new ArrayList<String>();
    

    如果是与以前的代码兼容,各种引用传值之间,必然会出现如下的情况:

    ArrayList<String> list1 = new ArrayList(); //第一种 情况
    ArrayList list2 = new ArrayList<String>(); //第二种 情况
    

    这样是没有错误的,不过会有个编译时警告。不过在第一种情况,可以实现与完全使用泛型参数一样的效果,第二种则没有效果。因为类型检查就是编译时完成的,new ArrayList()只是在内存中开辟了一个存储空间,可以存储任何类型对象,而真正设计类型检查的是它的引用,因为我们是使用它引用list1来调用它的方法,比如说调用add方法,所以list1引用能完成泛型类型的检查。而引用list2没有使用泛型,所以不行。

    举例子:

    public class Test {  
    
        public static void main(String[] args) {  
    
            ArrayList<String> list1 = new ArrayList();  
            list1.add("1"); //编译通过  
            list1.add(1); //编译错误  
            String str1 = list1.get(0); //返回类型就是String  
    
            ArrayList list2 = new ArrayList<String>();  
            list2.add("1"); //编译通过  
            list2.add(1); //编译通过  
            Object object = list2.get(0); //返回类型就是Object  
    
            new ArrayList<String>().add("11"); //编译通过  
            new ArrayList<String>().add(22); //编译错误  
    
            String str2 = new ArrayList<String>().get(0); //返回类型就是String  
        }  
    
    }  
    

    通过上面的例子,我们可以明白,类型检查就是针对引用的,谁是一个引用,用这个引用调用泛型方法,就会对这个引用调用的方法进行类型检测,而无关它真正引用的对象。

    泛型中参数话类型为什么不考虑继承关系?

    在Java中,像下面形式的引用传递是不允许的:

    ArrayList<String> list1 = new ArrayList<Object>(); //编译错误  
    ArrayList<Object> list2 = new ArrayList<String>(); //编译错误
    

    我们先看第一种情况,将第一种情况拓展成下面的形式:

    ArrayList<Object> list1 = new ArrayList<Object>();  
    list1.add(new Object());  
    list1.add(new Object());  
    ArrayList<String> list2 = list1; //编译错误
    

    实际上,在第4行代码的时候,就会有编译错误。那么,我们先假设它编译没错。那么当我们使用list2引用用get()方法取值的时候,返回的都是String类型的对象(上面提到了,类型检测是根据引用来决定的),可是它里面实际上已经被我们存放了Object类型的对象,这样就会有ClassCastException了。所以为了避免这种极易出现的错误,Java不允许进行这样的引用传递。(这也是泛型出现的原因,就是为了解决类型转换的问题,我们不能违背它的初衷)。

    再看第二种情况,将第二种情况拓展成下面的形式:

    ArrayList<String> list1 = new ArrayList<String>();  
    list1.add(new String());  
    list1.add(new String());
    
    ArrayList<Object> list2 = list1; //编译错误
    

    没错,这样的情况比第一种情况好的多,最起码,在我们用list2取值的时候不会出现ClassCastException,因为是从String转换为Object。可是,这样做有什么意义呢,泛型出现的原因,就是为了解决类型转换的问题。我们使用了泛型,到头来,还是要自己强转,违背了泛型设计的初衷。所以java不允许这么干。再说,你如果又用list2往里面add()新的对象,那么到时候取得时候,我怎么知道我取出来的到底是String类型的,还是Object类型的呢?

    所以,要格外注意,泛型中的引用传递的问题。

    2.3.2 自动类型转换

    因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。既然都被替换为原始类型,那么为什么我们在获取的时候,不需要进行强制类型转换呢?

    看下ArrayList.get()方法:

    public E get(int index) {  
    
        RangeCheck(index);  
    
        return (E) elementData[index];  
    
    }
    

    可以看到,在return之前,会根据泛型变量进行强转。假设泛型类型变量为Date,虽然泛型信息会被擦除掉,但是会将(E) elementData[index],编译为(Date)elementData[index]。所以我们不用自己进行强转。当存取一个泛型域时也会自动插入强制类型转换。假设Pair类的value域是public的,那么表达式:

    Date date = pair.value;
    

    也会自动地在结果字节码中插入强制类型转换。

    2.3.3 类型擦除与多态的冲突和解决方法

    现在有这样一个泛型类:

    class Pair<T> {  
    
        private T value;  
    
        public T getValue() {  
            return value;  
        }  
    
        public void setValue(T value) {  
            this.value = value;  
        }  
    }
    

    然后我们想要一个子类继承它。

    class DateInter extends Pair<Date> {  
    
        @Override  
        public void setValue(Date value) {  
            super.setValue(value);  
        }  
    
        @Override  
        public Date getValue() {  
            return super.getValue();  
        }  
    }
    

    在这个子类中,我们设定父类的泛型类型为Pair<Date>,在子类中,我们覆盖了父类的两个方法,我们的原意是这样的:将父类的泛型类型限定为Date,那么父类里面的两个方法的参数都为Date类型。

    public Date getValue() {  
        return value;  
    }  
    
    public void setValue(Date value) {  
        this.value = value;  
    }
    

    所以,我们在子类中重写这两个方法一点问题也没有,实际上,从他们的@Override标签中也可以看到,一点问题也没有,实际上是这样的吗?

    分析:实际上,类型擦除后,父类的的泛型类型全部变为了原始类型Object,所以父类编译之后会变成下面的样子:

    class Pair {  
        private Object value;  
    
        public Object getValue() {  
            return value;  
        }  
    
        public void setValue(Object  value) {  
            this.value = value;  
        }  
    }  
    

    再看子类的两个重写的方法的类型:

    @Override  
    public void setValue(Date value) {  
        super.setValue(value);  
    }  
    @Override  
    public Date getValue() {  
        return super.getValue();  
    }
    

    先来分析setValue方法,父类的类型是Object,而子类的类型是Date,参数类型不一样,这如果实在普通的继承关系中,根本就不会是重写,而是重载。

    我们在一个main方法测试一下:

    public static void main(String[] args) throws ClassNotFoundException {  
            DateInter dateInter = new DateInter();  
            dateInter.setValue(new Date());                  
            dateInter.setValue(new Object()); //编译错误  
    }
    

    如果是重载,那么子类中两个setValue方法,一个是参数Object类型,一个是Date类型,可是我们发现,根本就没有这样的一个子类继承自父类的Object类型参数的方法。所以说,却是是重写了,而不是重载了。

    为什么会这样呢?原因是这样的,我们传入父类的泛型类型是Date,Pair<Date>,我们的本意是将泛型类变为如下:

    class Pair {  
        private Date value;  
        public Date getValue() {  
            return value;  
        }  
        public void setValue(Date value) {  
            this.value = value;  
        }  
    }
    

    然后再子类中重写参数类型为Date的那两个方法,实现继承中的多态。

    可是由于种种原因,虚拟机并不能将泛型类型变为Date,只能将类型擦除掉,变为原始类型Object。这样,我们的本意是进行重写,实现多态。可是类型擦除后,只能变为了重载。这样,类型擦除就和多态有了冲突。JVM知道你的本意吗?知道!!!可是它能直接实现吗,不能!!!如果真的不能的话,那我们怎么去重写我们想要的Date类型参数的方法啊。

    于是JVM采用了一个特殊的方法,来完成这项功能,那就是桥方法

    首先,我们用javap -c className的方式反编译下DateInter子类的字节码,结果如下:

    class com.tao.test.DateInter extends com.tao.test.Pair<java.util.Date> {  
      com.tao.test.DateInter();  
        Code:  
           0: aload_0  
           1: invokespecial #8                  // Method com/tao/test/Pair."<init>":()V  
           4: return  
    
      public void setValue(java.util.Date);  //我们重写的setValue方法  
        Code:  
           0: aload_0  
           1: aload_1  
           2: invokespecial #16                 // Method com/tao/test/Pair.setValue:(Ljava/lang/Object;)V  
           5: return  
    
      public java.util.Date getValue();    //我们重写的getValue方法  
        Code:  
           0: aload_0  
           1: invokespecial #23                 // Method com/tao/test/Pair.getValue:()Ljava/lang/Object;  
           4: checkcast     #26                 // class java/util/Date  
           7: areturn  
    
      public java.lang.Object getValue();     //编译时由编译器生成的桥方法  
        Code:  
           0: aload_0  
           1: invokevirtual #28                 // Method getValue:()Ljava/util/Date 去调用我们重写的getValue方法;  
           4: areturn  
    
      public void setValue(java.lang.Object);   //编译时由编译器生成的桥方法  
        Code:  
           0: aload_0  
           1: aload_1  
           2: checkcast     #26                 // class java/util/Date  
           5: invokevirtual #30                 // Method setValue:(Ljava/util/Date; 去调用我们重写的setValue方法)V  
           8: return  
    }
    

    从编译的结果来看,我们本意重写setValuegetValue方法的子类,竟然有4个方法,其实不用惊奇,最后的两个方法,就是编译器自己生成的桥方法。可以看到桥方法的参数类型都是Object,也就是说,子类中真正覆盖父类两个方法的就是这两个我们看不到的桥方法。而打在我们自己定义的setvaluegetValue方法上面的@Oveerride只不过是假象。而桥方法的内部实现,就只是去调用我们自己重写的那两个方法。

    所以,虚拟机巧妙的使用了桥方法,来解决了类型擦除和多态的冲突。不过,要提到一点,这里面的setValuegetValue这两个桥方法的意义又有不同。setValue方法是为了解决类型擦除与多态之间的冲突。而getValue却有普遍的意义,怎么说呢,如果这是一个普通的继承关系:

    那么父类的setValue方法如下:

    public ObjectgetValue() {  
        return super.getValue();  
    }
    

    而子类重写的方法是:

    public Date getValue() {  
        return super.getValue();  
    }
    

    其实这在普通的类继承中也是普遍存在的重写,这就是协变。
    关于协变:。。。。。。

    并且,还有一点也许会有疑问,子类中的巧方法Object getValue()和Date getValue()是同 时存在的,可是如果是常规的两个方法,他们的方法签名是一样的,也就是说虚拟机根本不能分别这两个方法。如果是我们自己编写Java代码,这样的代码是无法通过编译器的检查的,但是虚拟机却是允许这样做的,因为虚拟机通过参数类型和返回类型来确定一个方法,所以编译器为了实现泛型的多态允许自己做这个看起来“不合法”的事情,然后交给虚拟器去区别。

    2.3.4 泛型类型变量不能是基本数据类型

    不能用类型参数替换基本类型。就比如,没有ArrayList<double>,只有ArrayList<Double>。因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储double值,只能引用Double的值。

    2.3.5 编译时集合的instanceof

    ArrayList<String> arrayList = new ArrayList<String>();
    

    因为类型擦除之后,ArrayList<String>只剩下原始类型,泛型信息String不存在了。

    那么,编译时进行类型查询的时候使用下面的方法是错误的:

    ArrayList<String> arrayList = new ArrayList<String>();
    

    2.3.6 泛型在静态方法和静态类中的问题

    泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数

    举例说明:

    public class Test2<T> {    
        public static T one;   //编译错误    
        public static  T show(T one){ //编译错误    
            return null;    
        }    
    }
    

    因为泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。

    但是要注意区分下面的一种情况:

    public class Test2<T> {    
    
        public static <T >T show(T one){ //这是正确的    
            return null;    
        }    
    }
    

    因为这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的 T,而不是泛型类中的T。

    3 泛型通配符

    转载于:https://juejin.cn/post/6844903917835419661

    我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?

    常用的 T,E,K,V,?

    本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:

    • ? 表示不确定的 java 类型
    • T (type) 表示具体的一个java类型
    • K V (key value) 分别代表java键值中的Key Value
    • E (element) 代表Element

    3.1 “?” 无界通配符

    先从一个小例子看起,我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表,我的第一个想法是像这样的:

    List<Animal> listAnimals
    

    但是老板的想法确实这样的:

    List<? extends Animal> listAnimals
    

    为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。

    static int countLegs (List<? extends Animal > animals ) {
        int retVal = 0;
        for ( Animal animal : animals )
        {
            retVal += animal.countLegs();
        }
        return retVal;
    }
    
    static int countLegs1 (List< Animal > animals ){
        int retVal = 0;
        for ( Animal animal : animals )
        {
            retVal += animal.countLegs();
        }
        return retVal;
    }
    
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
     	// 不会报错
        countLegs( dogs );
    	// 报错
        countLegs1(dogs);
    }
    

    当调用 countLegs1 时,就会飘红,提示的错误信息如下:
    在这里插入图片描述
    所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。像 countLegs 方法中,限定了上届,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错。而 countLegs1 就不行。

    3.2 上界通配符 < ? extends E>

    上届:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
    

    在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

    • 如果传入的类型不是 E 或者 E 的子类,编译不成功
    • 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
    private <K extends A, E extends B> E test(K arg1, E arg2){
        E result = arg2;
        arg2.compareTo(arg1);
        //.....
        return result;
    }
    

    类型参数列表中如果有多个类型参数上限,用逗号分开!

    3.3 下界通配符 < ? super E>

    下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
    

    在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。

    private <T> void test(List<? super T> dst, List<T> src){
        for (T t : src) {
            dst.add(t);
        }
    }
    
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        List<Animal> animals = new ArrayList<>();
        new Test3().test(animals,dogs);
    }
    // Dog 是 Animal 的子类
    class Dog extends Animal {
    
    }
    

    dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。

    3.4 ? 和 T 的区别

    在这里插入图片描述?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ? 不行,比如如下这种 :

    // 可以
    T t = operate();
    
    // 不可以
    ? car = operate();
    
    

    简单总结下:

    T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

    区别1:通过 T 来 确保 泛型参数的一致性

    // 通过 T 来 确保 泛型参数的一致性
    public <T extends Number> void
    test(List<T> dest, List<T> src)
    
    //通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型
    public void
    test(List<? extends Number> dest, List<? extends Number> src)
    

    像下面的代码中,约定的 T 是 Number 的子类才可以,但是申明时是用的 String ,所以就会飘红报错。
    在这里插入图片描述
    不能保证两个 List 具有相同的元素类型的情况:

    GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric<>();
    List<String> dest = new ArrayList<>();
    List<Number> src = new ArrayList<>();
    glmapperGeneric.testNon(dest,src);
    

    上面的代码在编译器并不会报错,但是当进入到 testNon 方法内部操作时(比如赋值),对于 dest 和 src 而言,就还是需要进行类型转换。

    区别2:类型参数可以多重限定而通配符不行

    在这里插入图片描述
    使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。

    区别3:通配符可以使用超类限定而类型参数不行

    类型参数 T 只具有 一种 类型限定方式:

    T extends A
    

    但是通配符 ? 可以进行 两种限定:

    ? extends A
    ? super A
    

    Class 和 Class<?> 区别:

    前面介绍了 ? 和 T 的区别,那么对于,Class 和 <Class<?> 又有什么区别呢?
    Class 和 Class<?>

    最常见的是在反射场景下的使用,这里以用一段发射的代码来说明下。

    // 通过反射的方式生成  multiLimit 
    // 对象,这里比较明显的是,我们需要使用强制类型转换
    MultiLimit multiLimit = (MultiLimit)
    Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();
    

    对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。

    对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接 检查到类型的问题:
    在这里插入图片描述
    Class 在实例化的时候,T 要替换成具体类。Class<?> 它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明:

    // 可以
    public Class<?> clazz;
    // 不可以,因为 T 需要指定类型
    public Class<T> clazzT;
    

    所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class<?>。
    在这里插入图片描述那如果也想 public Class clazzT; 这样的话,就必须让当前的类也指定 T ,

    public class Test3<T> {
        public Class<?> clazz;
        // 不会报错
        public Class<T> clazzT;
    
    展开全文
  • 常用的集合类是List和Map。List的具体实现包括ArrayList和Vector,它们是可变大小的列表,比较适合构建,存储和操作任何类型对象的元素列表,LIst适用于按索引访问元素的元素是的情形。 Map提供了一个更通用的元素...

    你所知道的集合类有哪些?主要方法 ?

    最常用的集合类是List和Map。List的具体实现包括ArrayList和Vector,它们是可变大小的列表,比较适合构建,存储和操作任何类型对象的元素列表,LIst适用于按索引访问元素的元素是的情形。

    Map提供了一个更通用的元素存储方法,Map集合类适用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。对于Set ,大概的方法是add ,remove ,contains; 对于map,大概的方法就是Put ,remove ,contains等,因为,我只要在eclipse下按点操作符,很自然的这些 方法就出来了,我记住的一些思想就是List类会有get(index )这样的方法。因为它可以按顺序取元素,而Set类中没有get(int index)这样的方法。

    List和Set都可以迭代出所有的元素,迭代时先要得到一个iterator 对象,所以,Set和list类都有一个iterator 方法,用于返回那个iterator对象,map可以返回三个集合,一个是返回所有的key的集合,另外 一个返回的是所有value的集合,再一个返回的key和value组合成的EntrySet对象的集合,map也有get方法,参数是key,返回值是key对应的value

    展开全文
  • python中数据类型有:整型、长整型、浮点型、字符串类型、布尔类型、列表类型、元组类型、字典类型、集合类型。数据类型是每种编程语言必备属性,只有给数据赋予明确数据类型,计算机才能对数据进行处理运算,因此...

    python中数据类型有:整型、长整型、浮点型、字符串类型、布尔类型、列表类型、元组类型、字典类型、集合类型。

    数据类型是每种编程语言必备属性,只有给数据赋予明确的数据类型,计算机才能对数据进行处理运算,因此,正确使用数据类型是十分必要的,不同的语言,数据类型类似,但具体表示方法有所不同,以下是Python编程常用的数据类型:

    1. 数字类型

    Python数字类型主要包括int(整型)、long(长整型)和float(浮点型),但是在Python3中就不再有long类型了。

    int(整型)

    在32位机器上,整数的位数是32位,取值范围是-231~231-1,即-2147483648~214748364;在64位系统上,整数的位数为64位,取值范围为-263~263-1,即9223372036854775808~9223372036854775807。

    long(长整型)

    Python长整型没有指定位宽,但是由于机器内存有限,使用长的长整数数值也不可能无限大。

    float(浮点型)

    浮点型也就是带有小数点的数,其精度和机器有关。

    complex(复数)

    Python还支持复数,复数由实数部分和虚数部分构成,可以用 a + bj,或者 complex(a,b) 表示, 复数的实部 a 和虚部 b 都是浮点型。

    2. 字符串

    在Python中,加了引号的字符都被认为是字符串,其声明有三种方式,分别是:单引号、双引号和三引号;Python中的字符串有两种数据类型,分别是str类型和unicode类型,str类型采用的ASCII编码,无法表示中文,unicode类型采用unicode编码,能够表示任意字符,包括中文和其他语言。

    3. 布尔型

    和其他编程语言一样,Python布尔类型也是用于逻辑运算,有两个值:True(真)和False(假)。

    4. 列表

    列表是Python中使用最频繁的数据类型,集合中可以放任何数据类型,可对集合进行创建、查找、切片、增加、修改、删除、循环和排序操作。

    5. 元组

    元组和列表一样,也是一种序列,与列表不同的是,元组是不可修改的,元组用”()”标识,内部元素用逗号隔开。

    6. 字典

    字典是一种键值对的集合,是除列表以外Python之中最灵活的内置数据结构类型,列表是有序的对象集合,字典是无序的对象集合。

    7. 集合

    集合是一个无序的、不重复的数据组合,它的主要作用有两个,分别是去重和关系测试。

    展开全文
  • Redis有哪些常用数据类型? 答:String 一般用于存放键值对, Hash 一般用户存放对象, List 类似于双端链表, Set 无序集合, SortedSet 有序集合, 还有比较高级数据类型,比如用于计数HyperLogLog,...
  • 常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 Map 提供了一个更...
  • 列表可以完成大多数集合数据结构实现。它支持字符,数字,字符串甚至可以包含列表(嵌套)列表用[ ]标识,是Python最通用复合数据类型。列表中值分割可以用到变量[头下标;尾下标],就可以截取相应列表,从...
  • 将获得的“点组数据”转换成三角面的集合体“多边形数据”生成3D实体。扫描方式大致分为两种:・接触式・非接触式接触式扫描是在对象物上安装传感器的同时测量坐标。因此,虽然精度高的优点,但是测量需要时间。非...
  • 背景介绍在了解JavaScript的数据类型之前,先了解下JavaScript是什么?JavaScript是一种属于网络的脚本语言,被广泛用在了各&lt;br&gt;种各样的Web应用中,主要用于嵌入...一个值的集合以及定义在这个值集上的...
  • 将获得的"点组数据"转换成三角面的集合体"多边形数据"生成3D实体。扫描方式大致分为两种:・接触式・非接触式接触式扫描是在对象物上安装传感器的同时测量坐标。因此,虽然精度高的优点,但是测量需要时间。非接触...
  • 而大数据技术,是指从各种各样类型的数据中,快速获得价值信息能力。那么关于大数据技术大致包含哪些内容? 第一,数据采集ETL工具负责将分布、异构数据源中数据如关系数据、平面数据文件等抽取到临时中间...
  • 常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表, 比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 Map 提供了一个更...
  • 而大数据技术,是指从各种各样类型的数据中,快速获得价值信息能力。那么关于大数据技术大致包含哪些内容? 第一,数据采集 ETL工具负责将分布、异构数据源中数据如关系数据、平面数据文件等抽取到临时...
  • 常用的集合类是List和Map,List的具体的实现包括ArrayList和Vector,,它们是可变大小的列表,,比较适合构建,存储,和操作任何类型对象的元素列表。。List适用于按照数值索引访问元素的情形。。 Map提供了一个...
  •  Rendezvous(负载过程中集合点下虚拟用户): 当设置集合点后会生成相关数据,反映了随着时间推移各个时间点上并发用户数目,方便我们了解并发用户变化情况。 Errors(错误统计): 通过错误信息可以了解...
  • 关于Map接口中常用的方法: 1.Map接口和Collection接口没有继承关系。 2.Map集合以key和value(键值对)的方式存储数据。 key和value都是引用数据类型,存储的都是对象的内存地址。 key起到主导的地位,value是key的...
  • 常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 Map 提供了一个更...
  • java的集合类都有哪些,主要方法.

    千次阅读 2012-11-14 23:33:28
    常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。 Map 提供了一...
  • 提供了生产负载虚拟用户运行状态相关信息,可以帮助我们了解负载生成结果。  Rendezvous(负载过程中集合点下... 通过错误信息可以了解错误产生时间和错误类型,方便定位产生错误原因。  Errors per S...
  • 在Python中很多数据类型,天天Python只为大家讲解一些常用常见数据类型,包括number类型,str字符串类型,list列表类型,tuple元组类型,set集合类型以及dict字典类型。在讲解数据类型之前,先为大家讲解一个...
  • 集合是什么:集合是java提供...集合有哪些集合分为单列集合和双列集合,单列集合存储是一个元素,双列集合存储是一对元素。 单列集合的根接口为collection collection有两个重要子接口,分别为list(元素有
  • 集合的常用知识

    2016-07-14 19:20:17
    Collection /* * 集合的由来: ... * 而要想存储多个对象,就不能是一个基本变量,而应该是一个容器类型的变量,在我们目前所学过知识里面,有哪些是容器类型的呢? * 数组和StringBuffer。但是呢?Str
  • 1.背景介绍 JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。...“数据类型”在数据结构中的定义”一个值的集合以及定义在这个值集上的一组操作”。在编程语言...
  • 常用数据结构有哪些

    2015-11-29 20:39:33
    线性结构:元素之间存在一对一关系常见类型有: 数组,链表,队列,栈,它们之间在操作上有所区别.例如:链表可在任意位置插入或删除元素,而队列在队尾插入元素,队头删除元素,栈只能在栈顶进行插 入,删除操作. 树形...
  • 在如今的互联网大背景下,网站开发成为了一个大热门,而网站开发离不开数据库的支持。对于开发人员来说,数据库日渐成为IT...常用的数据库类型如Access、SQLServer和MySQL。如果用户想做一个网站,哪种数据库更加适...
  • 众所周知,大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理数据集合。它含义十分广泛,并庞大复杂,1、数据处理工具:Excel在Excel,需要重点了解数据处理重要技巧及函数应用,特别是...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 201
精华内容 80
关键字:

常用的集合类型有哪些