Java之集合

一、集合框架的概述

  1. 集合、数组都是多个数据进行存储操作的结构,简称Java容器。
    说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt、.jpg、.avi,数据库中)

  2. 数组在存储多个数据方面的特点:
    ① 一旦初始化以后,其长度就确定了。
    ②数组一旦定义好,其元素类型也就确定了。我们也就只能操作指定类型的数据了
    比如:String[] arr、int[] arr、Object[] arr等

  3. 数组在存储多个数据方面的缺点:
    ① 一旦初始化以后,其长度就不可以修改了
    ② 数组提供的方法非常有限,对于添加、删除、插入等操作非常不便,同时效率不高
    ③ 获取数组中实际元素的得到个数需求,数组没有现成的属性或方法可用
    ④ 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

集合框架:

  1. Collection接口:单列集合,用来存储一个一个的对象
    ① List接口(Collection子接口):存储有序、可重复的数据(动态数组)
    实现类:ArrayList、LinkedList、Vector
    ② Set接口(Collection子接口):存储无序、不可重复的数据(集合)
    实现类:HashSet、LinkHashSet、TreeSet
  2. Map接口:双列数据,用来存储一对(Key - Value)一对数据(高中函数:y = f(x))
    实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、properties

二、Collection

1. Collection接口中声明的方法的测试

  1. add(Object e):奖元素添加到集合coll中
  2. addAll():将coll1集合的元素添加到当前元素的集合中
  3. size():获取添加元素的个数
  4. clear():清空集合元素
  5. isEmpty():判断集合是否为空
  6. contains(Object e):判断当前集合是否包含e
  7. containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
  8. remove(Object e):从当前集合中移除e元素
  9. removeAll(Collection coll1):从当前集合中移除coll1中的所有元素(差集)
  10. retainAll(Collection coll1):获取当前集合的coll1集合的交集,并返回给当前集合(交集)
  11. equals(Object e):判断当前集合和形参集合的元素是否相等(顺序也得一样)
  12. hashCode():返回当前对象的哈希值
  13. toArray():集合 --> 数组
  14. iterator():返回Iterator接口的实例,用于遍历集合元素
  15. 拓展:数组 --> 集合:调用Arrays类的静态方法asList()

测试代码:

public class CollectionTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();

        //1. add(Object e):奖元素添加到集合coll中
        coll.add("AA");
        coll.add("BB");
        coll.add(123); //自动装箱
        coll.add(new Date());

        //2. size():获取添加元素的个数
        System.out.println(coll.size());

        //3. addAll():将coll1集合的元素添加到当前元素的集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("abc");

        coll.addAll(coll1);
        System.out.println(coll.size());
        System.out.println(coll);

        //4. clear():清空集合元素
        coll.clear();

        //5. isEmpty():判断集合是否为空
        System.out.println(coll.isEmpty());

        //6. contains(Object e):判断当前集合是否包含e
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));

        System.out.println(coll.contains(123)); //true
        System.out.println(coll.contains(new String("Tom"))); //true
        System.out.println(coll.contains(new Person("Jerry", 20))); //false --> true

        //7. containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
        Collection coll2 = Arrays.asList(123, 4546);
        System.out.println(coll.containsAll(coll2));

        //8. remove(Object e):从当前集合中移除e元素
        coll.remove(123);
        System.out.println(coll);

        //9. removeAll(Collection coll1):从当前集合中移除coll1中的所有元素(差集)
        Collection coll3 = Arrays.asList(456, false, new Person("Jerry", 20));
        coll.removeAll(coll3);
        System.out.println(coll);
    }

    @Test
    public void test2(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));

        //10. retainAll(Collection coll1):获取当前集合的coll1集合的交集,并返回给当前集合(交集)
        Collection coll1 = Arrays.asList(123, 456);
        coll.retainAll(coll1);
        System.out.println(coll);

        //11. equals(Object e):判断当前集合和形参集合的元素是否相等(顺序也得一样)
        Collection coll2 = Arrays.asList("abc", 123);
        Collection coll3 = Arrays.asList("abc", 123);
        Collection coll4 = Arrays.asList(123, "abc");
        //可以说明ArrayList是有序的
        System.out.println(coll2.equals(coll3)); //true
        System.out.println(coll2.equals(coll4)); //false
    }

    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));

        //12. hashCode():返回当前对象的哈希值
        System.out.println(coll.hashCode());

        //13. toArray():集合 --> 数组
        Object[] arr = coll.toArray();
        System.out.println(Arrays.toString(arr));
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //拓展:数组 --> 集合:调用Arrays类的静态方法asList()
        List list = Arrays.asList(new int[]{123, 456});
        System.out.println(list.size()); //1
        List list1 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(list1.size()); //2

        List<String> list2 = Arrays.asList(new String[]{"aa", "bb", "cc"});
        System.out.println(list2);

        //14. iterator():返回Iterator接口的实例,用于遍历集合元素
    }
}

