Java——集合笔记

一、集合概述

为了在程序中保存数目不确定的对象,Java提供了一系列特殊的类,这些类可以存储任意类型的对象,并且长度可变,这些类统称为集合

二、Collection接口

Collection是所有单列集合的父接口,它定义了单列集合(List和Set)通用的一些方法。

Collection接口的常用方法

boolean add(Object o) //向集合添加一个元素
boolean addAll(Collection c) //将指定Collection中所有元素添加到该集合中
void clear() //删除该集合中所有的元素
boolean remove(Object o) //删除该集合中的指定元素
boolean removeAll(Collection c) //删除指定集合中的所有元素
boolean isEmpty() //判断该集合是否为空
boolean contains(Object o) //判断该集合中是否包含某个元素
boolean containsAll(Collection c) //判断该集合中是否包含指定集合中的所有元素
Iterator iterator() //返回在该集合的元素上进行迭代的迭代器,用于遍历该集合所有元素
int size() //获取该集合元素个数

三、List接口

3.1 List接口简介

List接口继承自Collection接口,是单列集合的一个重要分支。不但继承了Collection接口中的全部方法,还增加了一些根据元素索引操作集合的特有方法

List集合的常用方法

void add(int index,Object element) //将元素element插入在List集合的index处
boolean addAll(int index,Collection c) //将集合C所包含的悠悠元素插入到List集合的index处
Object get(int index) //返回集合索引index处的元素
Object remove(int index) //删除集合索引index处的元素
Object set(int index,Object element) //将集合索引index处的元素替换成element对象,并将替换后的元素返回。
int indexOf(Object o) //返回对象o在List集合中出现的位置索引
int lastIndex(Object o) //返回对象o在List集合中最后一次出现的位置索引
List subList(int fromIndex,int toIndex) //返回从索引fromIndex(包括)到toIndex(不包括)处的所有元素组成的子集合。

3.2 ArrayList集合

ArrayList是List接口的一个实现类,他是程序中最常见的一种集合,在ArrayList内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,因此可以将ArrayList集合看作一个长度可变的数组。

优缺点:

ArrayList集合的底层使用一个数组来保存元素,在增加或删除指定位置的元素时,会创建新的数组,效率比较低,所以不适合做大量的增加或删除操作。但因为这种数组的结构允许程序通过索引的方式来访问,所以使用ArrayList集合查找元素很便捷。

ArrayList集合中大部分方法都是从父类Collection和List继承过来的。

【案例】

import java.util.ArrayList;
public class test4 {
    public static void main(String[] args){
       ArrayList list=new ArrayList<>(); //创建ArrayList集合
       list.add("张三");
       list.add("李四");
       list.add("王五");
       list.add("赵六");
       //获取集合中的元素个数
       System.out.println("集合的长度:"+list.size());
       //取出并打印指定位置的元素,从0开始
       System.out.println("第2个元素是:"+list.get(1));
    }   
}

【运行结果】

 3.3 LinkedList集合

因为ArrayList集合在查询元素时速度很快,但在增加或删除元素时效效率比较低,所以为了克服这种局限性,可以使用另外一个实现类LinkedList。

LinkedList集合内部维护了一个双向循环链表,从而可以将所有的元素彼此连接起来。当插入一个新元素时,只需要修改元素之间的这种引用关系即可,删除一个节点也是如此。

LinkedList集合增加和删除元素的特有方法

void add(in index,E element) //在此集合中指定位置插入指定元素
void addFirst(Object o) //将指定元素插入此集合的开头
void addList(Object o) //将指定元素添加到此集合的结尾
Object getFirst() //返回此集合的第一个元素
Object getLast() //返回此集合的最后一个元素
Object removeFirst() //删除并返回此集合的第一个元素
Object removeLast() //删除并返回此集合的最后一个元素

【案例】


import java.util.LinkedList;
public class test4 {
    public static void main(String[] args){
       LinkedList link = new LinkedList<>(); //创建LinkedList集合
       link.add("张三");
       link.add("李四");
       link.add("王五");
       link.add("赵六");
       System.out.println(link.toString());//取出并打印该集合中的元素
       link.add(3,"student"); //向该集合中指定位置插入元素
       link.addFirst("First"); //向集合第一个位置插入元素
       System.out.println(link);
       System.out.println(link.getFirst()); //取出该集合中第一个元素
       link.remove(3); //删除该集合中指定位置的元素
       link.removeFirst(); //删除该集合中第一个元素
       System.out.println(link);
       
    }   
}

