java集合


Java集合

Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:List、Set 和 Queue

List, Set, Queue, Map 四者的区别?

  • List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
  • Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
  • Queue(实现排队功能): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的
  • Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),”x” 代表 key,”y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。

ArrayList在之前已经讲过

vector


vectord底层也是对象数组 protected Object[] elementData;
vector是线程同步的 Vector 类的操作方法带有 synchronized

vector源码分析
创建vector对象 并循环添加

public class Vector_ {
    public static void main(String[] args) {
//        无参构造 创建对象
        Vector vector = new Vector();
        for (int i = 0; i < 10; i++) {
            vector.add(i);
        }
    }
}

首先执行无参构造器

public Vector() {
        this(10);
    }

   public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

所以new Vector()会默认创建容量为10的对象数组

add方法添加时

   public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

首先执行ensureCapacityHelper() 方法判断是否要扩容
如果数组所需的最小容量大于当前数组容量,执行grow方法扩容

private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow()方法源码:

默认扩容两倍大小

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

也可指定扩容大小。在vector带参构造器中指定扩容大小

   public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

LinkedList

LinkedList底层是双向链表所以不需要扩容

LinkedList与ArrayList的区别

  • 线程都是不安全的
  • LinkedList的底层数据结构是双向链表,ArrayList底层数据结构是Object []数组
  • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
  • LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(add(E e)、addFirst(E e)、addLast(E e)、removeFirst() 、 removeLast()),近似 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element),remove(Object o)) 时间复杂度近似为 O(n) ,因为需要先移动到指定位置再插入。
  • LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  • ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)

Set


存放数据无序(但是取出的顺序是固定的),不许重复,所以null最多只有一个,

不能通过索引获取

HashSet

HashSet底层是HashMap

不能有重复的元素或对象的理解

package set;
import java.util.HashSet;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
       Set set = new HashSet();
       set.add("zf");
       set.add("zf");//失败
        set.add(new Dog("哈哈"));//成功
        set.add(new Dog("哈哈"));//成功
        System.out.println(set);

        set.add(new String("test"));//成功
        set.add(new String("test"));//失败
        System.out.println(set);
    }
}

class Dog{
    public String name;
    public Dog(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}

判断是否能够加入数据的依据是根据Hash()和equals进行比较的

HashMap扩容机制

先说结论

源码

//对该代码进行分析
 package set;
import java.util.HashSet;

public class HashSetSourceCode {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("java");
        hashSet.add("c++");
        hashSet.add("java");
        System.out.println(hashSet);
    }
}

首先第一步进入构造方法

public HashSet() {
    map = new HashMap<>();
}

由此证明HashSet底层是HashMap了

添加第一个数据时

public boolean add(E e) {  //e:"java"
    return map.put(e, PRESENT)==null;  PRESENT是一个final类型静态的一个Object对象
}

执行put方法

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

这时进入hash方法

//进入到hash方法
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    //这里的hash方法计算出来的不是hashcode  而是计算出hashcode 与hashcode无符号右移16位  的值进行按位异或
}

真正的putVal方法

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当前table为空 大小为0  第一次库容到16空间
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //根据key得到hash  去计算key应该存放到table表的哪个索引位置  并把这个位置对象赋给p
    //如果p为空  还没放过数据  就创建一个node
    if ((p = tab[i = (n - 1) & hash]) == null)  //n=16  根据(n - 1) & hash确定要放数据的索引  如果当前这里没数据  就newNode()将hash key  value放进去
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                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;
   // 判断现在数据数量是否大于12  
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

如果tab和table值为空 进入到resize方法

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;   //赋容量为16
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;  //计算临界值
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

第二次进入putVal

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)//不会进入这句 因为table中已经有数据
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)  //继续确定要放置的位置
        tab[i] = newNode(hash, key, value, null);  //放入数据完成
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                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;   //再次modCount++
    if (++size > threshold) // 判断是否大于12
        resize();
    afterNodeInsertion(evict);
    return null;
}

第三次put 放入跟第一次相同的数据 情况如何?

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        //进入这段代码
        Node<K,V> e; K k;
        //如果当前索引位置对应的链表第一个元素的hash值跟  要放入放入元素的hash值相同  并且  准备加入的key和p指向node节点的key是同一个对象  或者key不是空  且 要加入的key和指向p的key 满足equals  这里的equals方法是看程序员确定的  看他是否重写了
        //那么就不能添加了
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //再看这个条件  看p是不是红黑树
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      
        else {
            //在链表里面循环比较
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    //如果都不相同
                    //加入到链表的结尾
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD (8- 1) // -1 for 1st                  在把元素添加到链表后立即判断  链表是否有8个节点                      有8个节点 就对该条链表 进行树化
                        treeifyBin(tab, hash);  //在这个方法中 并不一定会直接转换成红黑树  要判断 如果table大小 小于64
                    //if(tab ==null || (n=tab.length) <MIN_TREEIFY_CAPACITY(64) )         resize();
                    //如果成立条件  才扩容  不成立  进行转成红黑树
                   
                
                  
                    break;
                }
                //如果有相同的直接break
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                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;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

