集合汇总

1.集合的理解与好处

1.1数组

  1. 长度开始时必须指定,而且一旦指定,不能更改

  2. 保存的必须为同一类型的元素

  3. 使用数组进行增加、删除元素比较麻烦

    //写出Person数组的扩容示意代码
    Person[] pers=new Person[1];//定义长度为1
    pers[0]=new Person();
    
    //增加新的Person对象?
    Person[] pers2=new Person[pers.length+1];//新创建数组
    for(){}//拷贝pers数组的元素到pers2
    pers2[pers2.length-1]=new Person();//添加新的对象集合
    

1.2集合

  1. 可以动态保存任意多个对象,使用比较方便
  2. 可以提供一系列方便的操作对象的方法:add、remove、set、get等
  3. 使用集合添加、删除新元素的示意代码-简洁了

2.集合的框架体系

单列集合

image

双列集合【键值对】

image

3.Collection接口和常用方法

3.1Collection接口实现类的特点

public interface Collection<E> extends Iterable<E>
  1. Collection实现子类可以存放多个元素,每个元素可以是Object
  2. 有些Collection的实现类,可以存放重复的元素,有些不可以
  3. 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
  4. Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的

3.2Collection遍历方式

  1. Iterator迭代器

    1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素

    2. 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器

    3. Iterator仅用于遍历集合,Iterator本身并不存放对象

    4. 提示:在调用iterator.next()方法之前,必须要调用iterator.hasNext()进行检查,若不调用,且下一条记录无效,则直接调用iterator.next()会抛出NoSuchElementException异常。

    5. //迭代器执行原理
      Iterator iterator = coll.iterator();//得到一个集合的迭代器
      //hasNext()://判断是否还有下一个元素
      while(iterator.hasNext()){
          //next();//①指针下移,②将下移以后集合位置上的元素返回
          Object next=iterator.next();//返回下一个元素,类型是Object
          System.out.println(iterator.next());
      }
      //生成while快捷键itit  
      
  2. 增强for循环

    1. 增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质是一样的,增强for循环只能用于遍历集合数组

    2. 增强for循环,底层仍然是迭代器;可以debug进入for里面

    3. 基本用法

      //基本用法
      for(元素类型 元素名:集合名或数组名){
          访问元素
      }
      //增强for,用于集合上
      Collection col =new ArrayList();
      col.add(new Book(){"三国演义","罗贯中",20.2});
      col.add(new Book(){"西游记","吴承恩",18.9});
      for(Object book:col){
          System.out.println(book);
      }
      
      //增强for,用于数组上
      int[] nums={1,23,5,67,45};
      for(int i:nums){
          System.out.println(i);
      }
      

4.List接口和常用方法

4.1 List接口基本介绍

  1. List接口是Collection接口的子接口
  2. List集合类中元素有序(即添加顺序和取出顺序一致且可重复)
  3. List集合中的每个元素都有其对应的顺序索引,即支持索引【索引从0开始】
  4. List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

4.2List常用方法

以ArrayList模拟List中的方法

add()、addAll()方法

public static void main(String[] args) {
    List list=new ArrayList();
    list.add("hytour");
    list.add("cct");

    //1.在指定index位置插入元素,在hytour和cct之间插入bsm
    list.add(1,"bsm");
    System.out.println(list);//[hytour, bsm, cct]

    //2.在指定位置添加集合
    List list2=new ArrayList();
    list2.add("刀剑笑");
    list2.add("韦大宝");
    list.addAll(1,list2);//在hytour与bsm之间添加list2集合
    System.out.println(list);//[hytour, 刀剑笑, 韦大宝, bsm, cct]
}

get()方法

public static void main(String[] args) {
    //get属于 List 接口中提供方法
    List list=new ArrayList();
    list.add("hytour");
    list.add("cct");

    //获取索引为1的元素
    System.out.println(list.get(1));//cct

    //如果获取元素时,索引超过了集合的索引长度,则直接抛出异常
    Object obj=list.get(2);//抛出异常
    System.out.println(obj);
}

indexOf()、lastIndexOf()方法

public static void main(String[] args) {
    //indexOf、lastIndexOf属于 List 接口中提供方法
    List list=new ArrayList();

    list.add("刀剑笑");
    list.add("银蛟龙");
    list.add("韦大宝");
    list.add("银蛟龙");
    list.add("张三");
    list.add("银蛟龙");
    list.add("李四");

    //银蛟龙首次出现的位置,如果此集合不包含该元素,则返回 -1
    System.out.println(list.indexOf("银蛟龙"));//1
    System.out.println(list.indexOf("jh"));//-1

    //银蛟龙最后一次出现的位置,如果此集合不包含该元素,则返回 -1
    System.out.println(list.lastIndexOf("银蛟龙"));//5
    System.out.println(list.lastIndexOf("rx"));//-1
}

set()方法

public static void main(String[] args) {
    //set属于 List 接口中提供方法
    List list = new ArrayList();

    list.add("小三");
    list.add("小四");
    list.add("小五");

    System.out.println(list);//[小三, 小四, 小五]
    //将指定index位置的元素指定为当前值,将index=1位置的值设置为“刀剑笑”,但是返回值还是原来的值
    Object obj = list.set(1, "刀剑笑");
    System.out.println(obj);//小四
    System.out.println(list);//[小三, 刀剑笑, 小五]
}

subList()方法

public static void main(String[] args) {
    //subList属于 List 接口中提供方法
    List list=new ArrayList();

    list.add(1);//index=0
    list.add(2);//index=1
    list.add(3);//index=2
    list.add(4);//index=3
    list.add(5);//index=4
    list.add(6);//index=5
    list.add(7);//index=6
    list.add(8);//index=7
    list.add(9);//index=8
    list.add(10);//index=9


    //List subList(int fromIndex,int toIndex);返回从fromIndex到toIndex位置的子集合
    //注意返回的子集合 fromIndex <=subList < toIndex
    List new_list=list.subList(3,6);
    System.out.println(new_list);//[4, 5, 6]
}