【运行结果】

由此可见使用ListedList对元素进行增加和删除操作是非常便捷的

3.4 Iterator 接口 

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,Java专门提供了一个接口Iterator,Iterator 接口 也是集合中的一员,但他与Collection、Map接口有所不同。Collection和Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素。

【案例】

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

public class test4 {
    public static void main(String[] args){
       ArrayList list=new ArrayList<>(); //创建ArrayList集合
       list.add("张三");
       list.add("李四");
       list.add("王五");
       list.add("赵六");
       Iterator it=list.iterator(); //获取Iterator对象
       while (it.hasNext()){  //判断ArrayList集合中中是否存在下一个元素
           Object obj=it.next(); //取出ArrayList集合中的元素
           System.out.println(obj); 
       }

    }
    
}

在调用 Iterator 的next()方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next()方法后,迭代器的索引会向后位移一位,指向第一个元素并将该元素返回,依次类推。

【运行代码】

 要想在使用Iterator迭代器对集合中的元素进行迭代时,如果调用了对象的remove()方法删除元素之后,继续使用迭代器遍历元素,会出现异常,解决方法如下:

【案例】

【运行代码】

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

public class test4 {
    public static void main(String[] args){
       ArrayList list=new ArrayList<>(); //创建ArrayList集合
       list.add("张三");
       list.add("李四");
       list.add("王五");
       list.add("赵六");
       Iterator it=list.iterator(); //获取Iterator对象
       while (it.hasNext()){  //判断ArrayList集合中中是否存在下一个元素
           Object obj=it.next(); //取出ArrayList集合中的元素
           if("张三".equals(obj)){
               //方法一:迭代器本身的删除方法
               //it.remove();
               //方法二:找到该学生直接退出
               list.remove(obj);
               break;
           }
       }
       System.out.println(list); 
    }
}

【运行结果】

可以观察到,张三已经被删除掉了

3.5 foreach循环

虽然Iterator可以用来遍历集合中的元素,但写法上比较繁琐,所以引进了foreach循环。foreach循环被用于遍历数组或集合中的元素,语法如下:

for(容器中元素类型 临时变量:容器变量){
    执行语句
}

 【案例】

相比Iterator迭代器确实方便不少

【运行代码】

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

public class test4 {
    public static void main(String[] args){
       ArrayList list=new ArrayList<>(); //创建ArrayList集合
       list.add("张三");
       list.add("李四");
       list.add("王五");
       list.add("赵六");
       for(Object obj:list){ //使用foreach循环遍历ArrayList对象
           System.out.println(obj); //取出并打印ArrayList集合中的元素
       }
    }
}

【运行结果】

forerch循环虽然书写起来很简洁,但使用过程中也有一定的局限性。当使用foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改

【案例】演示foreach循环的局限性

【运行代码】

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

public class test4 {
    static String[] strs={"aaa","bbb","ccc"};
    public static void main(String[] args){
        //foreach循环遍历数组
        for(String str:strs){
            str="ddd";
        }
        System.out.println("foreach循环修改后的数组:"+strs[0]+","+strs[1]+","+strs[2]);

        //for循环遍历数组
        for(int i=0;i<strs.length;i++){
            strs[i]="ddd";
        }
        System.out.println("foreach循环修改后的数组:"+strs[0]+","+strs[1]+","+strs[2]);
        
      
    }
}

【运行结果】

分别使用foreach循环和普通的for循环去修改数组中的元素。从结果来看foreach循环并不能修改数组中的元素的值。原因是第6行代码中的str=“ddd”只是将临时变量的str指向了一个新的字符串,与数组中的元素没有一点关系,而在普通for循环中,是可以通过索引的方式来引用数组中的元素并对其值进行修改的。

四、Set接口

4.1 Set接口简介

        Set接口和List接口一样,同样是继承自Collection接口,与Collection接口的方法基本一致,并没有对Collection接口进行功能扩充,只是比Collection接口更加严格,与List接口不同的是,Set接口中的元素是无序的,并且都会以某种规则保证存入的元素不出现重复。

        Set接口主要由两个实现类,分别为HashSet和TreeSet。其中,HashSet是根据对象的散列值来确定元素在集合中存储的位置,具有良好的存取和查找性能。TreeSet则是以二叉树的方式来存储,他可以实现对集合中的元素进行排序。