树化机制:我们知道相同hashcode且equals不相同的值会放到一条链表里面,当这条链表里面的size大于等于8,且table表的长度大于等于64会进行树化,但是没有达到64时不会树化,但是会触发resize方法。

比如table里面有16个数据,但是一条链表上有7个元素,再添加一个后,table长度会变到32,再添加一个会变到64,然后就树化了

关于扩容因子12

 if (++size > threshold)
        resize();

扩容的代码在这里 这里的size指的是 添加节点的个数 不管是table表上的还是在链表上的 都算在内

1.先获取元素的哈希值

2.对哈希值进行运算,得出一个索引值即为要存放在哈希表中的位置号

3.如果该位置上没有其他元素,则直接存放 如果在位置上已经有其他元素,需要进行equals判断 相等不添加 不相等则添加

小总结

以下内容摘自我的 Java 启蒙书《Head first java》第二版:

当你把对象加入HashSet时,HashSet 会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。

在openjdk8中,HashSet的add()方法只是简单的调用了HashMap的put()方法,并且判断了一下返回值以确保是否有重复元素。直接看一下HashSet中的源码

也就是说,在openjdk8中,实际上无论HashSet中是否已经存在了某元素,HashSet都会直接插入,只是会在add()方法的返回值处告诉我们插入前是否存在相同元素。

hashCode()与 equals() 的相关规定:

如果两个对象相等,则 hashcode 一定也是相同的
两个对象相等,对两个 equals() 方法返回 true
两个对象有相同的 hashcode 值,它们也不一定是相等的
综上,equals() 方法被覆盖过,则 hashCode() 方法也必须被覆盖
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
==与 equals 的区别

对于基本类型来说,== 比较的是值是否相等;

对于引用类型来说,== 比较的是两个引用是否指向同一个对象地址(两者在内存中存放的地址(堆内存地址)是否指向同一个地方);

对于引用类型(包括包装类型)来说,equals 如果没有被重写,对比它们的地址是否相等;如果 equals()方法被重写(例如 String),则比较的是地址里的内容。

LinkedHashSet

继承了hashset实现了set接口

1.LinkedHashSet 取出数据时跟加入数据的顺序一致

2..LinkedHashSet 底层维护的是数组+双向链表,底层维护的是LinkedHashMap(HashMap的子类)

3.LinkedList添加第一个数据将table扩容到16 存放的节点类型是 LinkedHashMap$Entry

4.数组是HashMap$Node[] 存放的元素是LinkedHashMap$Entry类型

//继承关系是在内部类完成的
static class Entry<K,V> extends HashMap.Node<K,V>{  //继承了HashMap.Node   所以他可以存放双向链表的结构
    Entry<K,V> before,after;
    Entry(int hash,K key,V value,Node<K,V> next){
        super(hash,key,value,next);
    } 
}

Map



map中的每一组key value数据是放在一个叫HashMap$Node的数据类型中的 ,但是为了方便我们遍历,于是有了entry和EntrySet 。EntrySet中存放着entry,每个entry的key其实是指向 HashMap$Node的key位置 每个entry的value其实是指向 HashMap$Node的value位置

HashMap的扩容机制和HashSet基本没有差别
这里简单介绍一下