isEmpty()方法

//===========声明List变量并初始化,添加元素==========================
List list = new ArrayList();

list.add("刀剑笑");
list.add("银蛟龙");
list.add(true);
list.add(10);

System.out.println(list.size());//4
System.out.println(list.isEmpty());//false,如果集合添加了元素,则返回false

//==========声明List变量并初始化,未添加元素===========================
List list2 = new ArrayList();
System.out.println(list2.size());//0
System.out.println(list2.isEmpty());//true,如果集合未添加任何元素,则返回true

//==========声明List变量未初始化===========================
List list3=null;//定义集合list3未进行初始化操作
System.out.println(list3.size());//如果list3是null,直接调用list3.size()则会报异常
System.out.println(list3.isEmpty());//如果list3是null,直接调用list3.isEmpty()则会报异常

clear()方法

List list = new ArrayList();

list.add("刀剑笑");
list.add("银蛟龙");
list.add(true);
list.add(10);

System.out.println(list.size());//4
System.out.println(list.isEmpty());//false,如果集合添加了元素,则返回false

list.clear();//list进行clear之后,是一个空集合,不是null

System.out.println(list.size());//0
System.out.println(list);//[]
System.out.println(list.isEmpty());//true

addAll()方法

List list = new ArrayList();

list.add("xiaosan");
list.add("刀剑笑");
list.add(123);
System.out.println(list);//[xiaosan, 刀剑笑, 123]

List list2 = new ArrayList();
list2.add("三国演义");
list2.add("西游记");
list2.add(123);

list.addAll(list2);//将集合list2的所有元素都添加到集合list中
System.out.println(list);//[xiaosan, 刀剑笑, 123, 三国演义, 西游记, 123]

contains()方法

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

    list.add("热血江湖");
    list.add("CF");

    //contains  list存在则返回true,list不存在则返回false  【严格区分大小写】
    
    boolean res = list.contains("Cf");
    System.out.println(res);//false
    boolean res2 = list.contains("CF");
    System.out.println(res2);//true
}

containsAll()方法

//boolean containsAll(Collection c)	判断集合中是否包含集合 c 中的所有元素
List list = new ArrayList();

list.add("xiaosan");
list.add("刀剑笑");
list.add(123);

System.out.println(list);//[xiaosan, 刀剑笑, 123]
List list2 = new ArrayList();
list2.add("三国演义");
list2.add("西游记");
list2.add(123);

list.addAll(list2);
System.out.println(list);//[xiaosan, 刀剑笑, 123, 三国演义, 西游记, 123]

//==========list3中的元素“红楼梦”,不在list中,所以返回false==========
List list3 = new ArrayList();
list3.add("三国演义");
list3.add("西游记");
list3.add("红楼梦");
System.out.println(list3);//[三国演义, 西游记, 红楼梦]
System.out.println(list.containsAll(list3));//false  如果list3的所有元素都在list中,则返回true;只要list3中有一个元素不在list中,则返回false

//==========list3中的元素全部都在list中,所以返回true==========
List list3 = new ArrayList();
list3.add("三国演义");
list3.add("西游记");
System.out.println(list3);//[三国演义, 西游记]
System.out.println(list.containsAll(list3));//true  如果list3的所有元素都在list中,则返回true;只要list3中有一个元素不在list中,则返回false

remove()方法

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

    list.add("三国演义");
    list.add("西游记");
    list.add("红楼梦");
    list.add("水浒传");

    //删除指定index位置的元素,并返回此元素
    Object obj=list.remove(0);//删除索引为0的元素
    System.out.println(obj);//三国演义
    System.out.println(list);//[西游记, 红楼梦, 水浒传]

    //删除指定的元素,删除成功返回true,否则返回false
    Object obj2=list.remove("红楼梦");
    System.out.println(obj2);//true
    System.out.println(list);//[西游记, 水浒传]

    //====================================================
    //如果集合中包含多个相同的元素,根据指定元素删除时,删除的只是第一次出现的元素
    List list =new ArrayList();

    list.add("三国演义");
    list.add("西游记");
    list.add("水浒传");
    list.add("红楼梦");
    list.add("水浒传");
    list.add("热血江湖");

    System.out.println(list);//[三国演义, 西游记, 水浒传, 红楼梦, 水浒传, 热血江湖]
    list.remove("水浒传");
    System.out.println(list);//[三国演义, 西游记, 红楼梦, 水浒传, 热血江湖]
}

removeAll()方法

//boolean removeAll(Collection c)	从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。
List list=new ArrayList();

list.add(123);
list.add("小三");
list.add("西游记");
list.add("三国演义");
list.add("刀剑笑");
list.add("三国演义");
System.out.println(list);//[123, 小三, 西游记, 三国演义, 刀剑笑, 三国演义]

//===========list2集合中的元素都在list集合中========
List list2=new ArrayList();
list2.add("三国演义");
list2.add("刀剑笑");
System.out.println(list2);//[三国演义, 刀剑笑]

//如果list2中的元素,在list中出现多个,则list中的多个元素都会被删除,比如:三国演义
System.out.println(list.removeAll(list2));//true
System.out.println(list);//[123, 小三, 西游记]

//===========list3集合中的元素不都在list集合中========
List list3=new ArrayList();
list3.add("三国演义");
list3.add("刀剑笑");
list3.add("红楼梦");
System.out.println(list3);//[三国演义, 刀剑笑, 红楼梦]

