15 集合类库下

193 阅读7分钟

1 泛型机制

集合中可以存放不同类型的对象,是因为将所有对象都看作Object类型放入的,所以从集合中取出的元素也都是Object类型,而为了表达元素的真实数据类型,通常需要使用强制类型转换,但是强制类型转换可能会导致类型转换异常。

为了解决上面提到的问题,java5开始引入泛型机制,在集合名称右侧使用<数据类型>的方式来明确该集合所存放的元素的数据类型,若放入其他数据类型的元素,编译会报错(泛型只在编译时期有效,在运行时期不区分类型)

泛型机制的底层原理:泛型的本质就是参数化类型,将数据类型当作参数进行传递,而其中E相当于形式参数负责占位,使用集合时<>中的数据类型当作实参,用来给形式参数E进行初始化,使得集合中所有的E都被实际参数所替换,由于实际参数可以传递各种各样的广泛的数据类型,因此称为泛型。

//E当作形式参数,负责占位
public interface List<E>{
  
}

//在使用集合时,将<>中的String当作实际参数传递给E进行初始化,从而E代表String
List<String> lt1=new LinkedList<>();




2 自定义泛型

自定义泛型有:自定义泛型接口、自定义泛型类、自定义泛型方法,下面会介绍这三种自定义泛型

  • 自定义泛型接口:和普通接口相比,自定义泛型接口就是在类名后面加上类型参数列表,所以类型参数可以有一个或多个

,比如public interface List<E,T,......>

  • 自定义泛型类:和自定义泛型接口一样,在普通类的后面加上了类型参数列表,类型参数同样可以有一个或多个,具体实例如public class Student<E,T,......>,需要注意的是在实例化自定义泛型类时,需要给出具体的数据类型,并且数据类型只能是引用类型;父类有泛型时,子类可以选择保留泛型也可以选择指定泛型类型;子类是“富二代”,不仅可以指定或保留父类泛型,还可以添加自己的泛型。

  • 自定义泛型方法:自定义泛型方法就是在普通方法输入参数时,输入的是泛型参数,而不再是具体的参数,在使用泛型方法时也就需要对泛型参数进行实例化。泛型方法的语法格式是:访问权限 <泛型> 返回值类型 方法名(泛型标识 参数名称){方法体;};在静态方法中使用泛型参数时,需要将静态方法定义为泛型方法。

    //定义方法打印数组中的所有元素,因为不知道数组的数据类型,所以需要用泛型,写成泛型方法
    public <T1> void printArry(T1[] arr){
      for(T1 a:arr){
        Syste.out.println(a);
      }
    }
    

3 泛型在继承上的体现

如果B是A的一个子类或接口,而D是具有泛型声明的类或接口,则D不是D的子类型。比如String是Object类型的子类,但List不是List的子类

4 通配符

当需要传入的类型是在一定范围内的,我们就需要使用泛型通配符,泛型通配符有三种:

  • 无限制通配符:表示可以传入任何类型的参数
  • 表示类型的上界是E,可以传入E或E的子类
  • 表示类型的下界是E,可以传入E或E的父类

使用无限制通配符<?>的集合不支持添加操作,可以获取集合元素;使用<? extends E>的集合也是不支持添加操作,可以获取集合元素;只有使用<? super E>的集合可以对集合元素进行添加和获取操作。

5 set集合

  • set集合是Collection集合的子集合,与List集合是平级关系
  • set集合存放元素时没有先后顺序,并且不允许存在重复元素
  • set集合是个接口,具体的实现类有:HashSet类、TreeSet类以及LinkedHashSet类
  • HashSe类的底层是用哈希表进行数据管理
  • TreeSet类的底层是用红黑树进行数据管理
  • LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素插入集合的先后顺序,因此便于迭代。

set集合的常用方法,以HashSet类为例,具体代码如下:

import java.util.HashSet;
import java.util.Set;

public class HashSetTest {
    public static void main(String[] args) {
        Set<String> set=new HashSet<>();
        boolean b=set.add("two");
        System.out.println("set集合中元素有:"+set);
        b=set.add("one");
        System.out.println("set集合中元素有:"+set);
        b=set.add("three");
        System.out.println("set集合中元素有:"+set);
        b=set.add("one");
        System.out.println("添加是否成功"+b);
        System.out.println("set集合中元素有:"+set);

    }
}

