面向对象-泛型

作者: 我叫吴智斌 | 来源:发表于2018-12-05 23:19 被阅读10次

面向对象-泛型

没有泛型存在的问题

1.往集合当中存储元素,可以存储任何类型元素

        List list = new ArrayList();
        list.add(1);
        list.add("a");
        取出来的都是Object类型
        Object obj = list.get(1);
        现在想要使用字符串当中的特有的方法,必须得要转回来
        String str= (String)obj;
        str.substring(0, 1);

2.没有办法约束集合当中只能存储某一种类型
3.如:设计一个类 点(point x,y)
想要传入多个不一样类型,同样参数的,就需要用到泛型

class Point{
    private String x;
    private String y;
    
    private Integer x1;
    private Integer y1;
    
    private Double x2;
    private Double y2;
    
}


泛型定义

什么是泛型?
广泛通用的类型 (一开始还不清楚是什么类型,在使用的时候,才能确定是什么类型)
代码模块中类型不确定,谁调用该段代码,谁就可以来指明这个类型

定义时分为两步
1.在类的后面<> T:代表不确定的类型,在创建对象的时候,才确定是什么类型
T:type E:element K:key

class Point<T>{
    1.可以是字符串类型
    2.Integer
    3.Double
    
2.在变量的前面添加上一个   T   
    把点定义为泛型
    T x;
    T y;
}

创建对象的时候并没有声明泛型是什么类型
如果没有指明泛型的类型,它就是Object类型,没使用泛型,什么类型都能传进去

        
        Point<String> p = new Point<String>();
        p.x="10";
        p.y="20";

在创建对象指明泛型,这个类型可以是任意的类型(只能是引用类型)

        Point<Integer> p2 = new Point<Integer>();
        p2.x=10;
        p2.y=20;

集合使用泛型的好处

规定集合当中只能存放String类型 (不能是基本数据类型,必须得要引用类型)

        ArrayList<String> list = new ArrayList<String>();       
        list.add("my");
        list.add("abc");
        //list.add(10);  加了泛型以后,在编译的时候就报错了

集合如果没有指定泛型,取出来的元素都是Object

    for (Object obj : list) {
        String str = (String) obj;
        System.out.println(str.length());
    }
    
    for (String str : list) {定义了泛型,就不需要再去强转
        System.out.println(str.length());
    }

泛型使用注意点及本质

泛型注意点:

1.泛型前后类型必须得要一致

2.从java 1.7开始,后面的类型可以不写 new ArrayList<>();菱形语法

3.泛型是没有继承的

        //错误的写法   ArrayList<Object> list = new ArrayList<String>(); 

4.泛型其实是一个语法糖 (本质还是Object 内部其实还是要进行强转的)

        Point<String> p1 = new Point<>();
        p1.x = "20";
        p1.y = "10";
        System.out.println(p1.getY());//里面会进行强转

自定义泛型方法

class Test<T>{
    T name;

    public T getName() {
        return name;
    }
}

自定义泛型方法
方法当中定义的泛型,是在使用方法时,参数传递确定具体是什么类型
方法想要单独使用泛型,必须得要有参数才有意义

class Student {
    <T> void test(T a) {
        System.out.println(a.getClass());
}

自定义方法也可以自定义泛型

static <E> void test2(E name) {
    System.out.println(name.getClass());
}

添加泛型返回值

static <E>  E test3(E name) {
    System.out.println(name.getClass());
    return name;//需要用传入什么类型就用什么类型来接收
    }
}

1.泛型类:在类上面定义的泛型,在创建对象的时候,要指明泛型的类型

        Test<String> t = new Test<String>();
        Test<Integer> t1 = new Test<Integer>();

泛型当中定义的泛型只能用在普通方法上面
不能使用在静态方法上面
因为静态方法是直接使用类名调用,泛型是在创建对象的时候,才去指定类型