4.2 HashSet

        HashSet是Set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的。

【案例】

import java.util.*;public class text{
    public static void main(String[] args){
        HashSet set=new HashSet<>(); //创建HashSet集合
        set.add("张三"); //向该set集合中添加字符串
        set.add("李四");
        set.add("王五");
        set.add("张三"); //向该set集合中添加重复元素
        Iterator it=set.iterator(); //获取Iterator对象
        while (it.hasNext()){       //通过while循环,判断集合中是否有元素
            Object obj=it.next();   // 如果有元素,就通过迭代器的next()方法获取元素
            System.out.println(obj);
        }
    }
}

【运行结果】

通过结果可以观察到取出的元素的顺序与添加元素的顺序并不一致,并且重复存入的字符串对象“张三”被去除了,只添加了一次 。

4.2.1 自定义Student对象存入HashSet

        为了保证HashSet正常工作,要求在存入对象时,重写Object类中的hashCode()和equals()。在上面的实验中,将字符串存入HashSet时,String类已经重写了hashCode()和equals()方法,下面通过一个案例演示向HashSet自定义存储Student对象

【案例】

import java.util.*;
class Student{
    String id;
    String name;
    public Student(String id,String name){ //创建构造方法
        this.id=id;
        this.name=name;
    }
    public String toString(){       //重写toString()方法
        return id+":"+name;
    }
}
public class text{
    public static void main(String[] args){
        HashSet hs=new HashSet<>(); //创建HashSet集合
        Student stu1=new Student("1", "张三"); //创建Student对象
        Student stu2=new Student("2", "李四");
        Student stu3=new Student("2", "李四");
        hs.add(stu1);
        hs.add(stu2);
        hs.add(stu3);
        System.out.println(hs);
        }
    }

【运行结果】

观察到运行结果出现了两个相同的学生信息“2李四”,这样的学生的信息应该被视为重复元素,应该不能出现在HashSet集合中。之所以没有去掉这样的重复元素,是因为在定义Student类时没有重写hashCode()和equals()方法,下面进行完善。

【完善后的代码】

import java.util.*;
class Student{
    String id;
    String name;
    public Student(String id,String name){ //创建构造方法
        this.id=id;
        this.name=name;
    }
    public String toString(){       //重写toString()方法
        return id+":"+name;
    }
    public int hashCode(){          //重写hashCode方法
        return id.hashCode();       //返回id属性的散列值
    }
    //重写equals方法
    public boolean equals(Object obj){
        if(this==obj){    //判断是否是同一个对象
            return true;  //如果是,直接返回true
        }
        if(!(obj instanceof Student)){ //判断对象是否为Student类型
            return false;
        }
        Student stu = (Student) obj; //将对象强制转换为Student类型
        boolean b =this.id.equals(stu.id); //判断id值是否相同
        return b;   //返回判断结果
    }
}
public class text{
    public static void main(String[] args){
        HashSet hs=new HashSet<>(); //创建HashSet集合
        Student stu1=new Student("1", "张三"); //创建Student对象
        Student stu2=new Student("2", "李四");
        Student stu3=new Student("2", "李四");
        hs.add(stu1);
        hs.add(stu2);
        hs.add(stu3);
        System.out.println(hs);
        }
    }

 【运行结果】

在hashCode()方法中返回id属性的散列值,在equals()方法中比较对象的id属性是否相同,并返回结果。当调用HashSet集合的add()方法添加stu3对象时,发现他的散列值与stu2对象相同,而且stu2.equals(stu3)返回true,HashSet集合就默认这两个对象是相同的,于是重复的对象被成功删除了。

4.2.2 让HashSet集合存储元素有序

        如果想让元素的存储顺序一致,可以使用java中提供的LinkedHashSet类, LinkedHashSet类是HashSet的子类,与LinkedList一样,它也使用双向链表来维护内部的元素的关系。

【案例】

import java.util.*;public class text{
    public static void main(String[] args){
        LinkedHashSet hs=new LinkedHashSet<>();
        hs.add("张三");
        hs.add("李四");
        hs.add("王五");
        Iterator it =hs.iterator(); //获取Iterator对象
        while(it.hasNext()){
            Object obj=it.next();
            System.out.println(obj);
        }
        }
    }

