第八章、Java基础语法----集合

第八章、Java基础语法----集合

本人也是刚入门Java语言,可能会有一些地方出现错误,描述的不对。如果发现不对的地方请及时指出,好对其进行修改。这样不仅可以让我学到东西,也可以让其他刚入门的朋友学习更正确的内容。

所有内容仅供参考。不具有完全的准确性!

注:关于Java的所有内容都会参考到尚硅谷在网上公开的学习视频及其提供的PPT

推荐:
https://www.liaoxuefeng.com/wiki/1252599548343744/1265109905179456
https://blog.csdn.net/feiyanaffection/article/details/81394745

一、Java集合框架概述
(一)为什么要使用集合
在很多时候,我们事先是不知道一个数组到底需要创建多少的长度才是合适的。如果设置的太长,会有很多位置实际是用不到的,这就会导致对内存空间造成极大的浪费。如果太短,后期就可能出现一些数据存储不进去,没有地方存放。
通过集合我们就可以完美的解决上述的问题。集合不需要自定义数组的长度,只要有数据需要存放,就一直可以添加到集合中,底层框架会自动帮我们扩大到合适的长度。

(二)什么是集合

  • 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。集合可以动态地把多个对象的引用放入容器中。
  • 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)

关于数组的优缺点:
数组在存储多个数据方面的特点:

  1. 一旦初始化以后,其长度就确定了。
  2. 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
    比如:String[] arr;int[] arr1;Object[] arr2;

数组在存储多个数据方面的缺点:

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

对于数组的缺点,集合都可以解决掉。
在这里插入图片描述

  • Collection接口
  1. Collection 接口是 List、Set 和 Queue 接口的父接口(即除Map外其他所有集合类的根接口),该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
  2. JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
  3. 在Java 1.5 之前,Java集合把所有类型的数据都当成是object类型的对象存储。在 Java 1.5以后,增加了泛型,通过泛型我们可以指明集合中存储数据的类型。

注意:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

  • Collection 接口提供的方法
    在这里插入图片描述在这里插入图片描述
    (三)三种集合的概述
    Java的java.util包主要提供了以下三种类型的集合:
  • List:一种有序列表的集合,例如,按索引排列的Student的List;
  • Set:一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set;
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。

二、List集合
(一) List接口框架
在这里插入图片描述在集合类中,List是最基础的一种集合:它是一种有序列表

List的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List的索引和数组一样,从0开始

数组和List类似,也是有序结构,如果我们使用数组,在添加和删除元素的时候,会非常不方便。
例如,从一个已有的数组{‘A’, ‘B’, ‘C’, ‘D’, ‘E’}中删除索引为2的元素:
在这里插入图片描述
这个“删除”操作实际上是把’C’后面的元素依次往前挪一个位置,而“添加”操作实际上是把指定位置以后的元素都依次向后挪一个位置,腾出来的位置给新加的元素。这两种操作,用数组实现非常麻烦。

由于使用数组对数据进行操作过于麻烦,List接口给我们提供了很多对数据进行操作的方法,使得我们可以直接使用现成的方法就行删除。非常滴方便
更多相关的方法:
在这里插入图片描述

(二)ArrayList

  • 先对ArryList下的源码进行分析:
    jdk 1.7 的时候:

ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity);指明要扩建的数组长度,当然,指明以后如果长度不够了,也还是会自动扩容的。

那为什么还要指明长度呢?
       因为有时我们需要存放的数据量过于庞大,如果一直让系统自动扩容的话,就会极大的增加扩容的次数,导致程序的工作效率下降。

jdk 8中的变化:

ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]

后续的添加和扩容操作与jdk 7 无异。

也就是jdk 8 中创建ArrayList集合,一开始没有直接创建数组的长度,而是在第一向里边添加数据时才创建。延迟了数组的创建,节省内存

例:

ArrayList arraysList = new ArrayList();

        // add(Object obj) 在集合的末尾添加元素
        arraysList.add(234);
        arraysList.add(86.45);
        arraysList.add("TY");
        arraysList.add(new Date());
        arraysList.add(86.45);
        System.out.println(arraysList);//[234, 86.45, TY, Wed Dec 02 16:00:00 CST 2020, 86.45]

        //void add(int index, Object ele):在index位置插入ele元素
        arraysList.add(2,"234");
        System.out.println(arraysList);//[234, 86.45, 234, TY, Wed Dec 02 16:00:00 CST 2020, 86.45]

        //通过asList创建的集合,创建完成以后不可以再添加数据
        List list = Arrays.asList("aa", "bb", "cc");

        System.out.println("未添加前的长度" + arraysList.size()); // 6
        //boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
        boolean b = arraysList.addAll(list); // addAll(Collection) 在当前对象的末尾添加
        System.out.println("添加后的长度" + arraysList.size()); // 9

        // 注意:要和add() 方法区别开。 若用成add,就变成了将list整个集合当成一个元素添加进去了
//        arraysList.add(list);
//        System.out.println(arraysList.size()); // 10 list的长度是 3,而添加之后长度却只增长了 1

注意:

  1. 在集合中调用的size()方法和数组中的length()方法不同,length()指明的是数组的总长度(无论数组中实际存储元素个数是多少);而size()返回的是集合中元素的个数,无论集合的长度有多大。
  2. Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
    编译时不会报错,运行时会报异常。

(三)LinkedList:链表集合
源码分析:

LinkedList list = new LinkedList(); 双向链表,内部没有声明数组,而是定义了Node类型的first和last属性,用于记录首末元素。默认值为null;
list.add(123);//将123封装到Node中,创建了Node对象。

Node除了保存数据,还定义了两个指针:

prev指向前一个元素
next指向下一个元素

其中,Node定义为:体现了LinkedList的双向链表的说法

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;
      }
}
  • 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

(四)Vector

源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
Vector集合在Java 1.0版本的时候就已经存在了,它与ArrayList的操作大多都类似,不同的时Vector时线程安全的。
只是举了一个方法,其他方法也都是安全的。
在这里插入图片描述
三、Set集合

  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
  • Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

(一)Set接口的框架
在这里插入图片描述
(二)Set接口的使用
Set:存储无序的、不可重复的数据

要求:

  1. 向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals(),因为HashSet 集合判断两个元素相等的标准是equals()
  2. 重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
  3. 重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

以HashSet为例说明(HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类):

  1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。(存储位置无序)
  2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。
  3. HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
  4. 底层也是数组,初始容量为16,当如果使用率超过0.75,(16*0.75=12)就会扩大容量为原来的2倍。

Set实现类之一:HashSet

HashSet 具有以下特点:

  1. 不能保证元素的排列顺序
  2. HashSet 不是线程安全的
  3. 集合元素可以是 null
  • 添加元素的过程:以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添加成功。—>情况2

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a

例:

 public void test1(){
        Set set = new HashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

		//迭代器稍后会说
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

注意:
HashSet底层:数组+链表的结构。(JDK 7)
在JDK1.8 增加了红黑树

Set实现类之二:LinkedHashSet

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

LinkedHashSet的使用

  1. LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个
  2. 数据和后一个数据。

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

例:

 @Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

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

Set实现类之三:TreeSet
TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
TreeSet底层使用红黑树结构存储数据

说明:

  1. 向TreeSet中添加的数据,要求是相同类的对象。
  2. 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator),默认情况下, 采用自然排序。
  3. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是通过equals().
  4. 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是通过equals().

自然排序:

TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable接口

判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值

 @Test
    public void test1(){
        TreeSet set = new TreeSet();

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

            //举例一:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);

        //举例二:
        // User类中已经实现了Comparable接口
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


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

    }

定制排序:

定制排序,通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。