2.泛型方法:就是在方法上面添加了泛型
单独对一个方法上面声明泛型
方法当中定义的泛型

        new Student().test("adf");
        Student.test2(12);

        Integer i = Student.test3(12);
        System.out.println(i);//class java.lang.Integer

泛型通配符

通配符:不知道使用什么类型来接收的时候,可以用?来表示未知

    List<Integer> list = new ArrayList<>();
    test(list);

只用来接收使用
不能做添加操作

        List<?> list2 = new ArrayList<>();
        list2.add("a");

泛型的上限和下限

泛型的的上限:用来限定元素的类型必须继承指定类型(Number的子类)<指定类型或者指定类型的子类>

static void test(List<? extends Number> list) {

}

需要用正确的类型接收指定继承的类型

        List<Integer> list = new ArrayList<>();
        test(list);
        

        List<String> list2 = new ArrayList<>();
        test(list2);//不是继承Number,报错

泛型的的下限:用来限定元素的类型必须继承指定类型(Number的父类)<指定类型或者指定类型的父类>

    static void test2(List<? super Number> list) {
        
    }
        List<Number> list3 = new ArrayList<>();
        //List<Object> list3 = new ArrayList<>(); 可以
        test(list3);//可以接收Number 的 父类和它自己类型本身

泛型擦除

泛型擦除(把泛型去除)

        List<String> list = new ArrayList<>();
        list.add("a");//定义了泛型,类型限定了传参
        List list2 = null;
        list2 = list ;//把list当中的泛型擦除掉,没有了泛型
        list2.add(10);
        list2.add("aaa");//什么类型都能放进去,编译过后也是这样,变成了Object类型

List与数组的互相转换

数组转成集合

        int[] arr = { 10, 20, 30 };
        List list = Arrays.asList(arr);//---->List<int[]> list = Arrays.asList(arr);泛型类型:数组

运行时报错,数组转换为集合并不能添加元素

        list.add(10);//报错

那为什么要转为集合呢?因为转过之后,可以面向对象的方式来操作数组(除了不能添加和删除元素之外,集合当中的其他东西都是可以使用的)

        @SafeVarargs
        @SuppressWarnings("varargs")
        public static <T>方法泛型    List<T>返回一个传入什么类型的List  asList(T...可变参数,本质是一个数组   a) {
        return new ArrayList<>(a);
        }

基本数据类型,转成集合时,是把基本数据类型的数组,当作是一个对象
在开发当中不会把基本数据类型数组转成集合,没意义

    System.out.println(list.size());//1     int[] arr = { 10, 20, 30 };

引用数据类型的数组才去转成集合

    Integer[] arr2 = { 10, 20, 30 };
    List list2 = Arrays.asList(arr2);//---->List<Integer> list2 = Arrays.asList(arr2);泛型类型:对象
    System.out.println(list2);//[10, 20, 30]
    System.out.println(list2.size());//3

集合转数组

    List<String> list3 = new ArrayList<>();
    list3.add("a");
    list3.add("b");
    list3.add("c");

    Object[] o = list3.toArray();//以前Object数组转法
    String[] strArr =  list3.toArray(new String[0]);
    //静态开辟空间,如果开辟的空间少于size,会自动的创建一个和size一样空间大小

    System.out.println(Arrays.toString(strArr));//[a, b, c]

    String[] strArr2 =  list3.toArray(new String[10]);
    //<T> T[] toArray(T[] a);
    System.out.println(Arrays.toString(strArr2));
    //[a, b, c, null, null, null, null, null, null, null]

集合嵌套集合

需求:学科担纲总有很多班级,班级当中又有很多学生

    public static void main(String[] args) {

        Person per1 = new Person("zs");
        Person per2 = new Person("ls");
        //班级1
        List<Person> c1 = new ArrayList<>();
        c1.add(per1);
        c1.add(per2);
        
        Person per3 = new Person("zs1");
        Person per4 = new Person("ls2");
        //班级2
        List<Person> c2 = new ArrayList<>();
        c2.add(per3);
        c2.add(per4);
        
        //学科(集合当中又存储集合)
        List<List<Person>> x = new ArrayList<>();
        x.add(c1);
        x.add(c2);
        
        //把所有班级当中的学生姓名打印出来
        for (List<Person> g : x) {
            //取出每一个班级
            for (Person person : g) {
                System.out.println(person.name);
            }
        }
        
    }