【运行结果】

这样输出的结果就与输入的一样了

4.3 TreeSet集合

上面学习了HashSet集合存储的元素是无序的且不可重复,为了对集合中的元素进行排序,Set接口提供了另外一个可以对HashSet集合中的元素 进行排序的类——TreeSet

【实验案例】 

import java.util.*;
public class text{
    public static void main(String[] args){
        TreeSet ts=new TreeSet();
        ts.add(1);
        ts.add(3);
        ts.add(2);
        ts.add(3);
        ts.add(5);
        ts.add(2);
        System.out.println(ts);
        }
}

【运行结果】

​ 

可以观察到添加的元素已经自动排序并且重复存入的整数只添加了一次。

4.3.1 TreeSet的原理

        TreeSet集合之所以可以对添加的元素进行排序,是因为元素的类可以实现Comparable接口(基本类型的包装类,String类都实现了该接口),comparable接口强行对实现他的每一个类的对象进行整体排序,这种排序成为类的自然排序。Comparable接口的compareTo()方法称为自然比较方法。如果将自定义的Student对象存入TreeSet,TreeSet将不会对添加的元素进行排序,Student对象必须实现comparable接口并重写compareTo()方法实现对象元素的顺序存取

【案例】

import java.util.*;class Student implements Comparable<Student>{
    private String id;
    private String name;
    public Student(String id,String name){
        this.id=id;
        this.name=name;
    }
    //重写toString()方法
    public String toString(){
        return id+":"+name;
    }
    public int compareTo (Student o){
        // return 1;  //集合按照怎么存就怎么取
        return -1;  //集合按照存入元素的倒序进行存储
    }
}public class text{
    public static void main(String[] args){
        TreeSet ts=new TreeSet();
        ts.add(new Student("1", "张三"));
        ts.add(new Student("2", "李四"));
        ts.add(new Student("2", "王五"));
        System.out.println(ts);
        }
}

【运行结果】

5、Map接口

        5.1 Map接口简介

                Map接口是一种双列集合,它的每一个元素都包含一个键对象Key和值对象Value,键和值对象之间存在一种对应的关系,称为映射。从map集合中访问元素时,只要指定了Key,就能找到对应的Value。

Map接口常用的方法

 HashMap集合是Map接口的一个实现类,用于存储键值映射关系,但HashMap集合没有重复的键且键值无序。

【案例】

import java.util.HashMap;

public class text1 {
    public static void main(String[] args){
        HashMap map = new HashMap();  //创建Map对象
        map.put("1","张三");          //存储键和值
        map.put("2","李四");
        map.put("3","王五");
        System.out.println("1:"+map.get("1"));  //根据键获取值
        System.out.println("2:"+map.get("2"));
        System.out.println("3:"+map.get("3"));

    }
}

【运行结果】

因为Map集合中的键具有唯一性,所以现在向Map集合中再存储一个相同的键时会覆盖掉之前存储的键值。

map.put("3","赵六");

5.2 两种遍历Map中所有键和值的方法

5.2.1 方法一:先遍历Map集合中所有的键,再根据键获取相应的值。

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

public class text1 {
    public static void main(String[] args){
        HashMap map = new HashMap();  //创建Map集合
        map.put("1","张三");    //存储键和值
        map.put("2","李四");
        map.put("3","王五");
        Set keySet = map.keySet();   //获取键的集合
        Iterator it=keySet.iterator();  //迭代键的集合
        while (it.hasNext()){
            Object key=it.next();
            Object value=map.get(key);  //获取每个键所对应的值
            System.out.println(key+":"+value);
        }
    }
}

【运行结果】

5.2.2 方法二 先获取集合中的所有的映射关系,然后从映射关系中取出键和值。

【案例】

首先调用Map对象的entrySet()方法获得存储在Map中的所有映射的Set集合,这个集合中存放了Map.Entry类型的元素(Entry是Map内部接口),每个Map.Entry对象代表Map中的一个键值对,然后迭代Set集合,获得每一个映射对象,并分别调用映射对象的getKey()和getValue()方法获取的键和值。

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