//如果list3中的元素,部分出现在list集合中,则在list中的元素会被删除,如果出现多个,则多个都会被删除,比如:三国演义
System.out.println(list.removeAll(list3));//true
System.out.println(list);//[123, 小三, 西游记]

 //===========list4集合中的元素都不在list集合中,则返回false========
List list4=new ArrayList();
list4.add("热血江湖");
list4.add("CF");
System.out.println(list4);

//如果list4中有一个元素在list集合中,则返回true;如果list4中的所有元素都不在list集合中,则返回false
System.out.println(list.removeAll(list4));//false
System.out.println(list);//[123, 小三, 西游记, 三国演义, 刀剑笑, 三国演义]

retainAll()方法

public static void main(String[] args) {
    //boolean retainAll(Collection c)  从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),
    // 如果该操作改变了调用该方法的集合,则该方法返回 true。
    List list = new ArrayList();

    list.add("三国演义");
    list.add("红楼梦");
    list.add("西游记");
    list.add("水浒传");
    System.out.println(list);//[三国演义, 红楼梦, 西游记, 水浒传]

    List list2 = new ArrayList();
    list2.add("三国演义");
    list2.add("西游记");
    System.out.println(list2);//[三国演义, 西游记]

    boolean res = list.retainAll(list2);
    System.out.println(res);//true
    System.out.println(list);//[三国演义, 西游记]  因为c集合中不包含“红楼梦"、"水浒传" 所以会把这两项删除了
}

4.3List的三种遍历方式

List的三种实现[ArrayList、LinkedList、Vector]

//这是一个程序入口
public static void main(String[] args) {
    List list=new ArrayList();

    list.add(123);
    list.add("门柱");
    list.add("热血江湖");
    list.add("西游记");
    list.add("person");

    //====================迭代器循环==============================
    Iterator iterator = list.iterator();
    //快捷键itit
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        System.out.println(obj);
    }
    
    //====================增强for循环==============================
    //快捷键大写字母I
    for (Object obj:list) {
        System.out.println(obj);
    }

    //====================普通for循环==============================
    //快捷键fori
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

4.4集合冒泡排序

public class Book{
    public String name;
    public String author;
    public double price        
}
//要求价格由小到大排序
public static void sort(List list){
    int listSize = list.size();
    for(int i = 0;i < listSize-1;i++){
        for(int j=0;j<listSize-1-i;j++){
            Book book1 = (Book)list.get(j);
            Book book2 = (Book)list.get(j+1);
            if(book1.getPrice()>book2.getPrice()){
                list.set(j,book2);
                list.set(j+1,book1);
            }
        }
    }
}

5.ArrayList底层结构和源码分析

5.1ArrayList的注意事项

  1. ArrayList可以添加任何元素,包括null,可以加入多个null
  2. ArrayList是由数组来实现数据存储的
  3. ArrayList是线程不安全的(执行效率高),在多线程情况下,不建议使用ArrayList

5.2ArrayList的底层操作机制源码分析

  1. ArrayList中维护了一个Object类型的数组elementData

    transient Object[] elementData;//transient 修饰的元素不会被序列话,比如ArrayList被序列化时,elementData属性不会被序列化
    
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加元素时,则扩容elementData为10,如果再次扩容,则扩容elementData为1.5倍。

    0->10->10 * 1.5=15->15 * 1.5 = 22 ->22 * 1.5 =33

  3. 当创建ArrayList对象时,如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍

    ArrayList arrayList = new ArrayList(5);//elementData容量初始大小为5
    
  4. 数组保留原先数据扩容Arrays.copyOf

    int arr = {1,2,3,4,5};
    arr = Arrays.copyOf(arr,10);//将arr扩容,由长度为5扩容到长度为10
    //此时arr的内容为
    {1,2,3,4,5,0,0,0,0,0}
    
  5. 具体源码跟踪可参考

    关于ArrayList源码跟踪 - 南晓东 - 博客园 (cnblogs.com)

6.Vector底层结构和源码剖析

6.1Vctor的基本介绍

  1. Vector的定义

    public class Vector<E>
        extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
  2. Vector底层也是一个对象数组

    protected Object[] elementData;
    
  3. Vector是线程同步的,即线程安全的,Vector类的操作方法带有synchronized关键字,比如:get

    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
    
        return elementData(index);
    }
    
  4. 在开发中,需要线程同步安全时,考虑是用Vector

6.2Vector与ArrayList的比较

底层结构 版本 线程安全(同步)效率 扩容倍数
ArrayList 可变数组 jdk1.2 不安全,效率高 如果有参构造1.5倍;如果是无参,1.第一次10,2.从第二次开始按1.5倍扩容
Vector 可变数组 jdk1.0 安全,效率不高 如果是无参,默认是10,满后就按2倍扩容;如果有参数指定大小,则每次直接按2倍扩容

7.LinkedList底层结构

7.1LinkedList底层操作机制

  1. 可以添加任意元素(元素可以重复),包括null
  2. 线程不安全,没有实现同步
  3. LinkedList底层维护了一个双向链表
  4. LinkedList中维护了两个属性first和last分别指向首节点和尾节点
  5. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个节点,通过next指向后一个节点,最终实现双向链表
  6. LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