Set集合特性演示

set当中存的元素是无序的,里面没有重复的元素
set已经覆盖了toString方法

        HashSet<String> hs = new HashSet<>();
        boolean res1 = hs.add("a");
        boolean res2 = hs.add("a");//添加不成功
        hs.add("b");
        hs.add("c");
        hs.add("1");
        System.out.println(res1);//true
        System.out.println(res2);//false
        System.out.println(hs);//[a, 1, b, c]无序存放排列
        
        Iterator it = hs.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }//a 1 b c
        

能够使用迭代器都可以使用增强for循环(foreach)

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

Set自定义对象相等判断

想要在Set中自定义对象中去重
1.覆盖equals方法
2.覆盖hasCode方法
​ 如果两个自定义对象,属性完全相同,那么就判定你是同一个对象

public class Demo1 {

    public static void main(String[] args) {
        
        Set<Person> s  =new HashSet<>();
        s.add(new Person("zs",19));
        s.add(new Person("zs",19));
        s.add(new Person("ls",30));
        s.add(new Person("ls",30));
        s.add(new Person("ls",30));
        System.out.println(s.toString());//[Person [name=zs, age=19], Person [name=ls, age=30]]

    }

}
class Person{
    String name;
    int age;

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

    @Override//覆盖toString方法
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    @Override//覆盖hashCode
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override//覆盖equals
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    
}

hashCode

每一个对象都会有一个hashCode

hashCode就是跟内存地址对应的一个编号
编号就是对应你内存的地址
每一个对象的hashCode都是不一样的

        Cat c = new Cat("mm");
        System.out.println(c);
        System.out.println(c.hashCode());

不覆盖hashCode会帮你生成一个唯一的值

    @Override
    public int hashCode() {
        return 10;
    }

HashSet去重原理分析

class Cat {
    String name;

    Cat(String name) {
        this.name = name;
    }

    // 不覆盖hashCode会帮你生成一个唯一的值(每次生成的值都不一样)
    
    /*@Override
     * public int hashCode() {
        System.out.println("执行了hashCode");
        return 10;
    }
    public boolean equals(Object obj) {
        System.out.println("执行了equals");
        return false;
    }*/
    

选择系统覆盖hashCode和equals方法

    @Override
    public int hashCode() {
        //如果属性相同,返回相同的hashCode
        //属性不同,hashCode就不相同
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Cat other = (Cat) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}
        Cat c1 = new Cat("mm");
        Cat c2 = new Cat("mm");
        Set<Cat> s = new HashSet<>();

添加一个对象时,会调用对象的hashCode
hashCode不相同的时候,c2对象和集合当中的hashCode不相同,就不会调用equals
当hashCode值相同的时候才会去调用equals方法 如果equals返回为true,就不添加到集合当中

        s.add(c1);
        s.add(c2);
        System.out.println(s);//equals为false的时候,会添加元素进去[集合.泛型.Cat@a, 集合.泛型.Cat@a]

LinkedHashSet

LinkedHashSet是HashSet的子类

它底层是用链表实现的,它是set集合当中,唯一一个保证元素是怎么存怎么取出来的

HashSet 能够保证元素的唯一性

        HashSet<String> set1 = new HashSet<>();
        set1.add("a");
        set1.add("b");
        set1.add("1");
        set1.add("e");
        System.out.println(set1);//使用普通的HashSet,顺序会掉乱[a, 1, b, e]
        
        LinkedHashSet<String> set2 = new LinkedHashSet<>();
        set2.add("a");
        set2.add("1");
        set2.add("c");
        set2.add("e");
        System.out.println(set2);//[a, 1, c, e]

生成1到20之间不重复的随机数

获取十个随机数
不允许有重复