/**
         * 第一步进入构造方法  new HashNap()
         * 初始化  加载因子 loadFactory =0.75
         * 此时有一个 HashMap$Node 类型的数组 但为空
         *
         * 2.执行put
         *  public V put(K key, V value) {// key  value 就是对应的传入值
         *         return putVal(hash(key), key, value, false, true);
         *     }
         *     算hash
         *
         *  3.执行putVal()
         *  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
         *                    boolean evict) {
         *         Node<K,V>[] tab; Node<K,V> p; int n, i;
         *         if ((tab = table) == null || (n = tab.length) == 0)
         *             n = (tab = resize()).length;
         *
         *             //当前元素算出要放入的位置
         *         if ((p = tab[i = (n - 1) & hash]) == null)
         *             tab[i] = newNode(hash, key, value, null);  //为空 无哈希冲突
         *         else {
         *             Node<K,V> e; K k;
         *             //如果哈希值相同    如果key相同 且key不为空 且key equals k
                         如果table的索引位置的key的hash相同和新的key的hash值相同,
                        并满足(table现有的结点的key和准备添加的key是同一个对象 或equals返回真)

         *             if (p.hash == hash &&
         *                 ((k = p.key) == key || (key != null && key.equals(k))))
         *                 e = p;
         *             else if (p instanceof TreeNode)  //当前table的node已经是红黑树
         *                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
         *             else {
         *             如果冲突的位置是一条链表  一直循环比较
         *                 for (int binCount = 0; ; ++binCount) {
         *                     if ((e = p.next) == null) {
         *                         p.next = newNode(hash, key, value, null);
         *                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
         *                             treeifyBin(tab, hash);
         *                         break;
         *                     }
         *
         *                     //在循环比较发现相同 跳出循环  在下方代码替换
         *                     if (e.hash == hash &&
         *                         ((k = e.key) == key || (key != null && key.equals(k))))
         *                         break;
         *                     p = e;
         *                 }
         *             }
         *             if (e != null) { // existing mapping for key
         *                 V oldValue = e.value;
         *                 if (!onlyIfAbsent || oldValue == null)
         *                     e.value = value;  //key 已经存在了    这是替换原来key对应的value 所以执行这句代码
         *                 afterNodeAccess(e);
         *                 return oldValue;
         *             }
         *         }
         *         ++modCount;  //增加一个node size++
         *         if (++size > threshold)
         *             resize();
         *         afterNodeInsertion(evict);
         *         return null;
         *     }
         */

HashTable


扩容机制 rehash()方法

Properties

Hashtable子类

参考springboot配置文件.properties

TreeSet

可排序

public class TreeSetTest {
    public static void main(String[] args) {
        //使用无参构造器创建treeset  依然无序
        //使用有参构造器  匿名内部类实现conparator接口


        //如果实现类comparator接口能否加入  是取决于  比较的规则是否相等
        /**
         * 比如我们按照长度排序   treeSet.add("zs");
         *         treeSet.add("ko");(加不进去ko)只能加进去一个长度为2的  有一个
         */
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o2).charAt(0)-((String) o1).charAt(0);
            }
        });
        treeSet.add("dsak");
        treeSet.add("ab");
        treeSet.add("zs");
        treeSet.add("ko");
        treeSet.add("ki");
        System.out.println(treeSet);

        /**
         *   1,构造器把传入的比较器对象,赋给了TreeSet的底层的 TreeNap的属性this.comparator
         *         public TreeMap(Comparator<? super K> comparator) {
         *         this.comparator = comparator;
         */

        /*
                            2。在调用treeSet.add( "tom"),在底层会执行到
                                if (cpr != null) {//cpr就是我们的匿名内部类(对象)
                                   do {
                                    parent = t;
                             /动态绑定到我们的匿名内部类(对象)comparecmpI= cpr.compare(key,t.key);
                                    if (cmp < 0)
                                    t = t.left;
                                    else  if (cmp >0)
                                    t = t.right;
                                    else   //如果相等不加入
                                    return t.setValue(value);
                                    }while (t != null);
                                   }



         */

Collections

Collections 工具类常用方法:

排序
查找,替换操作
同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合)

排序操作

void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面

查找,替换操作

int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target)
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素
package Collections_;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionTest {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("tom");
        list.add("tom");
        list.add("javc22k");
        list.add("zhefu");
        list.add("sjadka");
        //翻转
        Collections.reverse(list);
        System.out.println(list);
        //随机排序
        Collections.shuffle(list);
        //自然排序
        Collections.sort(list);

        //自定义比较器
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        });
        //执行位置交换
        Collections.swap(list,0,2);
        System.out.println(list);

        //根据元素的自然排序返回给定集合的最大元素
        System.out.println("根据元素的自然排序返回给定集合的最大元素"+Collections.max(list));


        //自定义比较规则返回给定集合的最大元素
        System.out.println("自定义比较规则返回给定集合的最大元素"+Collections.max(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        }));

        //根据元素的自然排序返回给定集合的最小元素  min
        //自定义比较规则返回给定集合的最小元素
        //查看集合的元素出现次数
        System.out.println("查看集合的元素出现次数"+Collections.frequency(list, "tom"));

        ArrayList<Object> objects = new ArrayList<>();
        for (int i = 0; i <list.size() ; i++) {
            objects.add("");
        }
        //要提前确定长度  才能copy  否则报错
        Collections.copy(objects,list);
        System.out.println(objects);

    }
}

文章作者: 蛰伏
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 蛰伏 !
  目录