7.2源码分析

  1. 内部结构

    //定义了一个Node类对象
    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;
        }
    }
    
    //定义了主要属性
    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;
    
  2. 添加元素,add()方法

    //执行初始化操作
    LinkedList linkedList = new LinkedList();
    //内部执行
    public LinkedList(){}  //此时linkedLast的属性  first=null  last=null
    
    //执行添加元素操作
    linkedList.add(1);
    
    //内部执行
     public boolean add(E e) {
         linkLast(e);
         return true;
     }
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    
  3. 删除元素,remove()方法

    linkedList.remove();//默认删除第一个元素
    linkedList.remove(index);//删除指定索引的元素
    linkedList.remove(Object);//删除指定的元素
    
    //=================================源码分析=====================================
    linkedList.remove(index);//删除指定索引的元素
    
    //内部执行
    public E remove(int index) {
        checkElementIndex(index);//检查索引是否正常
        return unlink(node(index));//1.根据索引获取到元素   2.调用删除方法
    }
    
    //检查索引是否正常
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))//判断是否为链表索引
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //判断是否为链表索引
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    
    //根据索引获取到链表中的元素
    Node<E> node(int index) {
        // assert isElementIndex(index);
    	//非常巧妙的 中间分组算法
        if (index < (size >> 1)) {//size >> 1 =>size/2
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;   //如果index=5,循环只能执行到i=4,此时x.next就相当于是i=5的元素
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    
    //调用删除方法
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
    
        //prev == null说明x是第一个元素,如果要删除第一个元素,则需要把first指向第二个元素,即x.next
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;//前一个元素的后一个结点,指向x的下一个结点,把x这个要删除的结点跳过
            x.prev = null;//当前要删除的x结点,就不需要指向前一个结点了
        }
    	//next == null 说明x是最后一个元素,如果要删除最后一个元素,则需要把last指向倒数第二个元素,即x.prev
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;//后一个元素的前一个结点,指向x的前一个结点,把x这个要删除的结点跳过
            x.next = null;//当前要删除的x结点,就不需要指向后一个结点了
        }
    
        x.item = null;//将当前要删除的x结点,置为null
        size--;//链表元素总个数减一
        modCount++;//修改次数+1
        return element;
    }
    
  4. 修改元素,set()方法

    //LinkedList修改某个结点对象
    linkedList.set(2,"xiaosi");
    
    
    //循环方式,因为LinkedList实现了List接口,遍历方式
    //1.迭代器循环->快捷键itit
    Iterator iterator = linkedList.iterator();
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        System.out.println(obj);            
    }
    //2.增强for循环->快捷键大写字母I
    for (Object obj :linkedList) {
        System.out.println(obj);
    }
    //3.普通for循环
    for (int i = 0; i < linkedList.size(); i++) {
        Object obj=linkedList.get(i);
        System.out.println(obj);
    }
    
  5. 获取元素,get()方法

    //得到LinkedList的第二个对象
    Object obj=linkedList.get(1);
    
  6. LInkedList循环

    //循环方式,因为LinkedList实现了List接口,遍历方式
    //1.迭代器循环->快捷键itit
    Iterator iterator = linkedList.iterator();
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        System.out.println(obj);            
    }
    //2.增强for循环->快捷键大写字母I
    for (Object obj :linkedList) {
        System.out.println(obj);
    }
    //3.普通for循环
    for (int i = 0; i < linkedList.size(); i++) {
        Object obj=linkedList.get(i);
        System.out.println(obj);
    }
    

7.3模拟双向链表

//定义一个Node类,Node 对象 表示双向链表的一个结点
public class Node{
    public Object item;//真正存放数据
    public Node next;//指向后一个结点
    public Node pre;//指向前一个结点
    public Node(Object name){
        this.item = name;
    }
    public String toString(){
        return "Node name="+item;
    }
}
public static void main(String[] args){
    //模拟一个简单的双向链表
    Node jack = new Node("jack");
    Node tom = new Node("tom");
    Node xiaosan = new Node("xiaosan");
    
    //连接三个结点,形成双向链表
    //jack -> tom ->xiaosan
    jack.next = tom;
    tom.next = xiaosan;
    //xiaosan -> tom -> jack
    xiaosan.pre = tom;
    tom.pre = jack;
    
    //定义双向链表的头结点
    Node first = jack;//让first引用指向jack
    
    //定义双向链表的尾结点
    Node last = xiaosan;//让last引用指向xiaosan
    
    //从头到尾进行遍历
    while(true){
        if(first == null){
            break;
        }
        //输出first信息
        System.out.println(first);
        first = first.next;//需要重点理解
    }
    
    //从尾到头进行遍历
    while(true){
        if(last == null){
            break;
        }
        //输出last信息
        System.out.println(last);
        last = last.pre;//需要重点理解
    }
    
    //在tom和xiaosan之间,插入一个对象   smith
    //1.先创建一个Node结点,name就是smith
    Node smith = new Node("smith");
    //2.下面就把smith加入到双向链表了
    smith.next=xiaosan;
    smith.pre=tom;
    xiaosan.pre = smith;
    tom.next = smith;
}

8.ArrayList和LinkedList比较

底层结构 增删效率 改查效率 线程安全
ArrayList 可变数组 较低,数组扩容 较高 线程不安全
LinkedList 双向链表 较高,通过链表追加 较低 线程不安全

如何选择ArrayList和LinkedList:

  1. 如果改查的操作较多,选择ArrayList
  2. 如果增删的操作多,选择LinkedList

9.Set接口和常用方法

9.1Set接口基本介绍

  1. 无序(添加和取出的顺序不一样),没有索引
  2. 不允许重复元素,最多包含一个null
  3. 注意:取出的顺序虽然不是添加的顺序,但是取出的顺序是固定的

9.2Set接口的常用方法

  1. 和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样

9.3Set接口的遍历方式

  1. 可以使用迭代器
  2. 增强for循环
  3. 不能使用索引的方式来获取【不能使用普通for循环】

10.Set接口实现类-HashSet

