JavaSE 第十章 集合

11.1 Java集合框架概述

  • 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊 端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。
  • 数组在内存存储方面的特点:
    • 数组初始化以后,长度就确定了。
    • 数组声明的类型,就决定了进行元素初始化时的类型
  • 数组在存储数据方面的弊端:
    • 数组初始化以后,长度就不可变了,不便于扩展
    • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数
    • 数组存储的数据是有序的、可以重复的。---->存储数据的特点单一
  • Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的 关联数组。

Java 集合可分为 Collection Map 两种体系

  • Collection接口:单列数据,定义了存取一组对象的方法的集合
    • List:元素有序、可重复的集合
    • Set:元素无序、不可重复的集合
  • Map接口:双列数据,保存具有映射关系“key-value对”的集合

在这里插入图片描述

11.2 Collection接口

  • Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法 既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
  • JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List) 实现。
  • 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都 当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容 器中对象的数据类型。

11.2.1 Collection的对象创建

  • 实际上由于Collection是一个接口,并不能创建对象,只能创建其实现类对象
  • 创建对象:
Collection<E> col = new 具体实现类<E>() ;    // 这里用到的是多态的性质

Collection<String> 集合名 = new ArrayList<>();
Collection<Student> 集合名 = new HashSet<>();
Collection 集合名 = new ArrayList();

: 泛型
作用 : 用来约束集合类元素的数据类型
使用方法 : 创建集合的时候,拿具体的引用数据类型类名替换E即可
注意 :
1. 泛型只能是引用数据类型不可以是基本数据类型
2. 如果不写泛型,泛型默认是Object
3. 等号右边的泛型可以省略,默认和前面一致

11.2.2 Collection接口的方法

  • 添加
    add(Object obj) : 添加一个元素
  • 获取有效元素的个数
    int size()
  • 清空集合
    void clear()
  • 是否是空集合
    boolean isEmpty()
  • 删除
    boolean remove(Object obj) :通过元素的equals方法判断是否是 要删除的那个元素。只会删除找到的第一个元素
  • 转成对象数组
    Object[] toArray()
  • 遍历
    iterator():返回迭代器对象,用于集合遍历
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class Demo1 {
    public static void main(String[] args) {
        Collection<String> col = new ArrayList<>();

        // add()添加方法
        col.add("张三") ;
        col.add("李四") ;
        col.add("王五") ;
        col.add("张三") ;
        System.out.println("col = " + col);
        // size()元素个数
        System.out.println("col.size() = " + col.size());
        // isEmpty()是否为空
        System.out.println("col.isEmpty() = " + col.isEmpty());

        col.remove("张三");
        System.out.println("col = " + col);

        Object[] array = col.toArray();
        System.out.println("Arrays.toString(array) = " + Arrays.toString(array));

        col.clear();

        System.out.println("col = " + col);
    }
}

11.2.3 遍历集合

  1. 转数组 Object[] toArray()
    @Test
    public void test1() {
        Collection<String> col = new ArrayList<>();

        col.add("张三") ;
        col.add("李四") ;
        col.add("王五") ;
        col.add("张三") ;

        Object[] array = col.toArray();
        for (int i = 0; i < array.length; i++) {
            System.out.println("array[" + i + "] = " + array[i]);
        }
    }
  1. 迭代器对象 Iterator iterator()

Iterator : 迭代器
如何获取迭代器对象 : Iterator<E> 对象名 = 集合对象.iterator();
成员方法 :
boolean hasNext() : 查看集合中是否有下一个元素
E next() : 获取集合中的下一个元素

    @Test
    public void test2() {
        Collection<String> col = new ArrayList<>();

        col.add("张三") ;
        col.add("李四") ;
        col.add("王五") ;
        col.add("张三") ;

        Iterator<String> iterator = col.iterator();
        while (iterator.hasNext()) {
            System.out.println("iterator.next() = " + iterator.next());
        }
    }
  1. 增强for循环(foreach)

增强for循环的底层还是迭代器

格式 :

for(容器元素类型 变量名 : 集合名/数组名){
       	//操作变量名 就是在操作集合中的元素
    }
    @Test
    public void test3() {
        Collection<String> col = new ArrayList<>();

        col.add("张三") ;
        col.add("李四") ;
        col.add("王五") ;
        col.add("张三") ;

        for (String s : col) {
            System.out.println("s = " + s);
        }
    }

11.3 Iterator迭代器

11.3.1 ConcurrentModificationException并发修改异常

  • 问题引出:
    当我们在使用集合时,很容易遇到在遍历的过程中对其进行添加、修改、删除的操作

示例:创建一个集合对象,在其中添加四个字符串对象,使用Iterator迭代器遍历,当遍历到元素"李四"时,向list集合中添加一个字符串对象"田七"

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

public class Demo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三") ;
        list.add("李四") ;
        list.add("王五") ;
        list.add("赵六") ;

        Iterator<String> iterator = list.iterator();

        while (iterator.hasNext()) {
            String name = iterator.next();
            System.out.println("name = " + name);
            if (name.equals("李四")) {
                list.add("田七") ;
            }
        }
    }
}
name = 张三
name = 李四
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at cn.pdsu.edu._list.Demo6.main(Demo6.java:18)

想要了解为什么会出现
ConcurrentModificationException这个异常,就要先了解迭代器的底层是如何对集合对象进行遍历的

