美文网首页
03- Java集合

03- Java集合

作者: XAbo | 来源:发表于2020-11-29 18:42 被阅读0次

    一、泛型

    泛型通配符:通配符表示一种未知类型,并且对这种未知类型存在约束关系。不能创建对象使用,只能作为方法的参数使用。泛型没有继承概念。

    • ? extends T(上边界通配符upper bounded wildcard) 对应协变关系,表示 ? 是继承自 T的任意子类型.也表示一种约束关系,只能提供数据,不能接收数据。
    • ? 的默认实现是 ? extends Object, 表示 ? 是继承自Object的任意类型。
    • ? super T(下边界通配符lower bounded wildcard) 对应逆变关系,表示 ? 是 T的任意父类型.也表示一种约束关系,只能接收数据,不能提供你数据。

    2.1 泛型类

    • 泛型的类型参数只能是类类型,不能是简单类型。
    • 不能对确切的泛型类型使用instanceof操作。
    //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
    //在实例化泛型类时,必须指定T的具体类型
    public class Generic<T>{ 
        //key这个成员变量的类型为T,T的类型由外部指定  
        private T key;
        public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
            this.key = key;
        }
        public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
            return key;
        }
    }
    

    2.2 泛型接口

    //定义一个泛型接口
    public interface Generator<T> {
        public T next();
    }
    
    /**
     * 未传入泛型实参时,与泛型类的定义相同,
     * 在声明类的时候,需将泛型的声明也一起加到类中
     * 即:class FruitGenerator<T> implements Generator<T>{
     * 如果不声明泛型,如:class FruitGenerator implements 
     * Generator<T>,编译器会报错:"Unknown class"
     */
    class FruitGenerator<T> implements Generator<T>{
        public T next() {
            return null;
        }
    }
    
    /**
     * 传入泛型实参时:
     * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
     * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
     * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用
     * 泛型的地方都要替换成传入的实参类型
     * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
     */
    public class FruitGenerator implements Generator<String> {
        private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
        public String next() {
            Random rand = new Random();
            return fruits[rand.nextInt(3)];
        }
    }
    

    2.3 泛型方法

    泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

    public class Generic<T>{
        private T key;
        public Generic(T key) {
            this.key = key;
        }
        //这并不是一个泛型方法。
        public T getKey(){
            return key;
        }
        //这并不是一个泛型方法。
        public void setKey(T key){
            this.key = key;
        }
        /**
         * 这才是一个真正的泛型方法。
         * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
         * 这个T可以出现在这个泛型方法的任意位置.
         */
        public static <T> void init(T t){
           System.out.println("static");
        }
        public <T> T showKeyName(Generic<T> container){
            T test = container.getKey();
            return test;
        }
        public <T> void setKeyName(Generic<T> container){
           System.out.println(container);
        }
    }
     
    

    二、集合特性

    集合框架图
    • ArrayList是一个动态数组,也是我们最常用的集合,是List类的典型实现。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。 ArrayList擅长于随机访问。同时ArrayList是非同步的。

    • LinkedList是List接口的另一个实现,除了可以根据索引访问集合元素外,LinkedList还实现了Deque接口,可以当作双端队列来使用,也就是说,既可以当作“栈”使用,又可以当作队列使用。LinkedList的实现机制与ArrayList的实现机制完全不同,ArrayLiat内部以数组的形式保存集合的元素,所以随机访问集合元素有较好的性能;LinkedList内部以链表的形式保存集合中的元素,所以随机访问集合中的元素性能较差,但在插入删除元素时有较好的性能。

    • 与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。

    • Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

    • HashSet是Set集合最常用实现类,是其经典实现。HashSet是按照hash算法来存储元素的(先判断对象的哈希值(hashCode)是否相等,在判断对象的equals是否相等,所有集合中对象不会重复,使用HashSet存储自定义对象,一定要重写:hashCode和equals),因此具有很好的存取和查找性能。不能使用普通for循环遍历。哈希值就是系统随机给出的十进制地址。HashSet具有如下特点:1.不能保证元素的顺序。2.HashSet不是线程同步的,如果多线程操作HashSet集合,则应通过代码来保证其同步。3.集合元素值可以是null。

      哈希表
      去重原理
    • LinkedHashSet是HashSet的一个子类,具有HashSet的特性,也是根据元素的hashCode值来决定元素的存储位置。但它使用链表维护元素的次序,元素的顺序与添加顺序一致。由于LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但在迭代访问Set里的全部元素时由很好的性能。

    • TreeSet时SortedSet接口的实现类,TreeSet可以保证元素处于排序状态,它采用红黑树的数据结构来存储集合元素。TreeSet支持两种排序方法:自然排序和定制排序,默认采用自然排序。

    • HashMap与Hashtable是Map接口的两个典型实现,它们之间的关系完全类似于ArrayList与Vertor。HashTable是一个古老的Map实现类,它提供的方法比较繁琐,目前基本不用了,HashMap与Hashtable主要存在以下两个典型区别:1.HashMap是线程不安全,HashTable是线程安全的。2. HashMap可以使用null值最为key或value;Hashtable不允许使用null值作为key和value,如果把null放进HashTable中,将会发生空指针异常。为了成功的在HashMap和Hashtable中存储和获取对象,用作key的对象必须实现hashCode()方法和equals()方法

    • LinkedHashMap使用双向链表来维护key-value对的次序(其实只需要考虑key的次序即可),该链表负责维护Map的迭代顺序,与插入顺序一致,因此性能比HashMap低,但在迭代访问Map里的全部元素时有较好的性能。

    • TreeMap是SortedMap的实现类,是一个红黑树的数据结构,每个key-value对作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap也有两种排序方式:1.自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则会抛出ClassCastException。2.定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。

    1.1 实例

    Map:使用entrySet遍历Map

    package com.itheima.demo01.Map;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /*
        Map集合的第一种遍历方式:通过键找值的方式
        Map集合中的方法:
             Set<K> keySet() 返回此映射中包含的键的 Set 视图。
        实现步骤:
            1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
            2.遍历set集合,获取Map集合中的每一个key
            3.通过Map集合中的方法get(key),通过key找到value
     */
    public class Demo02KeySet {
        public static void main(String[] args) {
            //创建Map集合对象
            Map<String,Integer> map = new HashMap<>();
            map.put("赵丽颖",168);
            map.put("杨颖",165);
            map.put("林志玲",178);
    
            //1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
            Set<String> set = map.keySet();
    
            //2.遍历set集合,获取Map集合中的每一个key
            //使用迭代器遍历Set集合
            Iterator<String> it = set.iterator();
            while (it.hasNext()){
                String key = it.next();
                //3.通过Map集合中的方法get(key),通过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
            System.out.println("-------------------");
            //使用增强for遍历Set集合
            for(String key : set){
                //3.通过Map集合中的方法get(key),通过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
            System.out.println("-------------------");
            //使用增强for遍历Set集合
            for(String key : map.keySet()){
                //3.通过Map集合中的方法get(key),通过key找到value
                Integer value = map.get(key);
                System.out.println(key+"="+value);
            }
        }
    }
    
    

    Map:使用entrySet遍历Map

    package com.itheima.demo01.Map;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /*
        Map集合遍历的第二种方式:使用Entry对象遍历
    
        Map集合中的方法:
            Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
        实现步骤:
            1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
            2.遍历Set集合,获取每一个Entry对象
            3.使用Entry对象中的方法getKey()和getValue()获取键与值
     */
    public class Demo03EntrySet {
        public static void main(String[] args) {
            //创建Map集合对象
            Map<String,Integer> map = new HashMap<>();
            map.put("赵丽颖",168);
            map.put("杨颖",165);
            map.put("林志玲",178);
            //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
            Set<Map.Entry<String, Integer>> set = map.entrySet();
            //2.遍历Set集合,获取每一个Entry对象
            //使用迭代器遍历Set集合
            Iterator<Map.Entry<String, Integer>> it = set.iterator();
            while(it.hasNext()){
                Map.Entry<String, Integer> entry = it.next();
                //3.使用Entry对象中的方法getKey()和getValue()获取键与值
                String key = entry.getKey();
                Integer value = entry.getValue();
                System.out.println(key+"="+value);
            }
            System.out.println("-----------------------");
            for(Map.Entry<String,Integer> entry:set){
                //3.使用Entry对象中的方法getKey()和getValue()获取键与值
                String key = entry.getKey();
                Integer value = entry.getValue();
                System.out.println(key+"="+value);
            }
        }
    }
    

    Map:存放自定义类型

    package com.itheima.demo02.Map;
    import java.util.Objects;
    public class Person {
        private String name;
        private  int age;
    
        public Person() {
        }
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    package com.itheima.demo02.Map;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    /*
        HashMap存储自定义类型键值
        Map集合保证key是唯一的:
            作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
     */
    public class Demo01HashMapSavePerson {
        public static void main(String[] args) {
            show02();
        }
        /*
            HashMap存储自定义类型键值
            key:Person类型
                Person类就必须重写hashCode方法和equals方法,以保证key唯一
            value:String类型
                可以重复
         */
        private static void show02() {
            //创建HashMap集合
            HashMap<Person,String> map = new HashMap<>();
            //往集合中添加元素
            map.put(new Person("女王",18),"英国");
            map.put(new Person("秦始皇",18),"秦国");
            map.put(new Person("普京",30),"俄罗斯");
            map.put(new Person("女王",18),"毛里求斯");
            //使用entrySet和增强for遍历Map集合
            Set<Map.Entry<Person, String>> set = map.entrySet();
            for (Map.Entry<Person, String> entry : set) {
                Person key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key+"-->"+value);
            }
        }
        /*
            HashMap存储自定义类型键值
            key:String类型
                String类重写hashCode方法和equals方法,可以保证key唯一
            value:Person类型
                value可以重复(同名同年龄的人视为同一个)
         */
        private static void show01() {
            //创建HashMap集合
            HashMap<String,Person> map = new HashMap<>();
            //往集合中添加元素
            map.put("北京",new Person("张三",18));
            map.put("上海",new Person("李四",19));
            map.put("广州",new Person("王五",20));
            map.put("北京",new Person("赵六",18));
            //使用keySet加增强for遍历Map集合
            Set<String> set = map.keySet();
            for (String key : set) {
                Person value = map.get(key);
                System.out.println(key+"-->"+value);
            }
        }
    }
    

    Map:LinkedHashMap的有序

    package com.itheima.demo03.Map;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    /*
        java.util.LinkedHashMap<K,V> entends HashMap<K,V>
        Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
        底层原理:
            哈希表+链表(记录元素的顺序)
     */
    public class Demo01LinkedHashMap {
        public static void main(String[] args) {
            HashMap<String,String> map = new HashMap<>();
            map.put("a","a");
            map.put("c","c");
            map.put("b","b");
            map.put("a","d");
            System.out.println(map);// key不允许重复,无序 {a=d, b=b, c=c}
    
            LinkedHashMap<String,String> linked = new LinkedHashMap<>();
            linked.put("a","a");
            linked.put("c","c");
            linked.put("b","b");
            linked.put("a","d");
            System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b}
        }
    }
    
    

    Map:Hashtable的用法

    package com.itheima.demo03.Map;
    import java.util.HashMap;
    import java.util.Hashtable;
    /*
        java.util.Hashtable<K,V>集合 implements Map<K,V>接口
        Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
        HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
    
        HashMap集合(之前学的所有的集合):可以存储null值,null键
        Hashtable集合,不能存储null值,null键
    
        Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
        Hashtable的子类Properties依然活跃在历史舞台
        Properties集合是一个唯一和IO流相结合的集合
     */
    public class Demo02Hashtable {
        public static void main(String[] args) {
            HashMap<String,String> map = new HashMap<>();
            map.put(null,"a");
            map.put("b",null);
            map.put(null,null);
            System.out.println(map);//{null=null, b=null}
    
            Hashtable<String,String> table = new Hashtable<>();
            //table.put(null,"a");//NullPointerException
            //table.put("b",null);//NullPointerException
            table.put(null,null);//NullPointerException
        }
    }
    
    

    相关文章

      网友评论

          本文标题:03- Java集合

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