10.1HashSet全面说明

  1. HashSet实现了Set接口

  2. HahSet实际上是HashMap

    public HashSet(){
        map = new HashMap<>();
    }
    
  3. 可以存放null值,但是只能存放一个null

  4. HashSet不能保证元素是有序的,取决于hash后,再确定索引的结果

  5. 不能有重复元素/对象

10.2HashSet常用方法

HashSet set =new HashSet();

//在执行add方法后,会返回一个boolean值;如果添加成功,返回true,否则返回false
boolean res = set.add("rxjh");//true
boolean res1 = set.add("rxjh");//false

set.add(new Dog("tom"));//true
set.add(new Dog("tom"));//true

set.add(new String("jack"));//true
set.add(new String("jack"));//false

10.3模拟数组+链表

//结点,存储数据,可以指向下一个结点,从而形成链表
class Node{
    Object item;//存放数据
    Node next;//指向下一个结点
    public Node(Object item,Node next){
        this.item=item;
        this.next=next;
    }
}

//模拟一个HashMap的底层
public static void main(String[]args){
    //1.创建一个数组,数组的类型是Node[]
    Node [] table = new Node[16];
    
    //2.创建结点
    Node join=new Node("join",null);
    table[2]=join;//将join放在table表的索引为2的位置
    
    Node jack= new Node("jack",null);
    join.next=jack;//将jack结点挂载到join
    
    Node rose= new Node("rose",null);
    jack.next=rose;//将rose结点挂载到jack
    
    Node lucy = new Node("lucy",null);
    table[3]=lucy;//将lucy放到table表的索引为3的位置    
}

10.4HashSet底层机制

  1. HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)【单项链表】
  2. 添加一个元素时,先得到hash值-会转成->索引值
  3. 找到存储数据表table,看这个索引位置是否已经存放的有元素
  4. 如果没有直接添加
  5. 如果有,调用equals比较,如果相同就放弃添加,如果相同则添加到最后
  6. 在java8中,如果一个链表的元素个数到TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
public static void main(String[] args) {
    HashSet hashSet=new HashSet();
    hashSet.add("java");
    hashSet.add("php");
    hashSet.add("java");
}
//===============================源码=============================
//HashSet类中,维护变量信息
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();

//1.执行构造器 new HashSet();
public HashSet() {
    map = new HashMap<>();//底层是HashMap
}
//2.执行add("java");
public boolean add(E e) {  //E为泛型,e就是添加的元素,第一个add时,e就是java字符串
    return map.put(e, PRESENT)==null;
}
//3.以下代码已经进入到了HashMap类里面==============
public V put(K key, V value) {//key就是添加的java字符串;value就是PRESENT对象,起一个占位作用,为了使HashSet使用到HashMap,value放的统一都是object
    return putVal(hash(key), key, value, false, true);
}
//计算hash值,就是table的索引值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//实际添加元素核心代码【重点核心】
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;//定义了辅助变量
    //table就是HashMap的一个数组,类型是Node[]
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;//table表的第一次扩容,n=16
    //(1)根据key计算的hash,去计算key应该存放到table表的哪个索引位置,并把这个位置的对象,赋给p
    //(2)如果p为null,表示还没有存放元素,就创建一个Node放在tabl[i]这个位置
    if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)即索引从0-15
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
        //(1)准备加入的key和p指向的Node结点的key是同一个对象
        //(2)p指向的Node结点的key的equals()和准备加入的key比较后相同
        //如果满足这两个条件中的任意一个,就不加入
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //判断p是不是一个红黑树,如果是,则调用putTreeVal,来进行添加
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            //如果table对应的索引位置,已经是一个链表,就使用for循环比较
            for (int binCount = 0; ; ++binCount) {//这个for循环没有限制条件,其实是个死循环
                //依次和链表中的每一个元素比较后,都不相同,则加入到该链表的最后
                if ((e = p.next) == null) {//如果第一次就满足的话,说明链表中就一个元素
                    p.next = newNode(hash, key, value, null);
                    //注意:把元素添加到链表后,立即判断 该链表是否已经达到8个结点,如果达到就调用treeifyBin()对当前这个链接进行树化,转成红黑树【在转成红黑树时,还进行一个判断,如果table长度小于64不会进行树化,只是进行table数组扩容】
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
              //for中把p.next给了e,相当于e是p指向的下一个结点;此处又把e给了p相当于p向下移动了一个结点
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
        }
        ++modCount;
	    //size 就是没加入一个结点的Node(k,v,h,next) 无论结点是加在table中某个索引的第一个元素,还是某个索引的链表上,只要加入Node元size就会++【只要size到达12就会扩容】
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);//目的是让Hashmap得子类去实现的
        return null;//添加之后,返回null
    }

10.5HashSet扩容机制和转成红黑树机制

  1. HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75=12
  2. 如果table数组使用到了临界值12,就会扩容到16*2=32---->32 * 2=64,新的临界值就是32 * 0.75 =24---->64 * 0.75 =48,依次类推
  3. 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
//如何能for循环将所有的数据添加到同一个链表上 1.保证hash相同 2.key值不同
class A{
    private int n;
    
    public A(int n){
        this.n = n;
    }
    @Override
    public int hashCode(){
        return 100;//A的所有对象,hash都返回100
    }
}   
public static void main(String[] args){
    HashSet hashSet = new HashSet();
    for (int i=1;i<=12;i++){
        hashSet.add(new A(i));//equals()不同
    }
}
//for循环8次,即i=8时,此时所有的元素都在table[4]这个位置形成一个链表,链表的个数为8个
//for再循环一次,即i=9时,链表元素大于8,触发树化操作,但是table的长度没有到64【此时table的长度为32,16->32】,所以只对table的长度进行了扩容,链表没有树化【此时这个链表上的数据为9个,链表在table的索引位置变成了table[8]】
//for再循环一次,即i=10时,链表元素大于8,触发树化操作,但是table的长度没有到64【此时table的长度为64,32->64】,所以只对table的长度进行了扩容,链表没有树化【此时这个链表上的数据为10个,链表在table的索引位置变成了table[32]】
//for再循环一次,即i=11时,链表元素大于8,触发树化操作,同时满足了数组长度不小于64且链表长度大于等于8.则对该链表进行树化