11.3.2 迭代器底层原理

在这里插入图片描述

知道了迭代器在遍历集合时是不能修改集合内元素的,那么增强for循环也不能,因为增强for循环底层也是用迭代器实现的。

如何解决这一问题呢?
最好的解决方案就是不使用,使用for循环

import java.util.ArrayList;

public class Demo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("张三") ;
        list.add("李四") ;
        list.add("王五") ;
        list.add("赵六") ;

        for (int i = 0; i < list.size(); i++) {
            System.out.println("list.get(i) = " + list.get(i));
            if (list.get(i).equals("李四")) {
                list.add("田七") ;
            }
        }
        System.out.println("list = " + list);
    }
}

结果:

list.get(i) = 张三
list.get(i) = 李四
list.get(i) = 王五
list.get(i) = 赵六
list.get(i) = 田七
list = [张三, 李四, 王五, 赵六, 田七]

11.4 List接口

11.4.1 List接口的方法

List<E>接口 : 元素可重复,元素存取有序,元素有索引的单列集合根节点

对象创建 :
	//以多态的方式创建
	List<E> 集合名 = new List的实现类集合<>();

增删改查四类操作
	增 : 
		boolean add(E e) : 依次添加元素,永远返回true
		
		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
		void clear() : 清空集合中的所有元素
		
		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
	改 : 
		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
	查 : 
		E get(int index) : 获取集合中的指定索引元素
		int size() : 获取集合的长度和元素的个数
		
		boolean contains(Object obj) : 判断集合中是否包含传入的元素
		boolean isEmpty() : 判断集合是否为空集合
		
		//如果不存在,返回-1
		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置

遍历方式 :

  1. 转数组 Object[] toArray()
  2. 迭代器 Iterator iterator()
  3. 增强for 集合名.for
  4. 普通for 集合名.fori
  5. 列表迭代器 ListIterator listIterator()

11.4.2 ArrayList存储自定义对象类型,五种遍历方式

  • 自定义Student类
import java.util.Objects;

public class Student {
    private String name ;
    private int age ;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = 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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 五种遍历集合的方式
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;

public class Demo3 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();

        list.add(new Student("张三" , 20)) ;
        list.add(new Student("李四" , 21)) ;
        list.add(new Student("王五" , 22)) ;

        // 五种遍历集合的方式
        // 转数组遍历
        Object[] array = list.toArray();
        for (int i = 0; i < array.length; i++) {
            System.out.println("array[i] = " + array[i]);
        }

        // 迭代器
        Iterator<Student> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println("iterator.next() = " + iterator.next());
        }

        // 增强for循环
        for (Student student : list) {
            System.out.println("student = " + student);
        }

        // for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println("list.get(i) = " + list.get(i));
        }

        // listIterator迭代器
        ListIterator<Student> lit = list.listIterator();
        while (lit.hasNext()) {
            System.out.println("lit.next() = " + lit.next());
        }

    }
}

11.4.3 关于ArrayList集合中删除多个元素

public class Demo4 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张三") ;
        list.add("张四") ;
        list.add("张五") ;
        list.add("赵四") ;
        list.add("王五") ;
        list.add("李力") ;

        // 倒序删除
        for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i).startsWith("张")) {
                list.remove(i) ;
            }
        }
        // list = [赵四, 王五, 李力]
        System.out.println("list = " + list);
    }

    public static void method2(ArrayList<String> list) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).startsWith("张")) {
                list.remove(i) ;
                i -- ;  // 集合是一个长度变化的容器,删除成功后,list.size()长度减一
            }
        }
        // list = [赵四, 王五, 李力]
        System.out.println("list = " + list);
    }

    public static void method1(ArrayList<String> list) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).startsWith("张")) {
                list.remove(list.get(i)) ;
                list.remove(i) ;
            }
        }
        // list = [张四, 赵四, 王五, 李力]
        System.out.println("list = " + list);
    }
}

11.4.4 关于ArrayList集合中Integer类型元素的删除

public class Demo5 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();

        list.add(1) ;
        list.add(2) ;
        list.add(3) ;
        list.add(4) ;
        list.add(5) ;

        int num = 2 ;

        // 按索引删除    删除的是索引为2位置的元素
        list.remove(num) ;

        // 按元素值删除   删除的是元素值为2的元素
        list.remove(Integer.valueOf(num)) ;

        System.out.println("list = " + list);
    }
}
  • 这里是因为Java中调用方法的最佳适配,集合中的remove()方法是一个重载的方法,boolean remove(Object obj) E remove(int index) ,当我们传入int类型的num时,系统会自动匹配到按照索引删除的方法,可以直接使用num来进行方法的执行,这样是最方便的。如果匹配到按照元素删除的方法,int类型的num需要先进行自动装箱的操作,将int类型转为Integer类型,然后再执行remove()方法。反过来也是这样,传入的是Integer类型,就会优先匹配boolean remove(Object obj)方法

11.4.5 ListIterator列表迭代器

ListIterator<E>列表迭代器 : List集合下特有的迭代器对象
	ListIterator<E>列表迭代器接口 是 Iterator<E>接口的子接口,功能更为强大.但是它属于List专属
	
创建列表迭代器对象的方式 :
	1. List集合名.listIterator();
	2. List集合名.listIterator(索引);  指定列表迭代器对象的开始位置
	
