集合

集合

为什么要有集合?

数组的缺陷:长度指定就无法更改只能存一个类型的数据,增加和删除麻烦(灵活性不够)

所以就有了集合:可以动态保存多个对象,提供了一系列操作对象的API,add,remove,set,get

集合框架体系

1641256929350

单列集合:

两个子接口:list,set

1641257620923

双列集合(键值对)

Map接口

1641258190220

Collection接口

特点:单列集合

迭代器Iterator

实现Collection接口的集合类都能使用迭代器Iterator

1641258926795

原理:会有一个游标使用next()方法向下走,走过一个返回一个数

常用方法:

​ next()返回下一个元素

​ hasnext()判断下一个元素是否为空

注意:

调用next()方法前,必须调用hasnext()判断,不然会抛异常

 public static void main(String[] args) {
        Collection c=new ArrayList();
        c.add(1);
        c.add("你好");
        c.add(true);
        c.add('e');
//        1.拿到迭代器
        Iterator iterator = c.iterator();
//        这里有一个快捷键===>itit(快速生成迭代器循环)
//        快捷键Ctrl+J显示有哪些快捷键
        while (iterator.hasNext()){//2.判断是否还有数据
            Object next = iterator.next();//3.返回下一个数据,类型为Object
            System.out.println(next);
        }
//        当循环完,这里在获取迭代器的值会报错
//        要想,继续遍历,就得重置
        iterator=c.iterator();
        iterator.next();
    }

增强for循环:

1.增强for循环,底层也是用的迭代器

2.增强for循环可以说简化版本的迭代器

        Collection c=new ArrayList();
        c.add(1);
        c.add("你好");
        c.add(true);
        c.add('e');

//        1.增强for循环,底层也是用的迭代器
//        public Iterator<E> iterator() {
//        return new Itr();
//    }
//        2.增强for循环可以说简化版本的迭代器
//        3.快捷键:  I
        for (Object o:c){
            System.out.println(o);
        }

List接口:

特点(重复,有序,有索引):

1.List集合类中的元素有序(添加和取出顺序一致),且元素可重复

2.每个元素都有其顺序的索引

常用方法:

  1. add(int index,Object ele):插入到指定位置
  2. addall(int index,Collection ele),讲ele中的所有元素插入进来
  3. get(int index)获取指定位置元素
  4. int indexof(Object):返回obj在当前集合首次出现的位置
  5. int lastIndexOf(Object):返回obj在当前集合尾次出现的位置
  6. Objcet remove(int index):移除指定位置元素
  7. object set(int index,Object obj):设置指定位置的元素为obj,相当于替换
  8. List subList(int fromindex,int toIndex):这里的范围是左闭右开
//        常用方法:
//        1.add(int index,Object ele):插入到指定位置
        l.add(1,"插到1位置");
        System.out.println(l);
//        2.addall(int index,Collection ele),讲ele中的所有元素插入进来
        Collection c = new ArrayList();
        c.add(1);
        c.add("插");
        l.add(1,c);
        System.out.println(l);
//        3.get(int index)获取指定位置元素
//        4.int indexof(Object):返回obj在当前集合首次出现的位置
        System.out.println(l.indexOf("nihao"));
//        5.int lastIndexOf(Object):返回obj在当前集合尾次出现的位置
        System.out.println(l.lastIndexOf("nihao"));
//        6.Objcet remove(int index):移除指定位置元素
        l.remove(4);
        System.out.println(l);
//        7.object set(int index,Object obj):设置指定位置的元素为obj,相当于替换
        l.set(4,2);
        System.out.println(l);
//        8.List subList(int fromindex,int toIndex):这里的范围是左闭右开
        List list = l.subList(1, 2);
        System.out.println(list);
    }

ArrayList

特点(有序,高效,重复,但是插入删除等效率低):

​ 1、可以存放多个null值

​ 2、底层是由数组实现的

​ 3、ArrayList基本等同于Vactor,除了线程不安全执行效率高),在多线程的情况下不要用ArrayList