11.Set接口实现类-LinkedHashSet

11.1LinkedHashSet全面说明

  1. LinkedHashSet是HashSet的子类
  2. LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表【LinkedHashMap是HashMap的一个子类】
  3. LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的【插入的顺序与查询的顺序相同】
  4. LinkedHashSet不允许添加重复元素

11.2LinkedHashSet底层机制

img

12.TreeSet

与HashSet最大的区别是,TreeSet可以排序【默认情况下,不做任何处理TreeSet也是无序的】

12.1当使用TreeSet的无参构造器,创建TreeSet时,仍然是无序的

public static void main(String[] args) {
   TreeSet treeSet = new TreeSet();
    treeSet.add("tom");
    treeSet.add("lili");
    treeSet.add("jack");
    treeSet.add("a");
    System.out.println(treeSet);//[a, jack, lili, tom]
}

12.2使用TreeSet的一个构造器可以传入一个比较器(匿名内部类)并指定排序规则

public static void main(String[] args) {
    TreeSet treeSet = new TreeSet(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            //调用String的compareTo方法进行字符串大小的比较
            return ((String)o1).compareTo((String)o2);
        }
    });
    treeSet.add("tom");
    treeSet.add("lili");
    treeSet.add("jack");
    treeSet.add("a");
    System.out.println(treeSet);//[a, jack, lili, tom]
}

12.3使用Treeset添加一个对象

//=====================================发生异常情况========================================================
class  Person{}
public static void main(String[] args) {

    TreeSet treeSet1 = new TreeSet();
    treeSet1.add(new Person());//会抛出异常,原因是Person没有实现Comparator接口
}
//=====================================正常情况========================================================
class  Person implements Comparable{
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}
public static void main(String[] args) {
    TreeSet treeSet1 = new TreeSet();
    treeSet1.add(new Person());//正常添加
}

13.Map接口和常用方法

13.1Map实现类的特点

  1. Map用于保存具有映射关系的数据:key-value
  2. Map中的key和value可以是任何应用类型的数据,会封装到HashMap$Node对象中
  3. Map中key不允许重复【原因和HashSet一样】
  4. Map中的value可以重复
  5. Map的key可以为null,value也可以为null,注意key为null只能有一个,value为null可以有多个
  6. 常用String类作为Map的key
  7. key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
//如果添加相同key的数据,后添加的key的数据会把先添加的key的数据覆盖掉
Map map=new HashMap();
map.put("no1","刀剑笑");
map.put("no2","银蛟龙");
map.put("no1","韦大宝");
//最终结果只有两个元素,即{no2=银蛟龙,no1=韦大宝}

13.2常用方法

public static void main(String[] args){
    //map常用方法
    Map map=new HashMap();
    //put:添加元素
    map.put("no1","韦大宝");
    
    //remove:根据键删除映射关系
    map.remove("no1");
    
    //get:根据键获取值
    Object val=map.get("no1");//韦大宝
    
    //size:获取元素个数
    int size = map.size();
    
    //isEmpty:判断个数是否为0
    boolean bol=map.isEmpty();
    
    //clear:清除k-v
    map.clear();//void类型,无返回值
    
    //containsKey():查找键是否存在
    boolean no11 = map.containsKey("no1");//true

    //containsValue():查找值是否存在
    boolean nxd = map.containsValue("nxd");//true    
}

13.3遍历方式

创建map并输入map数据

Map map=new HashMap();
map.put("吴承恩","西游记");
map.put("罗贯中","三国演义");
map.put("施耐庵","水浒传");
map.put("曹雪芹","红楼梦");

System.out.println(map);//{吴承恩=西游记, 罗贯中=三国演义, 曹雪芹=红楼梦, 施耐庵=水浒传}

获取map所有的key

//获取map所有的key
Set keySet = map.keySet();
System.out.println(keySet);//[吴承恩, 罗贯中, 曹雪芹, 施耐庵]

获取map所有的value

//获取map所有的value
Collection values = map.values();
System.out.println(values);//[西游记, 三国演义, 红楼梦, 水浒传]

通过keySet循环

//①先取出所有的key,通过key取出对应的value
Set keySet=map.keySet();
System.out.println("==================keySet()方式-增强for======================");
//增强for循环【快捷键大写I】
for (Object key :keySet) {
    //吴承恩-西游记
    //罗贯中-三国演义
    //曹雪芹-红楼梦
    //施耐庵-水浒传
    System.out.println(key+"-"+map.get(key));
}
System.out.println("==================keySet()方式-迭代器======================");
//迭代器循环【快捷键itit】
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
    Object key =  iterator.next();
    System.out.println(key+"-"+map.get(key));
}

System.out.println("==================keySet()方式-普通for循环======================");
//普通for循环【不支持】
for (int i = 0; i < keySet.size(); i++) {
    //System.out.println(keySet.get(i));//keySet没有get()方法,不能进行普通for循环
}

通过values循环

//②取出所有的values
Collection values = map.values();
System.out.println("==================values()方式-增强for======================");
for (Object value :values) {
    //西游记
    //三国演义
    //红楼梦
    //水浒传
    System.out.println(value);
}
System.out.println("==================values()方式-迭代器======================");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
    //西游记
    //三国演义
    //红楼梦
    //水浒传
    Object value =  iterator1.next();
    System.out.println(value);
}
System.out.println("==================values()方式-普通for循环======================");
//普通for循环【不支持】
for (int i = 0; i < values.size(); i++) {
    //System.out.println(values.get(i));//values没有get()方法,不能进行普通for循环
}