2. 集合的遍历操作

1. 使用Iterator接口(迭代器)

  • 内部方法:
    ① hashNext():判断是否还有下一个元素
    ② next()指针下移,并将下移后集合位置上的元素返回
    ③ remove():可以在遍历的时候,删除集合中的元素

测试代码:

		Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));

        Iterator iterator = coll.iterator();
        
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
@Test
    public void test2(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));

        Iterator iterator = coll.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            if("Tom".equals(obj)){
                iterator.remove();
            }
        }

        iterator = coll.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

2. 使用 foreach 循环

  1. Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
  2. 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
  3. 遍历集合的底层调用Iterator完成操作。
  4. foreach还可以用来遍历数组。
for(集合元素的类型 局部变量 : 集合对象)

测试代码:

// 遍历集合
@Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(false);
        coll.add(new String("Tom"));
        coll.add(new Person("Jerry", 20));


        //for(集合元素的类型 局部变量 : 集合对象)
        //内部任然可以调用迭代器
        for(Object obj : coll){
            System.out.println(obj);
        }
    }

//遍历数组
    @Test
    public void test2(){
        int[] arr = new int[]{1, 2, 3, 4, 5};
        for(int i : arr){
            System.out.println(i);
        }
    }

3. Collection子接口之一:List接口

  • List接口(Collection子接口):存储有序、可重复的数据(动态数组)
    实现类:ArrayList、LinkedList、Vector

  • List接口常用的方法:
    ① void add(int index, Object ele): 在index位置插入ele元素
    ② boolean addAll(int index, Collection eles): 从index位置开始将eles中的所有元素添加进来
    ③ Object get(int index): 获取指定index位置的元素
    ④ int indexOf(Object obj): 返回obj在集合中首次出现的位置
    ⑤ int lastIndexOf(Object obj): 返回obj在当前集合中末次出现的位置
    ⑥ Object remove(int index): 移除指定index位置的元素,并返回此元素
    ⑦ Object set(int index, Object ele): 设置指定index位置的元素为ele
    ⑧ List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合

  • 总结:常用方法
    增:add(Object obj)
    删:remove(int index)、remove(Object obj)
    改:set(int index, Object ele)
    查:get(int index)
    插:add(int index, Object ele)
    长度:size()
    遍历:① Iterator迭代器方式、 ② 增强for循环、 ③ 普通循环

测试代码:

 @Test
    public void test1(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom", 22));
        list.add(456);

        System.out.println(list);

        //1. void add(int index, Object ele): 在index位置插入ele元素
        list.add(1, "BB");
        System.out.println(list);

        //2. boolean addAll(int index, Collection eles): 从index位置开始将eles中的所有元素添加进来
        List list1 = Arrays.asList(1, 2, 3);
        list.addAll(list1);
        System.out.println(list.size()); //9

        //3. Object get(int index): 获取指定index位置的元素
        System.out.println(list.get(4));

        //4. int indexOf(Object obj): 返回obj在集合中首次出现的位置(找不到,返回-1)
        System.out.println(list.indexOf(456));

        //5. int lastIndexOf(Object obj): 返回obj在当前集合中末次出现的位置
        System.out.println(list.lastIndexOf(456));

        //6. Object remove(int index): 移除指定index位置的元素,并返回此元素
        System.out.println(list.remove(3));
        System.out.println(list);

        //7. Object set(int index, Object ele): 设置指定index位置的元素为ele
        list.set(1, "CC");
        System.out.println(list);

//8. List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合(区间:[fromIndex, toIndex),即左闭右开)
        List subList = list.subList(0, 2);
        System.out.println(subList);

        //方式一:Iterator迭代器方式
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //方式二:增强for循环
        for(Object obj : list){
            System.out.println(obj);
        }

        //方式三:普通for循环
        for(int i = 0; i < list.size(); i++){
            System.out.println(list.get(i));
        }
    }