线程为什么不安全?

        ArrayList list = new ArrayList();
//      线程是不安全的,没有synchronized
//      public boolean add(E e) {
//        ensureCapacityInternal(size + 1);  // Increments modCount!!
//        elementData[size++] = e;
//        return true;
//    }

底层操作源码分析:

  1. ArrayList底层维护了一个Object类型(意味着什么都能往里面丢)的数组elementDate

    transient Object [] elementDate //transient短暂的,表示该属性不会被序列化

  2. 当创建ArrayList对象时,使用的是无参构造函器,则elementDate容量为0,第一次添加,则扩容为10,如继续扩容,则会10+位运算>>1,也就是扩容1.5倍

  3. 如果使用的是指定大小的构造器创建对象,则初始elementDate容量就为你指定的大小,如需要继续扩容,则直接扩容为你指定大小的1.5倍

创建无参构造器:

1.其实就是创建了一个空数组

1641265870031

2.这个时候add()会先确定是否扩容,然后执行赋值添加

1641266576611

2.1 确定是否扩容

1641267699411

2.2 扩容的长度

1641269567440

2.3 实现扩容

1641269671838

2.4 实现扩容的真正操作

1641269059523

创建有参构造器:

跟无参大体相同

1641270696611

1641270777670

1641271647971

总结:

1.首先进入创建ArrayList对象的构造器,会判断你是否有参

2.无参构造,调用add()方法的时候,ensureCapacityInternal()方法会先判断是否需要扩容,进入这个判断方法,会有一个方法calculateCapacity()判断你是不是空数组(也就是是不是无参构造器),如果是无参会给你容量minCapacity赋值为10,在进入下一个方法ensureExplicitCapacity()里有一个判断,会判断当前容量(你值的个数)是否大于数组长度,如果大于进入真正扩容的方法grow(),然后这个方法里如果你是空参就会把为10的容量minCapacity扩容数组

3.有参:跟无参一样,不过就是进入创建构造器时,有参会给你直接赋值你参数大小的长度的数组

Vector

特点(安全):

  1. 底层也是Object elementDate[]对象数组
  2. 线程是同步的,安全的,效率低

方法都是带锁的

1641273087397

源码分析:

1641275060556

无参构造器会直接跳到有参构造器,并且扩容为10

1641275092259

然后添加数据add(),进入下面这个方法

1641275268443

判断容量和数组大小

1641275303768

然后扩容

1641275324759

它这里是直接扩容两倍

1641275405770

LinkedList

特点(有序,重复且高效,无索引查找不便)

  1. 底层实现双向链表双端队列的特点
  2. 两个属性frist和last,首尾节点
  3. 添加和删除不是用数组实现的,效率高,数组插入和删除,要整个移动,而链表只要改变指向

源码解析:

  1. 无参LinkedList构造器,此时first和last都为null

  2. 执行add()添加,进入Linklast()尾插

    1641278641492

​ 3.尾插创建新结点

1641279165886

4.删除源码:

进入remove(),真正执行删除的是unlik()

1641280295867

在看看unlik()的源码:

1641280201276

图解一波:

1641280190370

ArrayList和linkedList的对比

1641280845957

增删用LinkedList,查询用ArrayList

Set接口

底层都是用的Map接口的不过[key,value]:value一直是一个定值PRESENT,没有用它,存的都是key

特点(无序不重复):

  1. 无序(添加和取出的数据不一致),没有索引

  2. 不允许重复,最多包含一个null

跟List接口一样都实现了Collection接口,所以基础方法相同,也可以迭代器和增强for遍历,但是不能通过索引获取了

常用方法:

    public static void main(String[] args) {
        Set set = new HashSet();
//       特点:
//        1.不能存重复元素,不能存多个null
//        2.存放和取出顺序不一致
//        3.但是它取出的顺序是固定的
        set.add(1);
        set.add(1);
        set.add(null);
        set.add(null);
        set.add("n");
        System.out.println(set);
        System.out.println(set);

//        遍历:
//        1.迭代器遍历
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }

//        2.增强for循环
        for (Object o:set) {
            System.out.println(o);
        }
        
//        删除
        set.remove(1);
        System.out.println(set);
    }

源码:

底层是HashMap

1641282041508

HashSet:

1641347030404

特点(不重复且高效插入):

  1. 实现了Set接口所以,同样不能有重复元素,只能存一个null
  2. 不保证元素的存放和取出顺序

疑问?

    public static void main(String[] args) {
//        这里是不同对象,所以也满足不重复
        HashSet hashSet = new HashSet();
        hashSet.add(new Dog(10));
        hashSet.add(new Dog(10));
        System.out.println(hashSet);
//        这里?为什么只存进一个
        hashSet.add(new String("1"));
        hashSet.add(new String("1"));
        System.out.println(hashSet);
    }
}

底层机制说明(数组+链表+红黑树):

  1. Hashset的底层是HashMap
  2. 添加一个元素时,会先得到一个hash值,再转成索引
  3. 找到存储的数据表table,看这个索引位置是否已经存放了元素
  4. 如果没有则直接添加
  5. 如果有,则调用equals判断内容是否相等,如果相同放弃添加,如果不同添加到最后
  6. 在java8中,如果链表的元素个数达到了****(默认值为8),并且table的大小>=64就会转换成红黑树

源码:

添加时进入add(),发现真正用到的方法是HashMap的put():

PRESEENT是一个固定对象,为了符合Map键值对的设计

1641287444606

进入Put():

1641287547485

进入hash(key):会有一个算法帮你求出hash值(不是单纯的hashcode)

右移16位降低冲突

可以看到当key=null,hash为0,也解释了为什么null添加时会在第一个

1641287575358

进入putval():

第一个add()添加

  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
      //定义一个Node数组    和辅助变量
        Node<K,V>[] tab; Node<K,V> p; int n, i;
      //第一次进入table肯定为null进入判断
        if ((tab = table) == null || (n = tab.length) == 0)
            //进入这个resize()方法,会把table长度扩容为16个空间
            n = (tab = resize()).length;
      //(1)根据key,得到的hash值,去计算该key应该存放在table表的那个索引位置
      //并且把这个位置的对象,给P
      //(2)如果p为空,表示没有存放这个元素,就创建一个Node
        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;
      //如果此时的数组长度不够
      //threshold临界长度在扩容时设置成(LoadFactor加载因子)0.75*扩容长度(提前扩容)
        if (++size > threshold)
            //扩容
            resize();
        afterNodeInsertion(evict);
        return null;
    }

第二个add()添加:跟第一次,差不多不过此时table不为null,不用初始赋空间了