通过EntrySet循环

//③通过EntrySet来获取k-v
Set entrySet = map.entrySet();
//迭代器循环
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
    Object entry  = iterator2.next();
    //System.out.println(next.getClass());//HashMap$Node没有getKey()、getValue()方法,HashMap$Node 实现->Map.Entry(getKey,getValue)
    //向下转型
    Map.Entry m=(Map.Entry)entry;
    System.out.println(m.getKey()+"-"+m.getValue());
}
//增强for循环
for (Object entry :entrySet) {
    //将entry转成Map.Entry【entry是没有getKey()方法、getValue()方法的】
    Map.Entry m=(Map.Entry)entry;
    System.out.println(m.getKey()+"-"+m.getValue());
}
//普通for循环【不支持】
for (int i = 0; i < entrySet.size(); i++) {
    //Object entry=entrySet.get(i);//entrySet没有get()方法,不能进行普通for循环
}

14.Map接口实现类-HashMap

14.1HashMap小结

  1. HashMap是Map接口使用频率最高的实现类
  2. HashMap是以key-val 键值对方式来存储数据(HashMap$Node类型)
  3. key不能重复,但是值可以重复,允许使用null键和null值;注意key为null只能有一个,value为null可以有多个
  4. 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换)
  5. 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的(jdk7.0数组+链表,jdk8.0的hashMap底层 数组+链表+红黑树)
  6. HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized

14.2HashMap底层机制

image-20221013222754315

14.3HashMap扩容机制

扩容机制和HashSet相同

  1. HashMap底层维护了Node类型的数组table,默认为null

    transient Node<K,V>[] table;
    
  2. 当创建对象时,将加载因子(loadfactor)初始化为0.75

    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    public HashMap() {
    	this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    
  3. 当添加key-val时,通过key的hash值得到在table的索引。然后判断该索引处是否有元素,如果没有元素则直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等则直接替换val;如果不相等,则需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。

  4. 第一次添加元素,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)

  5. 以后再扩容,则需要扩容table容量为原来的2倍(即32),临界值为原来的2倍(即24),以此类推

  6. 在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),该链表就会进行树化(红黑树)【该链表这句需要验证】

15.Map接口实现类-HashTable

15.1基本介绍

  1. 存放的元素是键值对:即K-V
  2. HashTable的键和值都不能为null,否则会抛出NullPointerException
  3. HashTable使用方法基本上和HashMap一样
  4. HashTable是线程安全的(synchronized),HashMap是线程不安全的

15.2基本使用

public static void main(String[] args) {
    Hashtable hashtable = new Hashtable();
    hashtable.put("join",100);//ok
    //hashtable.put(null,100);//NullPointerException   键和值都不能为null
    //hashtable.put("join",null);//NullPointerException		键和值都不能为null
    hashtable.put("lic",100);//ok
    hashtable.put("lic",100);//替换
}

15.3HashTable与HashMap对比

版本 线程安全(同步) 效率 允许null键null值
HashMap 1.2 不安全 可以
HashTable 1.0 安全 较低 不可以

16.Map接口实现类Properties

16.1基本介绍

  1. Properties类继承自HashTable类并且实现了Map接口,也是使用一种键值对的形式来保存数据
  2. 使用特点与HashTable类似
  3. Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改

16.2常用方法

public static void main(String[] args) {
    Properties properties = new Properties();
    properties.put("join", 100);//ok
    //properties.put(null,100);//NullPointerException
    //properties.put("join",null);//NullPointerException

    properties.put("lic", 100);//ok
    properties.put("lic", 88);//替换
    System.out.println(properties);//{lic=88, join=100}
    //删除
    properties.remove("lic");
    System.out.println(properties);//{join=100}

    //查询
    System.out.println(properties.get("join"));//100
    properties.put("lucy", 110);
    System.out.println(properties.getProperty("lucy"));//null【这个返回null,需要注意】
}

17.Map接口实现类-TreeMap

17.1小结

  1. TreeMap是TreeSet的底层实现

  2. TreeMap可以按照key进行排序【默认情况下,不做任何处理的TreeMap也是无序的】

  3. 键不能为null,值可以null且可以有多个

  4. 当使用TreeMap的无参构造器,创建TreeMap时,仍然是无序的

    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap();
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kiss","kkkk");
        treeMap.put("smith","斯密斯");
    
        System.out.println(treeMap);//{jack=杰克, kiss=kkkk, smith=斯密斯, tom=汤姆}
    }
    
  5. 使用TreeMap的一个构造器可以传入一个比较器(匿名内部类)并指定排序规则

    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //按照key由小到大排序
                //return ((String)o1).compareTo((String) o2);//如果返回0的话,value做替换操作
                //按照key的长度大小排序
                return ((String)o1).length()-((String)o2).length();  //如果返回0的话,value做替换操作
            }
        });
        treeMap.put("jack","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("kiss","kkkk");
        treeMap.put("smith","斯密斯");
    
        System.out.println(treeMap);//{tom=汤姆, jack=kkkk, smith=斯密斯}    
        //重点解释:为什么key为kiss的元素值没有添加进来,key为jack的元素值被替换为了key为kiss的值?
        //因为构造器中,指定的匿名内部类,是按照key字符串长度大小进行排序的,如果判断key的字符串长度相等的话,则值做替换操作
    }
    
  6. TreeMap的key必须实现Comparator接口

    //=====================================发生异常情况========================================================
    class  Person{}
    public static void main(String[] args) {
    
        TreeMap treeMap = new TreeMap();
        treeMap.put(new Person(),"213");//使用匿名person对象作为key直接抛出异常,原因是Person没有实现Comparator接口
    }
    //=====================================正常情况========================================================
    class  Person implements Comparable
    {
        @Override
        public int compareTo(Object o) {
            return 0;
        }
    }
    public static void main(String[] args) {
    
        TreeMap treeMap = new TreeMap();
        treeMap.put(new Person(),"213");//正常添加
    }
    
    