元素添加到HashSet集合原理

  • 使用元素调用hashCode方法获取对应的哈希码值,再由某中哈希码算法计算出该元素在数组中的索引下标
  • 若数组该下标下无元素存在,则直接将该元素放入
  • 若数组该下标下已经存在元素,则使用新元素与已存在的的元素依次比较哈希值,若哈希值不同,则将新元素直接放入
  • 若新元素的哈希值与已存在元素的哈希值相等,则使用新元素调用equals方法与已存在元素进行比较,若比较结果不相同,则直接放入,若结果相同,则添加元素失败

思考:为什么要求重写equals方法后还需要重写hashCode方法

解析:当两个元素调用equals方法相等时证明这两个元素相等,重写hashCode方法后可以保证这两个元素得到的哈希码值也相等,由同一个哈希算法生成索引的位置相同,此时只需要与该索引位置已有元素进行比较,提高了效率并避免了重复元素的出现。

6 TreeSet集合

在介绍TreeSet集合之前,先了解二叉树,二叉树是指每个节点最多有两个字节点的树形结构,在二叉树中还有一种特殊的二叉树,称为有序二叉树,要求二叉树满足以下几点要求:

  • 左子树的任意节点元素值都小于根结点元素值
  • 右子树的任意节点元素值都大于根结点元素值
  • 左子树和右子树内部也符合上述规则

TreeSet集合底层是使用红黑树进行数据管理,当有新元素插入到TreeSet集合中,需要使用新元素与已存在元素依次进行比较来确定新元素的存放位置。

比较元素大小有两种方式:

  1. 使用元素的自然排序规则进行比较,需要元素实现java.lang.Comparable接口

    //需要注意的是String类中已经实现了java.lang.Comparable接口,所以可以直接使用
    import java.util.Set;
    import java.util.TreeSet;
    
    public class TreeSetTest {
        public static void main(String[] args) {
            Set<String> set=new TreeSet<>();
            set.add("aa");
            set.add("cc");
            set.add("bb");
            System.out.println("TreeSet集合中元素是:"+set);
        }
    }
    //输出结果是:[aa,bb,cc]
    
  2. 使用自定义的比较器规则进行比较,需要在构造TreeSet集合时传入java.util.Comparator接口

    import java.util.Comparator;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class TreeSetTest {
        public static void main(String[] args) {
          //自定义按照年龄大小的比较规则
            Comparator<Student> comparator=new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    return o1.getAge()- o2.getAge() ;
                }
            };
            Set<Student> set=new TreeSet<>(comparator);
            set.add(new Student("liubei",56));
            set.add(new Student("zhangfei",32));
            set.add(new Student("guanyu",45));
            System.out.println("TreeSet集合元素是"+set);
    
    
    
        }
    }
    
    //输出结果: TreeSet集合元素是[Student{name='zhangfei', age=32}, Student{name='guanyu', age=45}, Student{name='liubei', age=56}]
    

从上面可以看到自然排序规则比较单一,不像比较器规则那样多元化,并且比较器也要优于自然排序规则

7 Map集合

Map<K,V>集合存取元素的基本单位是:单对元素,其中类型参数是:K是映射维护的键(key)的类型,相当于目录;而V是映射对应的值,相等于内容,需要注意的是在Map集合中是不允许key出现重复,且一个key对应一个value。

Map集合的具体实现类有:HashMap类、TreeMap类、LinkedHashMap类、HashTable类以及Properties类,关于这些实现类的介绍如下:

  • HashMap类底层采用哈希表进行数据管理
  • TreeMap类底层采用红黑树进行数据管理
  • LinkedHashMap类与HashTable类的不同之处在于LinkedHashMap类内部维护了一个双向链表,链表中记录了元素插入集合的先后顺序,便于进行迭代。
  • HashTable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或者value的值
  • Properties类是HashTable类的子类,主要用来处理配置文件,并且key和value的类型都是String
  • Map集合面向查询优化的数据结构,在大部分情况下有着优良的查询性能,经常用根据key查找value的业务场景