1. List实现类之一:ArrayList

  1. ArrayList 是 List 接口的典型实现类、主要实现类
  2. 本质上,ArrayList是对象引用的一个”变长”数组
  3. ArrayList的JDK1.8之前与之后的实现区别?
    • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
    • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组

建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)

2. List实现类之二:LinkedList

  1. 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

  2. 新增方法:
    void addFirst(Object obj)
    void addLast(Object obj)
    Object getFirst()
    Object getLast()
    Object removeFirst()
    Object removeLast()

  3. LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:

    • prev变量记录前一个元素的位置
    • next变量记录下一个元素的位置
private static class Node<E> {
	E item;
	Node<E> next;
	Node<E> prev;
	Node(Node<E> prev, E element, Node<E> next) {
		this.item = element;
		this.next = next;
		this.prev = prev;
	}
}

3. List 实现类之三:Vector

  1. Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList
    相同,区别之处在于Vector是线程安全的。
  2. 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
  3. 新增方法:
    void addElement(Object obj)
    void insertElementAt(Object obj,int index)
    void setElementAt(Object obj,int index)
    void removeElement(Object obj)
    void removeAllElements()

4. Collection子接口之二:Set接口

  1. Set接口(Collection子接口):存储无序、不可重复的数据(集合)
    实现类:HashSet、LinkedHashSet、TreeSet
    HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
    LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    TreeSet:可以按照添加对象的指定属性,进行排序
  2. set接口没有额外的定义新的方法,使用的都是Collection中声明的方法
  3. 要求1:向Set中添加的数据,其所在类一定要重写hashCode()、equals()
    要求2:重写的hashCode()和equals()尽可能保持一致性(即相等的对象必须具有相等的散列码)

1. Set实现类之一:HashSet

  1. HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
  2. HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
  3. HashSet 具有以下特点:
    • 不能保证元素的排列顺序
    • HashSet 不是线程安全的
    • 集合元素可以是 null
  4. HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
  5. 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

测试代码:

@Test
    public void test1(){
        Set set = new HashSet();
        set.add(123);
        set.add(456);
        set.add("AA");
        set.add(new Person("Tom", 22));
        set.add(new Person("Tom", 22));
        set.add(true);
        set.add("BB");
        set.add(123);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }

2. 添加元素的过程(以HashSet为例)

  • 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过底层某种算法计算出在HashSet底层数组中存放的位置(即为:索引位置),判断数组此位置上是否已经有元素:

    • 如果此位置上没有其它元素,则元素a添加成功 —> 情况1
    • 如果此位置上有其他元素b(或以链表形式存在多个元素),则比较元素a和b的hash值:
      • 如果hash值不相同,则元素a添加成功。 —> 情况2
      • 如果hash值相同,进而需要调用元素a所在类的equals()方法:
        • equals()返回true,元素a添加失败
        • equals()返回false,元素a添加成功 —> 情况3
  • 对于添加成功的情况2和情况3而言:元素a于已经存在指定索引位置上的数据以链表方式存储。

    • jdk 7:元素a放到数组中,指向原来的元素
    • jdk 8:原来的元素放在数组中,指向元素a

HashSet底层:数组 + 链表的结构

3. Set实现类之二:LinkedHashSet

  1. LinkedHashSet 是 HashSet 的子类
  2. LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  3. LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
  4. LinkedHashSet 不允许集合元素重复。
  5. LinkedHashSet底层结构
    在这里插入图片描述

优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

测试代码:

/**
     * LinkedHashSet的使用:
     * LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据嗨维护了两个引用,记录此数据前一个数据和后一个数据。
     * 优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
     */
    @Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(123);
        set.add(456);
        set.add("AA");
        set.add(new Person("Tom", 22));
        set.add(new Person("Tom", 22));
        set.add(true);
        set.add("BB");
        set.add(123);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