第三个add()我们添加一个相同的元素

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
      //定义一个Node数组    和辅助变量
        Node<K,V>[] tab; Node<K,V> p; int n, i;
      //第一次进入table肯定为null进入判断
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
      //(1)根据key,得到的hash值,去计算该key应该存放在table表的那个索引位置
      //并且把这个位置的对象,给P
      //(2)如果p为空,表示没有存放这个元素,就创建一个Node
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            第三次肯定会进入这里
            Node<K,V> e; K k;
             //(3)如果当前table位置的链表的头元素p的hash与新元素的hash相等
             //	并且满足下面两个条件之一
             //准备加入的key和p指向的node结点的key相同
             //key值不为空,并且加入的key值内容等于p指向的node结点的k值内容
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //(4)再判断是不是红黑树,如果是就调用putTreeVal()添加
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //(5) 如果也不是红黑树
            else {
                //遍历当前元素位置的链表
                for (int binCount = 0; ; ++binCount) {
                    //如果遍历到最后为null,就退出循环,把新元素尾插进去
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //把新元素添加到链表后,立即判断链表长度是否有8个
                        if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                            //转成红黑树,这个方法treeifyBin里判断,table表的大小<64,只扩容
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果hash值或者对象地址或者值内容相同退出循环
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //p指向e(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;
      //如果此时的数组长度不够
        if (++size > threshold)
            //扩容
            resize();
        afterNodeInsertion(evict);
        return null;
    }

总结:

首先,HashSet底层用的是HashMap(数组+链表+红黑树)

首次进入添加时:,首先会判断你是不是空表,如果表为空,会进入resize()进行扩容,扩容长度为16,此后每次扩容*2,但是还会根据(LoadFactor加载因子)0.75*16得到一个临界长度threshold,超过临界值就会扩容(提前扩容),然会根据hash值求出你在table表的索引位置的元素P,如果此时这个元素p为空,就添加元素

后面添加重复元素时:会进入p元素不为空的判断,此时会判断p元素的hash与新元素的hash是否相等,相等继续后面判断,(p元素的node结点的key的地址是否与新元素相等 || key值不为空,key的内容相等)满足其一就可进入判断,判断内容为e=p(e指向p),此时不能添加元素

如果hash相等,后面判断不成立,继续else if 判断是不是红黑树,如果是调用putTreeVal()添加

如果上面还不成立,继续else 遍历p的当前链表遍历时判断hash,对象地址,对象值是否相等(如果相等就退出循环),不等就把p=e(e为p的下一个结点)然后继续循环,循环到最后把新结点元素添加到尾部,添加后,看当前链表长度是不是>8,如果成立进入treeifyBin()红黑树方法,并不是直接转换成红黑树,它里面会看你表的个数是否达到64,如果达到64才会变成红黑树)此时退出循环链表

注意:添加时不是要把表的每个索引到占着到12才扩容而是只要对象达到12就会扩容

LinkHashSet:

特点:(不重复,且有序可以查询)

  1. 是HashSet的子类
  2. 底层是一个LinkHashMap,底层维护了一个hash表+双向链表(head,tail,before,after),next维护数组中一行的单链表,before和after维护一个双链表保证有序
  3. 同HashSet一样也是通过hashcode确认在数组中的位置,同时使用了链表维护了插入次序(插入有序和取出顺序一致)

跟HashSet一样,添加元素时都是先求hash值,再求索引,确定在table表的位置,再把元素添加到双向链表,如果存在就不添加(跟HashSet一样)

分析:

  1. 第一次add()添加时,直接扩容为16(同HashSet),存放的类型是 LinkedHashMap$Entry
  2. 数组的类型是HashMapLinked$Node[]存放的类型却是LinkedHashMap$Entry,里面内部类继承

总结:

大体跟HashSet一样,效率不如HashSet,但是加了双向链表保证了有序

HashSet和LikHashSet都可以通过hashcode和equals来操作他们

Map:

特点:

  1. 与Collection并列存在,用于保存具有映射关系的数据:key-value
  2. Map中的kep和value可以是任何类型的数据,会封装到HashMap$Node对象中
  3. Map中的key不能重复当key重复时,value会替换),原因跟HashSet一样,但是value可重复
  4. Map中key跟Hashset时一样可以为null,但是只能有一个nullnull哈希值固定的,只能添加一次),但是value可以为很多null(value只是内容)
  5. 通过key能一对一找到value
  6. 为了方便遍历会有一个集合EntrySet存放我们的Node结点的引用,存储的类型是Entry(Node实现了Entry)通过,为什么要是Entry类型,因为接口Entry中有getKey()和getValue()

1641364853281

实现:

1641360798886

 public static void main(String[] args) {
//      1.为了遍历的方便,创建了一个EntrySet集合,存放的元素为Entry,但是实际上存放的是HashMap$Node ,因为Node实现了Entry这个接口
//                 Entryset集合里存放的是HashMap$Node的地址
//      2.当把HashMap$Node放到Entryset中就会方便我们的遍历,因为里面有getKey()和getValue()
        Map map = new HashMap();
        map.put("1",'a');
        map.put("3",'a');
        map.put("2",'a');
     实现接口entrySet()
        Set set = map.entrySet();
        for (Object o: set){
            转换Entry接口类型调用方法
            Map.Entry e=(Map.Entry)o;
            System.out.println(e.getKey()+" "+e.getValue());
        }
    }