18.集合汇总

如何选择集合实现类

  1. 先判断存储的类型(一组对象【单列】或一组键值对【双列】)
  2. 一组对象【单列】:Collection接口
    1. 允许重复:List
      1. 增删多:LinkedList【底层维护了一个双向链表】
      2. 改查多:ArrayList【底层维护了Object类型的可变数组】
    2. 不允许重复Set
      1. 无序:HashSet【底层是HashMap,维护了一个哈希表 即(数组+链表+红黑树)】
      2. 排序:TreeSet
      3. 插入和取出顺序一致:LinkedHashSet【底层是LinkedHashMap的底层是HashMap】,维护数组+双向链表
  3. 一组键值对【双列】:Map
    1. 键无序:HashMap【底层是:哈希表 jdk7:数组+链表 jdk8:数组+链表+红黑树】
    2. 键排序:TreeMap
    3. 键插入和取出顺序一致:LinkedHashMap【底层是:数组+双向链表】
    4. 读取文件 :ProPerties

19.Collections工具类

19.1基本介绍

  1. Collections是一个操作Set、List和Map等集合的工具类
  2. Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

19.2排序操作(均为静态方法)

  1. reverse(List):反转List中元素的顺序
  2. shuffle(List):对List集合元素进行随机排序
  3. sort(List):根据元素的自然顺序对指定List集合元素按升序排序
  4. sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
  5. swap(List,int,int):将指定List集合中的i处元素和j处元素进行交换
public static void main(String[] args) {
    List list = new ArrayList();
    list.add("tom");
    list.add("smith");
    list.add("king");
    list.add("milan");

    System.out.println(list);//[tom, smith, king, milan]

    //reverse(List):反转List中元素的顺序
    Collections.reverse(list);
    System.out.println(list);//[milan, king, smith, tom]

    //shuffle(List):对List集合元素进行随机排序
    Collections.shuffle(list);//[milan, smith, tom, king]  每次顺序都不一样
    System.out.println(list);
    Collections.shuffle(list);//[tom, milan, smith, king]  每次顺序都不一样
    System.out.println(list);

    //sort(List):根据元素的自然顺序对指定List集合元素按升序排序
    Collections.sort(list);
    System.out.println("自然排序:"+list);//自然排序:[king, milan, smith, tom]
    //希望按照字符串的长度大小排序
    Collections.sort(list, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            //if(o1 instanceof  String)  可以加上这样的判断
            return ((String)o1).length()-((String)o2).length();
        }
    });
    System.out.println("按照字符长度大小排序:"+list);//按照字符长度大小排序:[tom, king, milan, smith]

    //swap(List,int,int):将指定List集合中的i处元素和j处元素进行交换
    System.out.println("交换前="+list);//交换前=[tom, king, milan, smith]
    Collections.swap(list,0,1);
    System.out.println("交换后="+list);//交换后=[king, tom, milan, smith]
}

19.2查找、替换

  1. Object max(Collection):根据元素的自然顺序,返回给定集合中最大元素
  2. Object max(Collection Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
  3. Object min(Collection)
  4. Object min(Collection Comparator)
  5. int frequency(Collection,Object):返回指定集合中指定元素的出现次数
  6. void copy(List dest,List src):将src中的内容复制到dest中
  7. boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
 public static void main(String[] args) {
     List list = new ArrayList();
     list.add("tom");
     list.add("smith");
     list.add("king");
     list.add("milan");

     System.out.println(list);//[tom, smith, king, milan]

     //Object max(Collection):根据元素的自然顺序,返回给定集合中最大元素
     System.out.println("自然顺序最大元素=" + Collections.max(list));//自然顺序最大元素=tom

     //Object max(Collection Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
     //比如,我们要返回长度最大的元素
     Object maxObject = Collections.max(list, new Comparator() {
         @Override
         public int compare(Object o1, Object o2) {
             return ((String) o1).length() - ((String) o2).length();
         }
     });
     System.out.println("长度最大的元素=" + maxObject);//长度最大的元素=smith

     //int frequency(Collection,Object):返回指定集合中指定元素的出现次数
     int tom_num = Collections.frequency(list, "tom");
     System.out.println("tom出现的次数=" + tom_num);//tom出现的次数=1

     //void copy(List dest,List src):将src中的内容复制到dest中
     ArrayList dest = new ArrayList();
     //Collections.copy(dest,list);
     //System.out.println("dest="+dest);//异常:Source does not fit in dest
     //为了完成一个完整拷贝,我们需要先给dest赋值,大小和list.size()一样
     for (int i = 0; i < list.size(); i++) {
         dest.add("");
     }
     //拷贝
     Collections.copy(dest,list);
     System.out.println("dest="+dest);//dest=[tom, smith, king, milan]

     //boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
     list.add("tom");
     System.out.println("list替换前:"+list);//list替换前:[tom, smith, king, milan, tom]
     //list中有tom就替换成汤姆
     Collections.replaceAll(list,"tom","汤姆");
     System.out.println("list替换后:"+list);//list替换后:[汤姆, smith, king, milan, 汤姆]
 }
posted on 2022-10-16 22:46  南晓东  阅读(30)  评论(0编辑  收藏  举报