4. Set实现类之三:TreeSet

  1. 向TreeSet中添加的数据,要求是相同类的对象。
  2. 两种排序方式:自然排序(s实现了Comparable接口) 和 定制排序
  3. 自然排序和定制排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不在是equals()。

测试代码:

/**
     * TreeSet的使用:
     * 1. 向TreeSet中添加的数据,要求是相同类的对象。
     * 2. 两种排序方式:自然排序(s实现了Comparable接口) 和 定制排序
     * 3. 自然排序和定制排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不在是equals()。
     */
    @Test
    public void test3(){
        TreeSet set = new TreeSet();

//        失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new Person("Tom", 22));

//        举例一:
//        set.add(123);
//        set.add(-12);
//        set.add(34);
//        set.add(45);

//        举例二:(自然排序)
        set.add(new Person("Tom", 23));
        set.add(new Person("Marry", 13));
        set.add(new Person("John", 20));
        set.add(new Person("Jack", 30));
        set.add(new Person("Jack", 50));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test4(){
        //定制排序
        Comparator comparator = new Comparator() {
            //按照年龄从小到大排序
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Person && o2 instanceof Person){
                    Person person1 = (Person) o1;
                    Person person2 = (Person) o2;
                    return Integer.compare(person1.getAge(), person2.getAge());
                }
                throw new RuntimeException("输入的数据不匹配");
            }
        };

        TreeSet set = new TreeSet(comparator);

        set.add(new Person("Tom", 23));
        set.add(new Person("Marry", 13));
        set.add(new Person("John", 20));
        set.add(new Person("Jack", 30));
        set.add(new Person("Jack", 50));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

三、Map接口

1. Map实现类的结构情况

  • Map:双列数据,存储key - value对的数据(类似于高中的函数:y = f(x))
    • HashMap:作为Map的主要实现类;线程不安全,效率高;能存储null的key和value

      • LinkedHashMap:保证遍历map元素时,可以按照的顺序实现遍历
        原因:在原有的HashMap底层结构的基础上,添加了一对指针,指向前一个和后一个元素(双向链表)
    • TreeMap:保证按照添加的key - value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
      底层使用的是时红黑树

    • Hashtable:作为古老的实现类;线程安全,效率低;不能存储null的key和value

      • Properties:常用来处理配置文件。key和value都是String类型
    • HashMap的底层:数组 + 链表(jdk7及以前)
      数组 + 链表 + 红黑树(jdk8)

在这里插入图片描述

2. Map结构的理解

  1. Map中的key:无序的、不可重复的、使用Set存储所有的key —> key所在的类要重写equals()和hashCode()(以HashMap为例)
  2. Map中的value:无序的、可重复的、使用Collection存储所有的value --> value所在类要重写equals()
  3. 一个键值对:key-value构成了一个Entry对象
  4. Map中的entry:无序的、不可重复的、使用Set存储所有的entry

在这里插入图片描述

3. Map接口:常用方法

  1. 添加、删除、修改操作:
    ① Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
    ② void putAll(Map m):将m中的所有key-value对存放到当前map中
    ③ Object remove(Object key):移除指定key的key-value对,并返回value
    ④ void clear():清空当前map中的所有数据
  2. 元素查询的操作:
    ① Object get(Object key):获取指定key对应的value
    ② boolean containsKey(Object key):是否包含指定的key
    ③ boolean containsValue(Object value):是否包含指定的value
    ④ int size():返回map中key-value对的个数
    ⑤ boolean isEmpty():判断当前map是否为空
    ⑥ boolean equals(Object obj):判断当前map和参数对象obj是否相等
  3. 元视图操作的方法:
    ① Set keySet():返回所有key构成的Set集合
    ② Collection values():返回所有value构成的Collection集合
    ③ Set entrySet():返回所有key-value对构成的Set集合

常用方法总结:
添加:put(Object key,Object value)
删除:remove(Object key)
修改:put(Object key,Object value)
查询:get(Object key)
长度:size()
遍历:keySet()、values()、entrySet()

测试代码:

public class MapTest {
    @Test
    public void test1(){
        Map map = new HashMap();
        map.put(123, "AA");
        map.put(234, "BB");
        map.put(465, "CC");

        System.out.println(map);
    }