public class text1 {
    public static void main(String[] args){
        HashMap map = new HashMap();
        map.put("1","张三");
        map.put("2","李四");
        map.put("3","王五");
        Set entrySet=map.entrySet();
        Iterator it=entrySet.iterator(); // 获取Iterator对象
        while (it.hasNext()){
            Map.Entry entry=(Map.Entry)(it.next());
            Object key=entry.getKey(); // 获取Entry中的键
            Object value=entry.getValue(); //获取Entry中的值
            System.out.println(key+":"+value);
        }
    }
}

【运行结果】

 5.3 操作集合的常用方法

values()方法用于得到Map实例中所有的value,返回值类型为Collection;size()方法获取Map集合类的大小;containKey()方法用于判断是否包含传入的键;containsValue()方法用于判断是否包含传入的值;remove()方法用于根据key删除Map中的与该key对应的value等。

【案例】

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

public class text1 {
    public static void main(String[] args){
        HashMap map = new HashMap();
        map.put("1","张三");
        map.put("2","李四");
        map.put("3","王五");
        map.put("4","赵六");
        System.out.println("集合大小为:"+map.size());
        System.out.println("判断是否包含传入的键"+map.containsKey("2"));
        System.out.println("判断是否包含传入的值"+map.containsValue("王五"));
        System.out.println("删除键为1的值是"+map.remove("1"));
        System.out.println("删除键后集合大小为:"+map.size());
        Collection values=map.values();
        Iterator it=values.iterator(); // 获取Iterator对象
        while (it.hasNext()){
            Object value=it.next(); //获取Entry中的值
            System.out.println(value);
        }
    }
}

 【运行结果】

5.4 LinkedHashMap

跟HashSet一样,HashMap集合所迭代出来的元素顺序和存入的顺序是不一致的,如果想让这两个顺序一致,可以使用java中提供的LinkedHashMap类,他是HashMap的子类,与LinkedList一样,它也是用双向链表来维护内部元素的关系。

【案例】

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

public class text1 {
    public static void main(String[] args){
        LinkedHashMap map=new LinkedHashMap<>(); //创建map集合
        map.put("1","张三");
        map.put("2","李四");
        map.put("3","王五");
        map.put("4","赵六");
        Set keySet=map.keySet();
        Iterator it=keySet.iterator(); // 获取Iterator对象
        while (it.hasNext()){
            Object key=it.next(); //获取Entry中的值
            Object value=map.get(key);
            System.out.println(key+":"+value);
        }
    }
}

 【运行结果】

现在元素迭代出来的顺序和存入的顺序是一致的。

 5.5 TreeMap集合

HashMap集合存储的元素的键值是无序且不可重复的,所以引进TreeMap集合对集合中的元素的键值进行排序。

【案例】

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class text1 {
    public static void main(String[] args){
        TreeMap map=new TreeMap<>();  //创建Map集合
        map.put(3,"李四"); //存储键和值
        map.put(2,"王五");
        map.put(4,"赵六");
        map.put(3,"张三");
        Set keySet=map.keySet();
        Iterator it=keySet.iterator();
        while (it.hasNext()){
            Object key=it.next();
            Object value=map.get(key); //获取每个键所对应的值
            System.out.println(key+":"+value);
        }
    }
}

【运行结果】

和TreeSet一样,TreeMap中的键必须是唯一的,不能重复且有序,如果键相同,则后面的值覆盖前面的值。

5.5.1 比较排序法实现按键值排序

import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;

class Student{
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(){
        this.age=age;
    }
    public Student(String name,int age){
        super();
        this.name=name;
        this.age=age;
    }
    public String toString(){
        return "Student [name="+name+",age="+age+"]";
    }
}
public class text1 {
    public static void main(String[] args){
        TreeMap tm=new TreeMap(new Comparator<Student>() {
            public int compare(Student s1,Student s2){
                int num = s1.getName().compareTo(s2.getName());//按照姓名比较
                return num==0?num:s1.getAge()-s2.getAge();
            }
        });
        tm.put(new Student("张三", 23),"北京");
        tm.put(new Student("李四", 13),"上海");
        tm.put(new Student("赵六", 43),"深圳");
        tm.put(new Student("王五", 33),"广州");
        Set keySet = tm.keySet();
        Iterator it = keySet.iterator();
        while (it.hasNext()){
            Object key=it.next();
            Object value=tm.get(key);  //获取每个键所对应的值
            System.out.println(key+":"+value);
        }

    }
}