        //1.使用Random来生成随机数 创建Random
        Random r = new Random();
        //2.创建存放生成结果的集合,HashSet
        HashSet<Integer> hs = new HashSet<>();
        //3.当HashSet.size大于10的时候,就不放,否则一直生成往里面放
        while(hs.size()<10) {
            //4.生成1到20之间的随机数
            int res = r.nextInt(20)+1;//nextInt(20)生成的是0-19的随机整数
            //5.添加到集合当中
            hs.add(res);
        }
        System.out.println(hs);//[1, 17, 18, 3, 5, 6, 7, 11, 12, 15]

去除重复字符串练习

        //aaabbbccc
        //abc
        
        //1.获取从键盘上输入的字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = sc.nextLine();
        //2.拿字符串当中的每一个字符
        char[] arr = line.toCharArray();
        System.out.println(Arrays.toString(arr));
        //3.取出每一个元素,添加到一个可以去重的集合当中
        LinkedHashSet<Character> hs = new LinkedHashSet<>();
        for(int i = 0 ; i<arr.length;i++) {
            char c = arr[i];//取出数组当中的所有元素
            hs.add(c);
        }

TreeSet

Tree它是无序的(不是按照你添加的顺序展示的)
对添加的元素进行排序
TreeSet是用来对元素进行排序,里面也是可以保证元素唯一

        //按数字排序
        TreeSet<Integer> set = new TreeSet<Integer>();
        set.add(10);
        set.add(2);
        boolean res = set.add(2);
        System.out.println(res);//false
        set.add(9);
        set.add(6);
        System.out.println(set);//[2, 6, 9, 10]

        //按字母排序
        TreeSet<String> set2 = new TreeSet<String>();
        set2.add("a");
        set2.add("b");
        set2.add("d");
        set2.add("c");
        set2.add("g");
        System.out.println(set2);//[a, b, c, d, g]

        //汉字是按照unicode-->里面已经包含了Assic码
        TreeSet<String> set3 = new TreeSet<String>();
        set3.add("我");
        set3.add("的");
        set3.add("名");
        set3.add("字");
        System.out.println(set3);//[名, 字, 我, 的]

        System.out.println('我' + 0);//25105
        System.out.println('的' + 0);//30340
        System.out.println('名' + 0);//21517
        System.out.println('字' + 0);//23383

        //按字母排序,比较了前面,前面相同再比后面
        TreeSet<String> set4 = new TreeSet<String>();
        set4.add("aa");
        set4.add("ac");
        set4.add("aba");
        System.out.println(set4);//[aa, aba, ac]

TreeSet添加自定义对象

class Person implements Comparable<Person>{
    String name;
    int age;
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    //如果返回0的话,只添加一个元素
    //如果是一个正数,都能添加到集合当中,顺序怎么添加的,怎么显示
    //如果是一个负数,顺序怎么添加的,倒序显示(第一个添加的,显示到最后)
    @Override
    public int compareTo(Person o) {
        return -1;
    }
    
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    
}
public class Demo1 {