    @Test
    public void test2(){
        Map map = new LinkedHashMap();
        map.put(123, "AA");
        map.put(234, "BB");
        map.put(465, "CC");

        System.out.println(map);
    }

    @Test
    public void test3(){
        Map map = new HashMap();
        //添加
        map.put("AA", 123);
        map.put(45, 123);
        map.put("BB", 56);
        //修改
        map.put("AA", 87);

        System.out.println(map);

        Map map1 = new HashMap();
        map1.put("CC", 123);
        map1.put("DD", 123);

        map.putAll(map1);
        System.out.println(map);

        //remove(Object key)
        System.out.println(map.remove("CC"));
        System.out.println(map);

        //clear()
        map.clear();
        System.out.println(map);
    }

    @Test
    public void test4(){
        Map map = new HashMap();
        map.put("AA", 123);
        map.put(45, 123);
        map.put("BB", 456);

        //get(Object key)
        System.out.println(map.get("AA"));

        //containsKey(Object key)
        System.out.println(map.containsKey("BB"));

        //containsValue(Object value)
        System.out.println(map.containsValue(123));

        //size()
        System.out.println(map.size());

        //isEmpty()
//        map.clear();
        System.out.println(map.isEmpty());

        //equals(Object obj)
        System.out.println(map.equals("BB"));
    }

    @Test
    public void test5(){
        Map map = new HashMap();
        map.put("AA", 123);
        map.put(45, 123);
        map.put("BB", 456);

        //Set kySet:遍历所有的key集
        Set set = map.keySet();
        System.out.println(set);

        //Collection values():
        Collection collection = map.values();
        System.out.println(collection);

        //Set entrySet(),集合中的元素都是entry
        Set set1 = map.entrySet();
        System.out.println(set1);
    }
}

4. HashMap的底层实现原理?以jdk7为例说明

  • HashMap map = new HashMap():
    在实例化以后,底层创建了、长度为16的一维数组Entry[] table
  • map.put(key1, value1):
    • 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置

    • 如果此位置上的数据为空,此时的key1-value1添加成功。— 情况1

    • 如果此位置上的数据不为空,(此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经在一个或多个数据的哈希值:

      • 如果key1的哈希值和已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 — 情况2
      • 如果key1的哈希值和已经存在某一个数据的哈希值相同,继续比较,调用key1所在类的equals()方法,比较:
        • 如果equals()返回false:此时key1-value1添加成功。 — 情况3
        • 如果equals()返回true:使用value1替换相同key的value值。
    • 补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
      在不断添加过程中,会涉及到扩容问题,默认扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

jdk8相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一长度为16的数组
  2. jdk8底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组 + 链表。jdk8中底层的结构:数组 + 链表 + 红黑树。
    当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8且当前数组长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储。

5. LinkedHashMap的底层实现原理

  1. LinkedHashMap 是 HashMap 的子类
  2. 在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
  3. 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
  • HashMap中的内部类:Node
static class Node<K,V> implements Map.Entry<K,V> {
	final int hash;
	final K key;
	V value;
	Node<K,V> next;
}
  • LinkedHashMap中的内部类:Entr
static class Entry<K,V> extends HashMap.Node<K,V> {
	Entry<K,V> before, after;
	Entry(int hash, K key, V value, Node<K,V> next) {
		super(hash, key, value, next);
	}
}

6. Map实现类之三:TreeMap

  1. TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
  2. TreeSet底层使用红黑树结构存储数据
  3. TreeMap 的 Key 的排序:
    • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
    • 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现Comparable 接口
  4. TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。

测试代码:

package Map;

import org.junit.Test;

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

/**
 * @author XiaoQ
 * @create 2022-10-04 21:58
 */
public class TreeMapTest {
    //向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要求按照key进行排序:自然排序、定制排序
    //自然排序
    @Test
    public void test1(){
        Map map = new TreeMap();
        map.put(new User("aaa", 12), new User("asf", 23));
        map.put(new User("sdf", 12), new User("dfg", 13));
        map.put(new User("acv", 12), new User("dfg", 45));
        map.put(new User("dfh", 42), new User("ert", 22));
        map.put(new User("aaa", 11), new User("ghj", 9));

        Set set = map.entrySet();
        for(Object obj : set){
            System.out.println(obj);
        }
        /*
        输出:
        User{name='sdf', age=12}=User{name='dfg', age=13}
        User{name='dfh', age=42}=User{name='ert', age=22}
        User{name='acv', age=12}=User{name='dfg', age=45}
        User{name='aaa', age=11}=User{name='ghj', age=9}
        User{name='aaa', age=12}=User{name='asf', age=23}
        总结:如果User类的CompareTo()返回非0,则这两个User类不相等,返回0,则相等
         */
    }

    //定制排序
    @Test
    public void test2(){
        Map map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                User user = (User) o1;
                User user1 = (User) o2;
                if(user instanceof User && user1 instanceof User){
                    return user.getName().compareTo(user1.getName());
                }
                throw new RuntimeException("输入数据不一致");
            }

        });

        map.put(new User("aaa", 12), new User("asf", 23));
        map.put(new User("sdf", 12), new User("dfg", 13));
        map.put(new User("acv", 12), new User("dfg", 45));
        map.put(new User("dfh", 42), new User("ert", 22));
        map.put(new User("aaa", 11), new User("ghj", 9));

        Set set = map.entrySet();
        for(Object obj : set){
            System.out.println(obj);
        }
    }

    @Test
    public void test3(){
        TreeMap treeMap = new TreeMap();
        treeMap.put(1, 2);
        treeMap.put(1, 3);
        treeMap.put(4, 2);
        treeMap.put(2, 2);

        Set set = treeMap.entrySet();
        for(Object obj : set){
            System.out.println(obj);
        }
    }
}