方法 :
	boolean hasNext() : 判断指针右边是否有下一个元素
    E next() : (先校验镜像和原集合的内容是否一致)获取指针右边的元素并移动指针到下一个可停留位置
    void add(E e)  : 列表迭代器添加元素的方法 (先往镜像中添加元素,立刻同步回原集合)
    void set(E e)  : 修改指针指向的那个索引元素(先修改镜像,再同步到原集合)   

在这里插入图片描述

使用这个迭代器也能避免并发修改异常问题

11.4.6 ArrayList集合

ArrayList<E>集合 数据结构是数组结构的List集合的实现;  -> 最常用的元素可重复的单列集合
	//集合命名 : 数据类型 + 集合类型
	
对象创建 :
	ArrayList<E> list = new ArrayList<>();

增删改查四类操作
	增 : 
		boolean add(E e) : 依次添加元素,永远返回true
		
		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
		void clear() : 清空集合中的所有元素
		
		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
	改 : 
		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
	查 : 
		E get(int index) : 获取集合中的指定索引元素
		int size() : 获取集合的长度和元素的个数
		
		boolean contains(Object obj) : 判断集合中是否包含传入的元素
		boolean isEmpty() : 判断集合是否为空集合
		
		//如果不存在,返回-1
		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置
		
遍历方式 :
	1. 转数组 Object[] toArray()
	2. 迭代器 Iterator iterator()
	3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
	4. 普通for 集合名.fori -> 推荐
	5. 列表迭代器  ListIterator listIterator()	

方法同List的方法相同

11.4.7 ArrayList的底层扩容原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

- 结论:
1、当创建一个ArrayList集合时,底层会先创建一个长度为0Object数组
2、当第一次向集合中添加元素时,源码会创建一个长度为10的新数组替换长度为0的旧数组(第一次扩容)
3、当集合添加到第十一个元素时,容积不够,源码会再创建一个长度为15的新数组替换长度为10的老数组(第二次扩容)
4、每当集合底层中的数组元素存满了,源码就会创建一个长度为 (老数组长度 * 1.5) 长度的新数组
    newCapacity = oldCapacity + (oldCapacity >> 1)
5hugeCapacity()方法的源码 :
			(minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;  
		a 如果此时集合底层数组的长度+1 没有超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是MAX_ARRAY_SIZE
		b 如果此时集合底层数组的长度+1 超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是 Integer.MAX_VALUE

当向ArrayList集合中添加第1024个元素值时,此时集合数组的长度为1234。(前提是使用无参构造创建的集合对象)

在这里插入图片描述

  • ArrayList的JDK1.8之前与之后的实现区别?
    • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
    • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组

11.4.8 LinkedList集合

LinkedList<E>集合 : 底层数据结构是双向链表结构
	源码验证 : 
        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;
            }
        }

创建对象的格式,增删改查,遍历方式 和 ArrayList 一致 //链表结构是没有索引的,但是LinkedList集合有索引
对象创建 :
	LinkedList<E> list = new LinkedList<>();

增删改查四类操作
	增 : 
		boolean add(E e) : 依次添加元素,永远返回true
		
		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
		void clear() : 清空集合中的所有元素
		
		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
	改 : 
		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
	查 : 
		E get(int index) : 获取集合中的指定索引元素
		int size() : 获取集合的长度和元素的个数
		
		boolean contains(Object obj) : 判断集合中是否包含传入的元素
		boolean isEmpty() : 判断集合是否为空集合
		
		//如果不存在,返回-1
		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置
		
遍历方式 :
	1. 转数组 Object[] toArray()
	2. 迭代器 Iterator iterator()
	3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
	4. 普通for 集合名.fori -> 推荐
	5. 列表迭代器  ListIterator listIterator()	
        
LinkedList的特殊方法 : 一些关于头尾操作的方法
	 void addFirst(E e)  
     void addLast(E e) 
     E getFirst()  
     E getLast() 
     E removeFirst()  
     E removeLast()  

方法示例:

import java.util.LinkedList;

public class Demo8 {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>() ;

        // LinkedList的add()方法
        linkedList.add("张三") ;
        linkedList.add("李四") ;
        linkedList.add("王五") ;

        System.out.println("linkedList = " + linkedList);   // linkedList = [张三, 李四, 王五]
        
        // addFirst()方法,头插
        linkedList.addFirst("我是头");
        // addLast()方法,尾插
        linkedList.addLast("我是尾");
        System.out.println("linkedList = " + linkedList);   // linkedList = [我是头, 张三, 李四, 王五, 我是尾]

        // getFirst()得到头元素
        System.out.println("linkedList.getFirst() = " + linkedList.getFirst()); // linkedList.getFirst() = 我是头
        // getLast()得到尾元素
        System.out.println("linkedList.getLast() = " + linkedList.getLast());   // linkedList.getLast() = 我是尾

        // removeFirst()删除头
        System.out.println("linkedList.removeFirst() = " + linkedList.removeFirst());   // linkedList.removeFirst() = 我是头
        // 删除尾
        System.out.println("linkedList.removeLast() = " + linkedList.removeLast());     // linkedList.removeLast() = 我是尾
        
        System.out.println("linkedList = " + linkedList);   // linkedList = [张三, 李四, 王五]
    }
}
  • 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