【运行结果】

29~35行代码定义了一个TreeMap集合,并在该集合中通过匿名内部类的方式实现了Comparator接口,然后重写了compare()方法,在compare()方法中通过三目运算符自定义了排序方式为先按照年龄排序,年龄相同再按照姓名排序;第36~46行代码打印输出。

5.6 Properties集合

Map接口中还有一个实现类Hashtable,它与HashMap十分相似,区别在于Hashtable是线程安全的。Hashtable存取元素时速度很慢,目前基本被HashMap类所取代,但Hashtable类的子类Properties在实际应用中非常重要。

Properties主要用来存储字符串类型的键和值。在实际开发中,经常使用Properties集合来存取应用的配置项。假设有一个文本编辑工具,要求默认背景色的红色,字体大小为14px,语言为中文,其配置项的代码如下:

Backgroup-color=red
Font-size=14px
Language=chinese

【案例】

import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

public class text1 {
    public static void main(String[] args){
        Properties p=new Properties();
        p.setProperty("Backgroup-color", "red"); //创建Properties
        p.setProperty("Font-size", "14px");
        p.setProperty("Language", "chinese");
        Enumeration names=p.propertyNames();//获取Enumeration对象所有键的枚举
        while (names.hasMoreElements()){   //循环遍历所有的键
            String key=(String)names.nextElement(); 
            String value=p.getProperty(key); //获取对应键的值
            System.out.println(key+"="+value);
        }
    }
}

【运行结果】

使用了Properties类中针对字符串存取的两个专用方法setProperty()和getProperty()。setProperty()方法用于将配置项中的键和值添加到Properties集合中。在第7行代码中通过调用Properties的propertyNames()方法得到一个包含所有键的Enumeration对象,然后在遍历所有的键时,通过调用getProperty()方法获得键所对应的值

6. 泛型

6.1 泛型概述

泛型是程序设计语言的一种特性,允许程序员在使用强类型程序设计语言编写代码时定义一些可变部分,这些可变部分在运行前必须做出指明。在编程中用泛型 来代替某个实际的类型,而后通过实际调用时传入或推导的类型来对泛型进行替换,以达到代码复用的目的。

public class text1<T>{
    private T value;
    public void set(T value){
        this.value=value;
    }
    public T get(){
        return value;
    }
}

上述代码中,Box类在定义时使用了"<T>"的形式,T表示此类型是又外部调用本类时指定的。这样,在实例化类对象时可以传入除基础数据类型以外的任意类型数据,使其具有良好的通用性。

6.2 泛型类和泛型对象

泛型类就是在类声明时通过一个标识表示类中某个属性的类型或者某个方法的返回值和参数类型

【案例】

import java.util.*;
public class text1{
    public static void main (String[] args){
        ArrayList list = new ArrayList<>(); //创建ArrayList集合
        list.add("String");     //添加字符串对象
        list.add(2);            //添加Integer
        list.add(1);
        for (Object obj:list){     //遍历集合
            String str = (String)obj;//强行转为String类型
        }

    }
}

【运行结果】

向list集合中存入3个元素,分别是一个字符串和两个int类型的整数,在取出这些元素的时候,都将他们强制转为String类型,由于Integer对象无法转换为String类型,所以程序在运行时会报错。

 所以此时使用泛型可以很好的解决上面的问题

import java.util.*;
public class text1{
    public static void main (String[] args){
        ArrayList<Integer> list = new ArrayList<Integer>(); //创建ArrayList集合
        list.add(2);            //添加Integer
        list.add(1);
        for (Integer str:list){     //遍历集合
            System.out.println(str);//强行转为String类型
        }

    }
}

【运行结果】

使用泛型规定了ArrayList集合只能存入Integer类型元素,所以不能存储string元素,向集合中存入两个Integer类型的元素,通过对集合的遍历,输出结果。

在使用泛型后,每次遍历集合元素时,可以指定元素类型为Integer,而不是Object,这样就可以避免在程序中进行强制类型转换。

6.3 泛型方法

在类中也可以定义泛型方法。泛型方法的定义与其所在的类是否是泛型类是没有任何关系的。

定义泛型方法:

[访问权限]<泛型标识>返回值类型 方法名称 (泛型标识 参数名称)

泛型方法的定义和使用【案例】