7. Map实现类之四:Hashtable

  1. Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。
  2. Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
  3. 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
  4. 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
  5. Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

8. Map实现类之五:Properties

  1. Properties 类是 Hashtable 的子类,该对象用于处理属性文件
  2. 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
  3. 存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

测试代码:

package Map;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * @author XiaoQ
 * @create 2022-10-04 22:53
 */
public class PropertiesTest {
    //Properties:常用来处理配置文件,key和value都是String类型
    @Test
    public void test1(){
        Properties properties = new Properties();

        try {
            FileInputStream fis = new FileInputStream("jdbc.properties");
            properties.load(fis); //加载流对应文件

            String name = properties.getProperty("name");
            String password = properties.getProperty("password");

            System.out.println("name: " + name);
            System.out.println("password: " + password);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

四、Collections工具类

*Collections:操作Collection、Map的工具类

  1. 排序操作:(均为static方法)
    ① reverse(List):反转 List 中元素的顺序
    ② shuffle(List):对 List 集合元素进行随机排序
    ③ sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
    ④ sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
    ⑤ swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
  2. 查找、替换:
    ① Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
    ② Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
    ③ Object min(Collection)
    ④ Object min(Collection,Comparator)
    ⑤ int frequency(Collection,Object):返回指定集合中指定元素的出现次数
    ⑥ void copy(List dest,List src):将src中的内容复制到dest中
    ⑦ boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值

测试代码:

@Test
    public void test1(){
        List list = new ArrayList();
        list.add(123);
        list.add(34);
        list.add(334);
        list.add(456);
        list.add(-45);

        System.out.println(list);
        //reverse(List)
        Collections.reverse(list);
        System.out.println(list);

        //shuffle(List)
        Collections.shuffle(list);
        System.out.println(list);

        //sort(List)
        Collections.sort(list);
        System.out.println(list);

        //swap(List, int, int)
        Collections.swap(list, 1, 2);
        System.out.println(list);

        //int frequency(Collection,Object)
        System.out.println(Collections.frequency(list, 456));

        //oid copy(List dest,List src)
        //报异常:java.lang.IndexOutOfBoundsException: Source does not fit in dest
//        List dest = new ArrayList();
//        Collections.copy(dest, list);

        List dest = Arrays.asList(new Object[list.size()]);
        Collections.copy(dest, list);

        System.out.println(dest);
        System.out.println(list);
    }
  • Collections类提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并访问集合时的线程安全问题
@Test
    public void test2(){
        List list = new ArrayList();
        list.add(123);
        list.add(34);
        list.add(334);
        list.add(456);
        list.add(-45);

        List list1 = Collections.synchronizedList(list); //返回的list1即为线程安全的List
    }

版权声明:本文为weixin_52068218原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。