11.4.9 Vector集合

  • Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。

  • 在各种list中,最好把ArrayList作为首选。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。

  • 新增方法:
    void addElement(Object obj)
    void insertElementAt(Object obj,int index)
    void setElementAt(Object obj,int index)
    void removeElement(Object obj)
    void removeAllElements()

11.4.10ArrayList/LinkedList/Vector的异同

请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层 是什么?扩容机制?Vector和ArrayList的最大区别?

  • ArrayList和LinkedList的异同 二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

  • ArrayList和Vector的区别Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

11.5 Set接口

  • Set接口是Collection的子接口,set接口没有提供额外的方法

  • Set接口的特点 : 元素唯一,元素存取无序。Set集合是元素无索引的单列集合根节点

  • Set集合的使用

因为Set是一个接口类型,无法直接创建对象,需要利用多态的性质。

创建对象:Set<E> set = new 具体实现类<>();

增 :
boolean add(E e) : 往集合中添加元素,返回添加是否成功
删 :
boolean remove(Object o) : 按照传入的元素删除集合中的该元素
void clear() : 清空集合中所有的内容

改 : 因为没有索引,所以没有set方法
查 : 因为没有索引,所以没有get方法

int size(): 集合元素个数/集合的长度

boolean contains(Object o) : 是否包含
boolean isEmpty() : 是否为空

遍历
1. 转数组
2. 迭代器
1. 增强for循环

示例代码:

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

public class Demo2 {
    public static void main(String[] args) {
//        创建对象
//        Set<E> 集合名 = new 具体的实现类对象<>();
        Set<String> set = new HashSet<>() ;
        System.out.println("set = " + set);

//        增删改查四类功能
//        增 :
//        boolean add(E e) : 往集合中添加元素,返回添加是否成功
        boolean b = set.add("张三");
        set.add("李四") ;
        set.add("王五") ;
        set.add(null) ;
        boolean b1 = set.add("张三");

        System.out.println("b = " + b);
        System.out.println("b1 = " + b1);
        System.out.println("set = " + set);

//        删 :
//        boolean remove(Object o)  : 按照传入的元素删除集合中的该元素
        boolean b2 = set.remove("王五");
        System.out.println("b2 = " + b2);
        System.out.println("set = " + set);

//        改 : 因为没有索引,所以没有set方法

//        查 : 因为没有索引,所以没有get方法
//        int size() : 集合元素个数/集合的长度
        int size = set.size();
        System.out.println("size = " + size);
//
//        boolean contains(Object o)  : 是否包含
        boolean b3 = set.contains("张三");
        System.out.println("b3 = " + b3);

//        boolean isEmpty()  : 是否为空
        boolean b4 = set.isEmpty();
        System.out.println("b4 = " + b4);

//      遍历
//        1. 转数组
        System.out.println("-----------------------");
        Object[] array = set.toArray();
        for (int i = 0; i < array.length; i++) {
            System.out.println("array[i] = " + array[i]);
        }

//        2. 迭代器
        System.out.println("-----------------------");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println("next = " + next);
        }

//        3. 增强for循环
        System.out.println("-----------------------");
        for (String s : set) {
            System.out.println("s = " + s);
        }

//        void clear() : 清空集合中所有的内容
        set.clear();
        System.out.println("set = " + set);
    }
}

set = []
b = true
b1 = false
set = [null, 李四, 张三, 王五]
b2 = true
set = [null, 李四, 张三]
size = 3
b3 = true
b4 = false
-----------------------
array[i] = null
array[i] = 李四
array[i] = 张三
-----------------------
next = null
next = 李四
next = 张三
-----------------------
s = null
s = 李四
s = 张三
set = []

11.5.2 Set实现类之一:HashSet集合

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

创建对象
HashSet<E> 集合名 = new HashSet<>();
增删改查四类功能
增 :
boolean add(E e) : 往集合中添加元素,返回添加是否成功
删 :
boolean remove(Object o) : 按照传入的元素删除集合中的该元素
void clear() : 清空集合中所有的内容
改 : 因为没有索引,所以没有set方法
查 : 因为没有索引,所以没有get方法
int size() : 集合元素个数/集合的长度

boolean contains(Object o) : 是否包含
boolean isEmpty() : 是否为空
遍历

  1. 转数组
  2. 迭代器
  3. 增强for循环