Map集合的常用方法:

方法声明功能介绍
V put(K key,V value)将元素key-value成对的方式存入集合
V get(K key)根据参数key查找集合中对应的value对象
boolean containsKey(Object K)判断集合中是否存参数指定的key
boolean containsValue(Object V)判断集合中是否存参数指定的value
V remove(Object k)删除集合中key值为k的元素
Set KeySet()返回此映射中包含键的Set视图
Collection values()返回此映射中包含值的Set视图
Set<Map.Entry<K,V>> entrySet()返回此映射中包含映射的Set视图

示例代码如下:

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

public class HashMapTest {
    public static void main(String[] args) {
        Map<Integer,String> map=new HashMap<>();
        String str=map.put(1,"java");
        System.out.println("原来的value数值是"+str);
        map.put(2,"pyhton");
        System.out.println("Map集合的元素是:"+map);
        boolean b=map.containsKey(1);
        System.out.println("Map集合中存在key值"+b);
        b=map.containsValue("python");
        System.out.println("Map集合中存在value值"+b);
        str=map.get(2);
        System.out.println("取出的值是"+str);
        str=map.remove(2);
        System.out.println("集合中删除的值是"+str);
        Set<Integer> s1=map.keySet();
        for(Integer i:s1){
            System.out.println(i+"="+map.get(i));
        }
        Collection<String> collection=map.values();
        for(String s:collection){
            System.out.println("s="+s);
        }
        Set<Map.Entry<Integer,String>> s2=map.entrySet();
        for(Map.Entry<Integer,String> in:s2){
            System.out.println(in);
        }
    }
}

元素添加到HashMap集合原理

  • 使用元素的key调用hashCode方法获取对应的哈希码值,再有某种哈希算法计算在数组中的索引位置。
  • 若该位置没有元素,则直接将该键值对放入
  • 若该位置已经存在元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入
  • 若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较
  • 若相等则将对应的value修改,否则将键值对直接放入即可

8 Collections类

Java.util.Collections类主要提供对集合操作或者返回集合的静态方法

Collectons常用方法如下:

方法声明功能介绍
static <T extends Object & Comparable <? super T>>T max(Collection<? extends T> Coll)根据元素的自然顺序返回给定集合的最大元素
static T max(Collection<? extends T> Coll,Comparator <? super T> comp)根据指定比较器引发的顺序返回给定集合的最大元素
static <T extends Object & Comparable <? super T>>T min(Collection<? extends T> Coll)根据元素的自然顺序返回给定集合的最小元素
static T max(Collection<? extends T> Coll,Comparator <? super T> comp)根据指定比较器引发的顺序返回给定集合的最小元素
static void copy(List< ? super T > dest,List< ? extends T> src )将第一个列表中的所有元素复制到另一个列表中
static void reverse(List<?> list)反转指定列表中元素的顺序
static void shuffle(List<?> list)使用默认的随机源随机置换指定的列表
static <T extends Comparable <? super T>> void sort(List list)根据其元素的自然顺序将指定列表按升序排序
static void sort(List list,Comparator <? super T> c)根据指定比较器指定的顺序对指定列表进行排序
static void swap(List< ?> list,int i,int j)交换指定列表中指定位置的元素

实例代码:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CollectionsTest {
    public static void main(String[] args) {
        List<Integer> list= Arrays.asList(11,22,33,44,55);
        System.out.println("集合中最大值是:"+ Collections.max(list));
        System.out.println("集合中最小值是:"+ Collections.min(list));
        System.out.println("集合中元素是:"+list);
        Collections.reverse(list);
        System.out.println("集合中元素反转后是:"+list);
        Collections.swap(list,0,2);
        System.out.println("交换后集合是:"+list);
        Collections.sort(list);
        System.out.println("排序后的集合是"+list);
        Collections.shuffle(list);
        System.out.println("随机置换后集合是:"+list);
        List<Integer> list1= Arrays.asList(new Integer[6]);
        Collections.copy(list1,list);
        System.out.println("list集合元素是"+list);
        System.out.println("list1集合元素是"+list1);
    }
}