    public static void main(String[] args) {

        //TreeSet当中存放的类型必须得是同一类型
        //TreeSet set = new TreeSet<>();
        //set.add(10);
        //set.add("a");
        
        //自定义对象不能直接添加到TreeSet当中
        //想要添加到TreeSet当中必须得要遵守一定的规则
        //实现接口(Comparable)
        //还得覆盖当中的compareTo方法
        TreeSet<Person> set = new TreeSet<>();
        set.add(new Person("zs",20));
        set.add(new Person("ls",21));
        set.add(new Person("ww",22));
        set.add(new Person("zl",23));
        System.out.println(set);
        //retrun为0的时候,[Person [name=zs, age=20]]只能显示头个元素
        //把return的结果改为任何一个正数,能全部显示在集合当中
    }
}

TreeSet二叉树原理分析

二叉树:在数据结构中,就像一棵树发叉,根节点线下衍生子节点,一个节点发两个叉



TreeSet自定义对象属性排序

想要进行自定义对象排序,对象必须实现接口Comparable,覆盖comparaTo方法

class Person implements Comparable<Person> {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Person obj) {
        //进行年龄排序
        int num = this.age - obj.age;//this.age当前的age,减去下一个传入来的age的值
        /*if (num == 0) {//如果num的值为0
            return this.name.compareTo(obj.name);//再进行名字的比较
        } else {
            return num;//非0直接返回num值
        }*/
        return num == 0 ? this.name.compareTo(obj.name): num;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}
需要进行年龄排序
        TreeSet<Person> set = new TreeSet<>();
        set.add(new Person("张三", 20));
        set.add(new Person("李四", 25));
        set.add(new Person("王五", 22));
        set.add(new Person("赵六", 21));
        set.add(new Person("赵六1", 21));
        System.out.println(set);
        //[Person [name=张三, age=20], Person [name=赵六, age=21], Person [name=赵六1, age=21], Person [name=王五, age=22], Person [name=李四, age=25]]

TreeSet比较器

默认情况下,比较是会调用对象的compareTo进行比较

如果传入了比较器,就不会调用compareTo,就会使用你传入的比较器进行比较

默认是使用字母的顺序进行比较的(compareTo)

        TreeSet<String> set = new TreeSet<String>();
        set.add("aaaaa");
        set.add("z");
        set.add("wc");
        set.add("wc");
        set.add("wzb");
        set.add("nba");
        System.out.println(set);
        
        //使用比较器进行比较,比较字符串的长度排序
        //1.实现一个接口comparator
        //2.定义一个类来去实现这个接口
        //3.覆盖它里面的方法
        TreeSet<String> set2 = new TreeSet<String>(new ComparaLength());//需要传入比较器对象
        set2.add("aaaaa");
        set2.add("z");
        set2.add("wc");
        set2.add("wc");
        set2.add("wzb");
        set2.add("nba");
        System.out.println(set2);//[z, wc, nba, wzb, aaaaa]
class ComparaLength implements Comparator<String>{
    @Override
    //1.代表当前正在添加的对象
    //2.代表集合当中的对象
    public int compare(String o1, String o2) {
        System.out.println("调用了比较器当中的compare");
        int lenght = o1.length() - o2.length();//主要是比长度
        return lenght == 0 ?o1.compareTo(o2):lenght ;//如果长度相等,再去比内容
    }
}

Map映射关系概述

Map:映射关系

A集合 B集合 (ArrayList LinkedList Vector HashSet LinkedHashSet TreeSet)

A集合当中的元素不能重复(Set),A集合当中的每一个元素称它是一个Key

A集合当中的每一个元素,都可以在B集合当中找到一个唯一的一个值与之对应

B集合当中的元素可以重复(list),B集合当中的每一个元素称它是一个Value

Map当作是一个字典(dict)

Key-Value(entry)



Map添加元素

往AB两个集合当中添加元素,并且关联在一起(value会跟着key走)
map当中的元素并不是有序的
key1=value1 entry键值对

        Map<String,Object> map = new HashMap<String,Object>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        map.put("key3", "value3");
        map.put("key4", "value4");
        map.put("key5", "value5");
        System.out.println(map);//{key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
        
        //获取A集合
        Set<String> set =  map.keySet();
        System.out.println(set);//[key1, key2, key5, key3, key4]
        
        //获取B集合
        Collection<Object> values= map.values();
        System.out.println(values);//[value1, value2, value5, value3, value4]

Map修改元素

        Map<String,Object> map =  new HashMap<>();
        //添加功能
        Object res1 = map.put("张三", 20);
        Object res2 = map.put("李四", 21);
        Object res3 = map.put("王五", 22);
        System.out.println(map);//{李四=21, 张三=20, 王五=22}
        //如果Key值是第一次添加,返回值就是null
        System.out.println(res1);
        System.out.println(res2);
        System.out.println(res3);//null
        //添加的Key并不是第一次存在,就用value把以前的值给替换掉,返回以前的值
        Object res4 = map.put("张三", 30);
        System.out.println(map);//{李四=21, 张三=30, 王五=22}
        System.out.println(res4);//20

Map常用方法

1.添加功能

2.删除功能