-向HashSet集合中添加元素的过程:

  • 当向HashSet集合中添加元素时,HashSet集合会调用该对象的HashCode()方法来得到它的HashCode值,然后根据HashCode值再通过某种散列函数决定该元素在HashSet底层数组中的存储位置(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
  • 如果两个元素的HashCode值相等,会再去调用equals()方法,如果equals()方法的结果为true,则添加失败;如果equals()结果为false,则会保存该元素。该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
  • 哈希表:

在这里插入图片描述

  • 重写 hashCode() 方法的基本原则
    • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
    • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
    • 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
  • 重写 equals() 方法的基本原则
    • 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是 要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。
    • 因此,违反了“相等的对象必须具有相等的散列码”。
    • 结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

11.5.3 Set实现类之二:LinkedHashSet

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

public class Demo3 {
    public static void main(String[] args) {
//      创建对象
        LinkedHashSet<String> set = new LinkedHashSet<>() ;
        System.out.println("set = " + set);

//      增删改查四类功能
//      增 :
//      boolean add(E e) : 往集合中添加元素,返回添加是否成功
        boolean b = set.add("张三");
        set.add("李四") ;
        set.add("王五") ;
        set.add(null) ;
        boolean b1 = set.add("张三");

        System.out.println("b = " + b);
        System.out.println("b1 = " + b1);
        System.out.println("set = " + set);

//      删 :
//      boolean remove(Object o)  : 按照传入的元素删除集合中的该元素
        boolean b2 = set.remove("王五");
        System.out.println("b2 = " + b2);
        System.out.println("set = " + set);

//      改 : 因为没有索引,所以没有set方法

//      查 : 因为没有索引,所以没有get方法
//      int size() : 集合元素个数/集合的长度
        int size = set.size();
        System.out.println("size = " + size);

//      boolean contains(Object o)  : 是否包含
        boolean b3 = set.contains("张三");
        System.out.println("b3 = " + b3);

//      boolean isEmpty()  : 是否为空
        boolean b4 = set.isEmpty();
        System.out.println("b4 = " + b4);

//      遍历
//      1. 转数组
        System.out.println("-----------------------");
        Object[] array = set.toArray();
        for (int i = 0; i < array.length; i++) {
            System.out.println("array[i] = " + array[i]);
        }

//      2. 迭代器
        System.out.println("-----------------------");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println("next = " + next);
        }

//      3. 增强for循环
        System.out.println("-----------------------");
        for (String s : set) {
            System.out.println("s = " + s);
        }

//      void clear() : 清空集合中所有的内容
        set.clear();
        System.out.println("set = " + set);
    }
}

set = []
b = true
b1 = false
set = [张三, 李四, 王五, null]
b2 = true
set = [张三, 李四, null]
size = 3
b3 = true
b4 = false
-----------------------
array[i] = 张三
array[i] = 李四
array[i] = null
-----------------------
next = 张三
next = 李四
next = null
-----------------------
s = 张三
s = 李四
s = null
set = []

在这里插入图片描述

11.5.4 Set实现类之三:TreeSet

11.5.4.1 树结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

11.5.4.2 TreeSet集合
  • TreeSet集合 : 底层数据结构是红黑树结构的Set集合
  • 特点 : 元素唯一,元素存取无序,无索引,元素不能为null

创建对象/增删改查/遍历 和 Set 接口中的功能一致的!

  • TreeSet集合可以对元素进行排序,但是排序的规则由程序员提供

常用类型的默认排序规则:
Integer : 默认升序 (由源码提供)
String : 默认升序(字符串中字符的ASCII码值的升序 ) //越靠前的字符ASCII值的优先级越高 (“10” 会排在在 “2” 字符串的前面)

自定义类型的元素存储在TreeSet集合中的排序规则 : 如果元素的类型没有提供排序规则,那么直接报错 (添加不进去)

import java.util.TreeSet;

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

        TreeSet<String> set = new TreeSet<>() ;
        System.out.println("set = " + set);


        set.add("王五") ;
        set.add("张三");
        set.add("李四") ;
        set.add("张三");

        System.out.println("set = " + set);
    }
}

set = []
set = [张三, 李四, 王五]

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

        TreeSet<Integer> set = new TreeSet<>() ;
        System.out.println("set = " + set);


        set.add(55) ;
        set.add(100);
        set.add(3) ;
        set.add(10);
        set.add(80);

        System.out.println("set = " + set);
    }
}

set = []
set = [3, 10, 55, 80, 100]

自定义Student类,没有实现Comparable接口

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

        TreeSet<Student> set = new TreeSet<>() ;

        set.add(new Student("张三" , 19)) ;
        set.add(new Student("李四" , 20));
        set.add(new Student("王五" , 21)) ;

        System.out.println("set = " + set);
    }
}

Exception in thread “main” java.lang.ClassCastException: cn.pdsu.edu._set.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at cn.pdsu.edu._set.Demo4.main(Demo4.java:12)

11.5.5 比较器

11.5.5.1 Comparable比较器
  • 当我们向 TreeSet 中添加元素时,只有第一个元素无须调用compareTo()方法进行比较,后面添加的所有元素都会调用compareTo()方法进行比较。

  • 因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同 一个类的对象。

  • 对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj) 方法比较返回值。

  • 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回0。

对于Integer和String类,它们都有实现Comparable接口,都重写了compareto方法,所以在我们使用TreeSet集合存储Integer或String类型的数据时,它们都会进行一个排序从小到大的排序(升序)。

import java.util.TreeSet;

public class Demo2 {
    public static void main(String[] args) {
        // 创建TreeSet集合存储Integer/String类型的元素,查看其存储的方式
        TreeSet<Integer> treeSet = new TreeSet<>();

        // 乱序添加数据
        treeSet.add(5) ;
        treeSet.add(2) ;
        treeSet.add(4) ;
        treeSet.add(1) ;
        treeSet.add(3) ;

        // 输出集合查看集合内元素的数据
        // treeSet = [1, 2, 3, 4, 5]
        // 对于Integer类型的数据,在TreeSet中就是根据数值的大小进行排序的
        System.out.println("treeSet = " + treeSet);


        TreeSet<String> treeSet1 = new TreeSet<>() ;

        // 添加String类型的数据
        treeSet1.add("123") ;
        treeSet1.add("ABC") ;
        treeSet1.add("abc") ;
        treeSet1.add("wer") ;
        treeSet1.add("888") ;

        // 输出集合内数据查看排序后的顺序
        // treeSet1 = [123, 888, ABC, abc, wer]
        // 对于字符串类型的数据,在TreeSet中是按照Ascall码表进行排序的,
        // 对于中文ascall表中虽然没有,但是在其他的编码中也有中文对应的编码值
        System.out.println("treeSet1 = " + treeSet1);
    }
}
  • 对于没有实现Comparable接口的类来说,是没法将元素存入进TreeSet集合的。