class Dog{
    String eat;
    Integer age;
    public <T> void show(T t){
        System.out.println(t);
    }
}
public class text1{
    public static void main (String[] args){
        //创建对象
        Dog dog=new Dog();
        //调用方法,传入的参数是什么类型,返回值就是什么类型的
        dog.show("hello");
        dog.show(12);
        dog.show(12.5);
    }
}

【运行结果】

6.4 泛型接口

声明泛型接口和声明泛型类的语法相似,也是在接口名称后面加上<T>,声明泛型接口的格式如下:

[访问权限] interface 接口名称<泛型标识> {}

利用以上格式定义一个泛型接口,如下:

interface Info<T>{
    public T getVar();
}

泛型接口定义完成之后,就要定义此接口的子类,定义泛型接口的子类有两种方式:一种是直接在子类实现的接口中明确地给出泛型类型;另外一种是直接在子类后声明泛型。如下给出两种方式:

6.4.1 直接在接口中指定具体类型

【运行代码】

~一个文件中有多个类,只有与文件名一致的类名,才能声明为:public


//定义一个泛型接口
 interface Inter<T>{
    abstract void show (T t);
}
//定义泛型接口的子类
class InterImpl implements Inter<String>{
    public void show (String s){
        System.out.println(s);
    }
}

class text1{
    
    public static void main (String[] args){
        Inter<String> inter = new InterImpl();
        inter.show("hello");
    }
}

【运行结果】 

hello

 6.4.2 在子类的定义上声明泛型类型

当子类不确定泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现中也需要定义出类型参数变量

只需要在定义泛型接口的子类中进行修改即可。

//定义一个泛型接口
 interface Inter<T>{
    abstract void show (T t);
}
//定义泛型接口的子类
class InterImpl<T> implements Inter<T>{
    public void show (T t){
        System.out.println(t);
    }
}

class text1{
    
    public static void main (String[] args){
        Inter<String> inter = new InterImpl();
        inter.show("hello");
        Inter<Integer>ii=new InterImpl();
        ii.show(12);
    }
}

【运行结果】

当子类不确定泛型类的类型参数变量时,在定义对象时泛型可以为任意类型。

hello
12

 6.5 类型通配符

在Java中,数组时可以协变的,也就是说,父类和子类可以保持相同形式的变化,例如,当前尤Dog类和Animal类,Animal类继承了Dog类,那么Animal[]与dog[]是可以兼容的。

当集合不能协变,也就是说Animal不是Dog的父类,为了解决集合无法协变的问题,Java泛型为我们提供了类型通配符“ ? ”。

【案例】

class text1{ 
    public static void main (String[] args){

        //List集合装载的是Integer
        List<Integer> list = new ArrayList();
        list.add(1);
        list.add(2);
        test(list);

    }
    public static void test (List<?> list){
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }

    }
}

【运行结果】

首先定义test()方法时使用List<?>表示test()方法的参数可以是存储任意类型数据的List对象。

需要注意的是,如果使用通配符" ? "接收泛型对象,则通配符" ? "修饰的对象只能接收,不能修改。

6.6 Lambda表达式

Lambda可以取代大部分的匿名内部类,写出更好的java代码,尤其在集合的遍历和其他集合操作中,可以极大的优化代码结构。

lambda表达式由参数列表、箭头符号->和函数体组成。函数体即可以是表达式,也可以是一个语句。其中,表达式会被执行,然后返回执行结果;语句块中的语句会被依次执行。

lambda表达式常用的语法格式 


【案例】

import java.util.Arrays;

class text1{ 
    public static void main (String[] args){
        String[] arr={"program","creek","is","a","java","site"};
        Arrays.sort(arr,(m,n)->Integer.compare(m.length(),n.length()));
        System.out.println("lambda语句体中只有一条语句,参数类型可推断:"+
        Arrays.toString(arr));
        Arrays.sort(arr,(String m,String n) ->{
            if(m.length()>n.length())
                return -1;
            else
                return 0;
        });
        System.out.println("lambda语句体中有多条语句:"+Arrays.toString(arr));
    }
}

 【运行结果】

使用了两种Lambda表达式语法对字符串的数组arr进行排序,其中第五行代码使用compare()方法比较字符串的长度来进行排序,后面一个使用了if...else语法比较字符串的长度进行排序。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值