常用方法:

  1. put:添加
  2. remove:删除
  3. get:获取值
  4. size:大小
  5. isEmpty:个数是否为0
  6. clear:清除所有
  7. containsKey:查找键是否存在
   public static void main(String[] args) {
//1. put
        Map map = new HashMap();
        map.put("1","A");
        map.put("1","B");
        map.put("2","A");
        map.put(null,"A");
        map.put(null,null);
        map.put("3",null);
        System.out.println(map);
//2. remove
        map.remove(null);
        System.out.println(map);
        System.out.println(map.remove("3", null));
        System.out.println(map);
//3. get:获取值
        System.out.println(map.get("1"));
//4. size
        System.out.println(map.size());
//5. isEmpty:判断个数是否为0
        System.out.println(map.isEmpty());
//6. clear:清除
        map.clear();
//7. containsKey:查找键是否存在
        System.out.println(map.containsKey("2"));
    }

Map的三种遍历方式:

  • 使用keyset(),取所有键
  • 使用values(),取所有值
  • 使用集合Entryset,使用getkey()和getvalues()分别取键值
 public static void main(String[] args) {
        Map map = new HashMap();
        map.put(1,"a");
        map.put(2,"b");
        map.put(4,"d");
        map.put(5,"e");
        map.put(3,"c");
//        第一组通过keyset()拿到key
//        用增强for循环
        Set set = map.keySet();
        for (Object key:set){
            System.out.println("第一种"+map.get(key));
        }
//        迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.println("第二种"+map.get(key));
        }
//      第二组:用values()取出所有值
//        for
        Collection values = map.values();
        for (Object v:values){
            System.out.println(v);
        }
//        迭代器
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object v =  iterator1.next();
            System.out.println(v);
        }

//        第三组:通过Entryset集合
//        for
        Set set1 = map.entrySet();
        for (Object key:set1){
            Map.Entry  K=(Map.Entry)key;
//            System.out.println(K);//用等号连接的键值对
            System.out.println(K.getKey()+""+K.getValue());//分开取
        }
//      迭代器
        Iterator iterator2 = set1.iterator();
        while (iterator2.hasNext()) {
            Map.Entry k = (Map.Entry) iterator2.next();
            System.out.println(k.getKey()+""+k.getValue());
        }
    }
 

HashMap:

特点(键值对,key不能重复,无序):

跟Map里的一样

  1. 是Map最常用的类
  2. key不能重复,value可以重复,key和value都能为null,但key只能有一个null
  3. 添加相同的key值,会替换原来key的value
  4. 与HashSet一样存入和取出顺序是乱的,因为底层是由hash表的方式存在的
  5. 没有实现同步,线程不安全

替换的源码:

1641368804518

机制分析:

1641369194282

(k,v)是一个Node对象实现了Entry接口,引用放在Entryset集合种

  1. HashMap底层维护的是一个Node类型数组table,初始为null
  2. 当添加key-value时,扩容机制看HashSet总结
  3. 扩容大于临界属性,就扩容
  4. 在树化方法里,进入树化这个方法看Node结点size是否>8由于是只有找到相同结点才去链表一个一个判断,所以0-7有8个要加上已经有了的那个结点就是9个),进去后数组length>64要扩容

源码:

看HashSet

总结:

HashMap与HashSet的区别

HashSet实现了set和Collction接口,所以有上面接口的方法和迭代器,但是根本上是一个HashMap,它的创建添加,跟HashMap一致,正如我们这么想,HashMap是一个双列键值对集合,键对值一一要对应,键不可重复很正常,而HashSet一个单列集合却也不能重复,原因就是因为也是由hash值确定table表索引,其实HashSet,也是一个键值对但是HashSet的value值是一个已经固定的值

另外HashSet有一个子类LinkedHashSet,就是在HashSet的基础上底层有了个双链表,所以有序,HashMap也有个子类叫LinkedHashMap,同样有序