自定义Student类

import java.util.Objects;

public class Student {
    private String name ;
    private int age ;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    /** 省略set/get方法*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

尝试将自定义类Student对象作为元素存入

import java.util.TreeSet;

public class Demo {
    public static void main(String[] args) {
        // 创建TreeSer集合
        TreeSet<Student> set = new TreeSet<>();

        // 向集合中存入元素
        set.add(new Student("张三" , 19)) ;
        set.add(new Student("李四" , 20)) ;
        set.add(new Student("王五" , 21)) ;

        System.out.println("set = " + set);
    }
}

Exception in thread “main” java.lang.ClassCastException: cn.pdsu.edu._test.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at cn.pdsu.edu._test.Demo.main(Demo.java:11)

因为Student类没有实现Comparable接口,在进行类型转换时就直接报错了,所以会有一个java.lang.ClassCastException

  • Student类实现Comparable接口并重写compareTo()方法
import java.util.Objects;

public class Student implements Comparable<Student>{
    
	
	......


    /**
     * 实现Comparable接口中的compareTo()方法
     * @param o     这里的o表示的是集合中存在的老元素,调用compareTo()方法的元素是新元素
     * @return  返回一个int类型的整数,0表示相等,正数表示新元素大于老元素,负数表示新元素小于老元素
     */
    @Override
    public int compareTo(Student o) {
        // 先按年龄进行比较,this - o表示升序
        if (this.age - o.age != 0) return this.age - o.age ;
        // 当年龄相等时按照字符串的排序规则返回
        return  this.name.compareTo(o.name);
    }
}
import java.util.TreeSet;

public class Demo {
    public static void main(String[] args) {
        // 创建TreeSer集合
        TreeSet<Student> set = new TreeSet<>();

        // 向集合中存入元素
        set.add(new Student("张三" , 19)) ;
        set.add(new Student("李四" , 20)) ;
        set.add(new Student("王五" , 21)) ;

        // set = [Student{name='张三', age=19}, Student{name='李四', age=20}, Student{name='王五', age=21}]
        // 年龄升序
        System.out.println("set = " + set);
    }
}
11.5.5.2 Comparator比较器
  • 对于Comparable比较器来说,我们需要实现Comparable接口并重写其抽象方法,类与接口之间有较强的耦合性。且对于Integer类和String类来说,它们的排序规则是无法改变的,因为无法继承这两个类对其compareTo()方法进行重写,所以有些不灵活。这时候我们就可以考虑使用Comparator比较器来对对象元素编写一套排序规则。

  • Comparator比较器 : 独立存在的比较器,给容器提供排序规则. 容器内的元素按照容器给的排序规则进行排序Comparator比较器的优先级比Comparable比较器高;

修改Integer和String的排序规则

import java.util.Comparator;
import java.util.TreeSet;

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

        // 使用Comparator比较器匿名内部类,实现compare()方法,对Integer类型的元素进行降序排序

        TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        treeSet.add(5) ;
        treeSet.add(2) ;
        treeSet.add(4) ;
        treeSet.add(1) ;
        treeSet.add(3) ;

        // treeSet = [5, 4, 3, 2, 1] 降序排列
        System.out.println("treeSet = " + treeSet);

        TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        }) ;

        set.add("123") ;
        set.add("ABC") ;
        set.add("abc") ;
        set.add("wer") ;
        set.add("888") ;

        // set = [wer, abc, ABC, 888, 123] 降序排列
        System.out.println("set = " + set);
    }
}

Student的排序规则

import java.util.Comparator;
import java.util.TreeSet;

public class Demo {
    public static void main(String[] args) {
        // 创建TreeSer集合
        TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {

            @Override
            public int compare(Student o1, Student o2) {
                return o2.getAge() - o1.getAge() ;
            }
        });

        // 向集合中存入元素
        set.add(new Student("张三" , 19)) ;
        set.add(new Student("李四" , 20)) ;
        set.add(new Student("王五" , 21)) ;

        // set = [Student{name='王五', age=21}, Student{name='李四', age=20}, Student{name='张三', age=19}]
        // 年龄降序
        System.out.println("set = " + set);
    }
}

11.5.6 Set集合中的两个注意点

import java.util.HashSet;

public class Demo5 {
    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();
        /**
         * 在HashSet集合中存储数据时,是按照hash值来确定位置的,在存储p1时,是按照  1001,“AA”来计算hash值的
         * 而在下面对其内容进行了修改,当调用remove()方法删除p1元素时,需要先计算hash值,而此时hash值的计算是按照1001,“CC”来的
         * 但是并不能找到以1001,“AA”计算的hash值,所以删除失败,p1仍存在
         *
         * 而在存储下面的  1001,“CC”  时,hash值是按1001,“CC”计算的,虽然存在相同的元素内容,但是根本不会调用到equals()方法
         * 存储成功,而存储   1001,“AA”时,equals()方法比较结果为false,存储成功
         */
        Person p1 = new Person(1001,"AA");
        Person p2 = new Person(1002,"BB");
        set.add(p1);
        set.add(p2);
        p1.name = "CC";
        boolean remove = set.remove(p1);
//        System.out.println("remove = " + remove); // false
        System.out.println(set);
        set.add(new Person(1001,"CC"));
        System.out.println(set);
        set.add(new Person(1001,"AA"));
        System.out.println(set);