@Test
    public void test2(){
        Comparator com = new Comparator() {
            //按照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


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

关于红黑树:https://www.yycoding.xyz/post/2014/3/27/introduce-red-black-tree

四、Iterator迭代器接口
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。

Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。

Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建
Iterator 对象,则必须有一个被迭代的集合

集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

  • 集合元素的遍历操作,使用迭代器Iterator接口
  1. 内部的方法:hasNext() 和 next()
  2. 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,
    默认游标都在集合的第一个元素之前。
  3. 内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()

注意:

  1. 在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。
  2. Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法不是集合对象的remove方法。
  3. 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。

例:

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

        Iterator iterator = coll.iterator();
        //方式一:
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        //报异常:NoSuchElementException。超出了元素范围
//        System.out.println(iterator.next());

        //方式二:不推荐
//        for(int i = 0;i < coll.size();i++){
//            System.out.println(iterator.next());
//        }

        //方式三:推荐
        hasNext():判断是否还有下一个元素
        while(iterator.hasNext()){
            //next():①指针下移 ②将下移以后集合位置上的元素返回
            System.out.println(iterator.next());
        }

    }

除了可以使用Iterator迭代器遍历集合,还可以通过foreach进行遍历

foreach 循环遍历集合元素
Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
遍历集合的底层调用Iterator完成操作。
foreach还可以用来遍历数组。

在这里插入图片描述

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

        //for(集合元素的类型 局部变量 : 集合对象)
        //内部仍然调用了迭代器。
        for(Object obj : coll){
            System.out.println(obj);
        }
    }
    
	public void test2(){
        int[] arr = new int[]{1,2,3,4,5,6};
        //for(数组元素的类型 局部变量 : 数组对象)
        for(int i : arr){
            System.out.println(i);
        }
    }

五、Map接口

  • Map与Collection并列存在。用于保存具有映射关系的数据:key-value
  • Map 中的 key 和 value 都可以是任何引用类型的数据
  • Map 中的 key 用Set来存放,无序不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法。value用Collection集合存放,无序可重复
  • 常用String类作为Map的“键”
  • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
  • Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是 Map 接口使用频率最高的实现类
  • 每对“key-value”在底层是一个Entry对象,使用的Set存储

在这里插入图片描述

@Test
    public void test1(){
        Map map = new HashMap();
//        map = new Hashtable();
        map.put(null,123);

    }

Map实现类之一:HashMap

  1. HashMap是 Map 接口使用频率最高的实现类
  2. 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
  3. 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()
  4. 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
  5. 一个key-value构成一个entry
  6. 所有的entry构成的集合是Set:无序的、不可重复的
  7. HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
  8. HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。

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

HashMap map = new HashMap():
实例化以后,底层创建了长度是16的一维数组Entry[] table
…可能已经执行过多次put…
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据 的哈希值:
        如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
        如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
                如果equals()返回false:此时key1-value1添加成功。----情况3
                 如果equals()返回true:使用value1替换value2。

补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来

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

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

HashMap底层典型属性的属性的说明:

DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

例:

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

        //遍历所有的key集:keySet()
        Set set = map.keySet();
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
        }
        System.out.println();
        //遍历所有的value集:values()
        Collection values = map.values();
        for(Object obj : values){
            System.out.println(obj);
        }
        System.out.println();
        //遍历所有的key-value
        //方式一:entrySet()
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            //entrySet集合中的元素都是entry
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
        System.out.println();
        //方式二:
        Set keySet = map.keySet();
        Iterator iterator2 = keySet.iterator();
        while(iterator2.hasNext()){
            Object key = iterator2.next();
            Object value = map.get(key);
            System.out.println(key + "=====" + value);

        }

    }

Map实现类之二:LinkedHashMa
LinkedHashMap 是 HashMap 的子类
在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

LinkedHashMap的底层实现原理(了解)
源码中:

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);
		}
}

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。
//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
    //因为要按照key进行排序:自然排序 、定制排序
    //自然排序
    @Test
    public void test1(){
        TreeMap map = new TreeMap();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);

        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
    }

    //定制排序
    @Test
    public void test2(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }
                throw new RuntimeException("输入的类型不匹配!");
            }
        });
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);

        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
    }

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一致。

Map实现类之五:Properties

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

在这里插入图片描述

六、Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类

Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

如:
排序操作:(均为static方法)
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

Collections工具类主要是扩充对集合的操作功能,使其更能更加强大。提高编程效率。

Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
例:

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

七、补充:Enumeration

Enumeration 接口是 Iterator 迭代器的 “古老版本”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值