Hashtable:

特点(键值对,线程安全,键值都不能为null):

  1. 键值对双列集合
  2. 键值都不能为空
  3. 用法跟HashMap一致
  4. 线程是安全的,方法都带synchronized

源码上看:确实不能放null

1641374376192

机制分析:

  • 底层数组为Hashtable$Entry[],初始为11

  • 临界值为11*0.75=8

  • 扩容(第二次):11*2+1=23( 源码部分:(oldCapacity << 1) + 1

Properties类:

  1. 实现了Map接口所以也是键值对
  2. 继承于Hashtable
  3. 常用于配置文件

TreeSet:

底层就是TreeMap

特点:

  1. 当我们使用无参构造器的时候就会给我们传入的数据排序,但只能升序排序
  2. 自定义规则排序,使用TreeSet提供的构造器,可以传入一个比较器(匿名内部类),并制定排序规则
    public static void main(String[] args) {
//        当我们使用无参构造器的时候就会给我们传入的数据排序,但只能升序排序
//        自定义规则排序,使用TreeSet提供的构造器,可以传入一个比较器(匿名内部类),并制定排序规则
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
//                调用字符串的compareto方法进行字符串大小比较
                return ((String)o1).compareTo((String)o2);
            }
        });
        treeSet.add("1");
        treeSet.add("7");//当传入两个相同的元素时(当规则比较的是长度时,那么传入长度相等的数也传不进去),添加不进去
        treeSet.add("0");
        treeSet.add("6");
        treeSet.add("1");
        System.out.println(treeSet);
    }

构造器底层:会把comparator给TreeMap

源码:
public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

添加在调用add时,在底层会执行到TreeMap的put()方法:

源码:
Comparator<? super K> cpr = comparator;//这个就是我们传入的comparator
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);//如果相等,返回0,添加不进去
            } while (t != null);

TreeMap:

特点:

跟TreeSet一致,就是当传入的key相同时,value会被覆盖

总结:

确认用什么?

先确认是用一个对象(单列集合)还是键值对(双列集合)

单列集合:实现Collecion接口

允许重复:List

增删多LinkedList(底层双向链表)

改查多AarryList(底层是一个Object类型数组)

不允许重复:Set

无序:HashSet(底层是HashMap==》数组+链表+红黑树)

排序TreeSet

插入和取出顺序一致LinkedHashSet(底层是HashMap==》在此基础+双向链表)

双列集合(键值对):实现Map接口

无序HashMap(底层哈希表==》数组+链表+红黑树)

排序TreeMap

插入和取出一致LinkedHashMap

读取文件Properties

Collections类:

  1. 是一个操作List,Set,Map等集合的工具类

    常用方法:

    1. 反转reverse
    2. 随机排序shuffle
    3. sort自然排序,也可自定义
    4. max最大值,也可以用Comparator自定义
    5. min,也可以用Comparator自定义
    6. frequency(),出现次数
    7. replaceAll,替换
     public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add(1);
            list.add(3);
            list.add(2);
            list.add(4);
            list.add(4);
    //        反转reverse
            Collections.reverse(list);
            System.out.println(list);
    //      随机排序
            Collections.shuffle(list);
            System.out.println(list);
    //        sort自然排序
            Collections.sort(list);
            System.out.println(list);
    //       sort自定义
    //        Collections.sort(list, new Comparator() {
    //            @Override
    //            public int compare(Object o1, Object o2) {
    //                return ((String)o1).length()-((String)o1).length();
    //            }
    //        });
    //          mxa最大值,也可以用Comparator自定义
            System.out.println(Collections.max(list));
    //        min,也可以用Comparator自定义
            System.out.println(Collections.min(list));
    //        frequency(),出现次数
            System.out.println(Collections.frequency(list, 4));
    //        replaceAll,替换
            Collections.replaceAll(list,4,1);
            System.out.println(list);
        }
    
posted @ 2022-03-14 18:06  又菜又ai  阅读(89)  评论(0)    收藏  举报