        /**
         * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
         * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
         * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
         */
    }
}
去重

使用Set集合对list集合中的元素进行去重

import java.util.ArrayList;
import java.util.HashSet;

public class Demo6 {
    public static void main(String[] args) {
        // 去除List集合中的重复数值
        ArrayList<Integer> list = new ArrayList<>();

        list.add(5) ;
        list.add(5) ;
        list.add(1) ;
        list.add(1) ;
        list.add(2) ;

        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.addAll(list);
        ArrayList<Integer> list1 = new ArrayList<>();

        list1.addAll(hashSet) ;
        System.out.println("list1 = " + list1);
    }
}

list1 = [1, 2, 5]

11.6 Map接口

11.6.1 Map接口继承树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.6.2 Map接口概述
  • Map与Collection并列存在。用于保存具有映射关系的数据:key-value

  • Map 中的 key 和 value 都可以是任何引用类型的数据

  • Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应 的类,须重写hashCode()和equals()方法 - 常用String类作为Map的“键”

  • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到 唯一的、确定的 value

  • Map接口的常用实现类:HashMap、TreeMap、LinkedHashMapProperties。其中,HashMap是 Map 接口使用频率最高的实现类

在这里插入图片描述

  • 添加、删除、修改操作:
    • 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中的所有数据
  • 元素查询的操作:
    • 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是否相等
  • 元视图操作的方法:
    • Set keySet():返回所有key构成的Set集合
    • Collection values():返回所有value构成的Collection集合
    • Set entrySet():返回所有key-value对构成的Set集合
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

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

        // 创建Map集合的实现类对象
        Map<String , String> map = new HashMap<>() ;

        System.out.println("map = " + map);
        System.out.println("----------------------");

        // 添加元素
        map.put("key1" , "value1") ;
        map.put("key2" , "value2") ;
        map.put("key3" , "value3") ;
        map.put("key4" , "value4") ;
        map.put("key5" , "value5") ;

        System.out.println("map = " + map);
        System.out.println("----------------------");

        // 修改元素
        // 修改元素和添加元素都是使用的put()方法
        map.put("key1" , "修改后") ;
        map.put("key3" , "修改后") ;

        System.out.println("map = " + map);
        System.out.println("----------------------");

        // 查找元素
        String key1 = map.get("key1");
        String key2 = map.get("key2");

        System.out.println("key1 = " + key1);
        System.out.println("key2 = " + key2);
        System.out.println("----------------------");

        // 删除元素
        map.remove("key1") ;
        map.remove("key3") ;

        System.out.println("map = " + map);
        System.out.println("----------------------");

        // 查找Map集合中是否存在指定的key/value
        boolean b = map.containsKey("key2");
        boolean b1 = map.containsValue("value2");

        System.out.println("b = " + b);
        System.out.println("b1 = " + b1);
        System.out.println("----------------------");

        // 判断当前map集合是否为空
        boolean empty = map.isEmpty();
        System.out.println("empty = " + empty);
        System.out.println("----------------------");

        // 返回当前map集合中key-value的个数
        int size = map.size();
        System.out.println("size = " + size);
        System.out.println("----------------------");

        // 返回所有key构成的set集合
        Set<String> set1 = map.keySet();
        System.out.println("set1 = " + set1);
        System.out.println("----------------------");

        // 返回所有value构成的set集合
        Collection<String> set2 = map.values();
        System.out.println("set2 = " + set2);
        System.out.println("----------------------");

        // 返回所有key-value对构成的Set集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        System.out.println("entries = " + entries);

    }
}