        //2.1可以根据key来删除元素(key-value)
        map.remove("张三");//{李四=21, 王五=22}
        System.out.println(map);

        //2.2清空map当中所有的key-value
        map.clear();
        System.out.println(map);//{} 里面一个元素都没有了
        
        //3.长度功能,查看有几个键值对
        //map当中有几个key-value
        System.out.println(map.size());//0

Map获取元素及遍历元素

        // 双列集合
        Map<String, Object> map = new HashMap<>();
        Object res1 = map.put("张三", 20);
        Object res2 = map.put("李四", 21);
        Object res3 = map.put("王五", 22);

获取元素
1.获取一个元素
根据相关的key可以获取相关联的value

        Object values = map.get("张三");
        System.out.println(values);// 20

2.获取所有元素(遍历)
map当中是没有迭代器,集合没有迭代器,你就不能用快速遍历(foreach)

        // 取出所有的key
        Set<String> allKeys = map.keySet();//map.keySet()  获取全部key返回Set装

        Iterator<String> it = allKeys.iterator();
        while (it.hasNext()) {
            // 取出key值
            String key = it.next();
            Object val = map.get(key);
            System.out.println( key + "="+val);
            //李四=21,张三=20,王五=22
        }
        
        //set能使用迭代器,就能使用foreach
        for (String key : allKeys) {
            System.out.println(key+"="+map.get(key));
        }

Map通过entry对象遍历元素

        // 双列集合
        Map<String, Object> map = new HashMap<>();
        Object res1 = map.put("张三", 20);
        Object res2 = map.put("李四", 21);
        Object res3 = map.put("王五", 22);
        
        //获取所有的key-value对象entry
        Set<Map.Entry<K, V>> set =  map.entrySet();

相关文章

  • 面向对象-泛型

    面向对象-泛型 没有泛型存在的问题 1.往集合当中存储元素,可以存储任何类型元素 2.没有办法约束集合当中只能存储...

  • java 泛型解析

    Java 泛型 1、泛型的精髓是什么 2、泛型方法如何使用 概述: 泛型在java中具有重要地位,在面向对象编程模...

  • GLSL常用内建函数

    genType可以理解为泛型 genType pow(genType x)genType有点像面向对象中泛型,即如...

  • 8.泛型

    泛型历史和概述 泛型发展 泛型程序最早出现1970年代的CLU和Ada语言中,后来被许多基于对象和面向对象的语言所...

  • Swift语法学习

    1.Swift既支持面向过程的编程机制,也支持面向对象的编程方式。(c++支持泛型编程,mark泛型编程) 2.S...

  • TS 面向对象 -- 泛型

    泛型generic:参数化的类型,一般用来限制集合的内容 class News { ... ... }class ...

  • 面向对象(十二)-泛型

    1. 简介: 2.0 版 C# 语言和公共语言运行时 (CLR) 中增加了泛型。 泛型将类型参数的概念引入 .NE...

  • 泛型方法

    主题 本节主要讲解泛型方法 泛型方法 泛型是反面向对象的一种性质(anti-oop) 本质上是abstract(请...

  • 面向指针编程(一)

    面向对象编程,面向设计模式编程(亦即设计模式),面向接口编程,面向模板编程(亦即泛型编程),面向函数编程(亦即函数...

  • C++ STL与泛型编程-第二篇 (Boolan)

    C++ STL与泛型编程-第二篇 (Boolan) 本章内容:1 OOP(面向对象编程) vs. GP(泛型编程)...

网友评论

    本文标题:面向对象-泛型

    本文链接:https://www.haomeiwen.com/subject/svplcqtx.html