map = {}
----------------------
map = {key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
----------------------
map = {key1=修改后, key2=value2, key5=value5, key3=修改后, key4=value4}
----------------------
key1 = 修改后
key2 = value2
----------------------
map = {key2=value2, key5=value5, key4=value4}
----------------------
b = true
b1 = true
----------------------
empty = false
----------------------
size = 3
----------------------
set1 = [key2, key5, key4]
----------------------
set2 = [value2, value5, value4]
----------------------
entries = [key2=value2, key5=value5, key4=value4]

11.6.3 Map实现类之一:HashMap

11.6.3.1 HashMap的特点
  • HashMap是 Map 接口使用频率最高的实现类。
  • 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
  • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写: equals()和hashCode()
  • 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类 要重写:equals()
  • 一个key-value构成一个entry
  • 所有的entry构成的集合是Set:无序的、不可重复的
  • HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true, hashCode 值也相等。
  • HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
11.6.3.2 HashMap的存储结构
  • JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
  • JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。

在这里插入图片描述

11.6.3.3 HashMap集合的方法

HashMap实现了Map接口的方法

  • 创建对象
    • HashMap<键集元素类型,值集元素类型> 集合名 = new HashMap<>();
  • 增删改查
    • 增 / 改 :

      • V put(K key,V value) : 添加一对映射关系,添加成功返回null; 添加失败返回oldValue
    • 删 :

      • V remove(K key) : 根据传入的键删除集合中的这一对映射关系,并返回这对映射关系的值
      • boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
      • void clear() : 清空集合中所有的映射关系
    • 查 :

      • V get(K key) : 根据传入的键获取此键对应的值 ;

      • int size() : 返回集合中有几对元素

      • boolean containsKey(K key) : 判断是否存在传入的键

      • boolean containsValue(V value) : 判断是否存在传入的值

      • Set<K> keySet() : 获取双列集合的键集

      • Collection<V> values() : 获取双列集合的值集

  • 遍历
    • Set<Map.Entry<K,V>> entrySet() :获取到HashMap集合中的所有Entry对象,从Entry对象中得到key和value值de
    • 通过keySet()方法先获取Map集合中的所有key集,然后根据key来获取value
public class Demo3 {
    public static void main(String[] args) {
        // 创建HashMap对象
        Hashtable<String, String> map = new Hashtable<>();

        // 添加元素
        map.put("张三" , "男") ;
        map.put("李四" , "男") ;
        map.put("王五" , "女") ;
        map.put("赵六" , "女") ;

        System.out.println("map = " + map);
        System.out.println("==========================");

        // 修改元素
        map.put("张三" , "女") ;

        System.out.println("map = " + map);
        System.out.println("==========================");

        // 删除元素
        map.remove("张三") ;

        System.out.println("map = " + map);
        System.out.println("==========================");

        // 查找元素
        String s = map.get("李四");

        System.out.println("s = " + s);
        System.out.println("==========================");

        // 返回集合中存在几对元素
        int size = map.size();

        System.out.println("size = " + size);
        System.out.println("==========================");

        // 判断key/value是否在集合中存在
        boolean b = map.containsKey("李四");
        boolean b1 = map.containsValue("女");

        System.out.println("b = " + b);
        System.out.println("b1 = " + b1);
        System.out.println("==========================");

        // 获取Map集合中的键集
        Set<String> keys = map.keySet();
        System.out.println("keys = " + keys);

        System.out.println("==========================");

        // 获取Map集合中的值集
        Collection<String> values = map.values();
        System.out.println("values = " + values);
    }
}

map = {赵六=女, 王五=女, 张三=男, 李四=男}
==========================
map = {赵六=女, 王五=女, 张三=女, 李四=男}
==========================
map = {赵六=女, 王五=女, 李四=男}
==========================
s = 男
==========================
size = 3
==========================
b = true
b1 = true
==========================
keys = [赵六, 王五, 李四]
==========================
values = [女, 女, 男]

  • 遍历Map集合内的元素
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

public class Demo4 {
    public static void main(String[] args) {
        // 创建HashMap对象
        Hashtable<String, String> map = new Hashtable<>();

        // 添加元素
        map.put("张三" , "男") ;
        map.put("李四" , "男") ;
        map.put("王五" , "女") ;
        map.put("赵六" , "女") ;

        // 遍历Map集合的元素
        // 第一种方法:先获取Map集合中的键集,根据键集来获取值集,遍历集合
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            String value = map.get(key);
            System.out.println(key + "-----" + value);
        }

        System.out.println("-----------------");

        // 第二种方法:获取所有的Entry对象,从entry对象中获取key/value值
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();

            System.out.println(key + "-----" + value);
        }

        /**
         * 赵六-----女
         * 王五-----女
         * 张三-----男
         * 李四-----男
         * -----------------
         * 赵六-----女
         * 王五-----女
         * 张三-----男
         * 李四-----男
         */
    }
}

11.6.4 Map实现类之二:LinkedHashMap

`- LinkedHashMap 是 HashMap 的子类

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

11.6.5 Map实现类之三:TreeMap

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

11.6.6 Map实现类之四:Hashtable

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

11.6.7 Map实现类之五:Properties

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

11.7 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 处元素进行交换
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo1 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();

        arrayList.add("张三") ;
        arrayList.add("李四") ;
        arrayList.add("王五") ;
        arrayList.add("赵六") ;
        arrayList.add("田七") ;

        // arrayList = [张三, 李四, 王五, 赵六, 田七]
        System.out.println("arrayList = " + arrayList);

        // 调用Collections工具类中的方法操作ArrayList集合

        // 集合反转
        Collections.reverse(arrayList);
        // arrayList = [田七, 赵六, 王五, 李四, 张三]
        System.out.println("arrayList = " + arrayList);

        // 对ArrayList集合元素进行随机排序
        Collections.shuffle(arrayList);
        // arrayList = [赵六, 田七, 李四, 王五, 张三]
        System.out.println("arrayList = " + arrayList);

        // 对ArrayList集合中的元素进行排序(默认排序规则)
        Collections.sort(arrayList);
        // arrayList = [张三, 李四, 王五, 田七, 赵六]
        System.out.println("arrayList = " + arrayList);

        // 对ArrayList集合中的元素进行排序(自定义排序规则排序规则)
        Collections.sort(arrayList, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        // arrayList = [赵六, 田七, 王五, 李四, 张三]
        System.out.println("arrayList = " + arrayList);

        // 交换指定位置的元素
        Collections.swap(arrayList , 0 , arrayList.size() - 1);
        // arrayList = [张三, 田七, 王五, 李四, 赵六]
        System.out.println("arrayList = " + arrayList);
    }
}

Collections常用方法

  • 查找、替换
    • 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 对象的所有旧值
  • Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值