美文网首页
十、集合

十、集合

作者: blank_white | 来源:发表于2020-07-19 20:58 被阅读0次

    十、集合

    ArrayList 一种可以动态增长和缩减的索引序列
    LinkedList 一种可以在任何位置进行高效地插人和删除操作的有序序列
    ArrayDeque 一种用循环数组实现的双端队列
    HashSet 一种没有重复元素的无序集合
    TreeSet —种有序集
    EnumSet 一种包含枚举类型值的集
    LinkedHashSet 一种可以记住元素插人次序的集
    PriorityQueue 一种允许高效删除最小元素的集合
    HashMap 一种存储键 / 值关联的数据结构
    TreeMap —种键值有序排列的映射表
    EnumMap 一种键值属于枚举类型的映射表
    LinkedHashMap 一种可以记住键 / 值项添加次序的映射表
    WeakHashMap 一种其值无用武之地后可以被垃圾回收器回收的映射表
    IdentityHashMap 一种用 = 而不是用 equals 比较键值的映射表
    Collection
    import org.example.Size;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.util.*;
    
    public class CollectionTest {
    
    
        Collection collection;
        Collection A;
        Collection B;
        @Before
        public void b(){
            collection=new ArrayList();
            for (int i = 0; i < 5; i++) {
                collection.add(i);
            }
            A=new ArrayList();
            for (int i = 0; i < 5; i++) {
                A.add(i);
            }
            B=new ArrayList();
            for (int i = 4; i < 7; i++) {
                B.add(i);
            }
        }
    
        /**
         *  collection 的基本遍历
         */
        @Test
        public void t1(){
    
    
            collection.iterator().forEachRemaining( o -> System.out.print(o));
            System.out.println();
            collection.iterator().forEachRemaining( System.out::print);
            System.out.println();
    
            Iterator<Integer> iterator=collection.iterator();
            while (iterator.hasNext()){
                Integer i=iterator.next();
                if (i==2)iterator.remove();
                System.out.print(i);
            }
            System.out.println();
            for (Object o :collection){
                System.out.print(o);
            }
            
            System.out.println();
            collection.forEach(o -> {
                System.out.print(o);
            });
        }
    
        /**
         * collection 常用方法
         */
        @Test
        public void t2(){
            // 集合大小
            System.out.println(collection.size());
            // 集合是否为空
            System.out.println(collection.isEmpty());
            // 集合是否包含对象
            System.out.println(collection.contains(3));
            // 集合是否包含集合
            System.out.println(collection.containsAll(new ArrayList<>()));
    
    
            //可以看到这个方法改变了集合A中的元素,将存在于集合A中但不存在于集合B中的元素移除。
            //如果集合A的大小发生了改变,返回true,即使两个集合完全没有交集,也会返回true。
            //如果集合A的大小没有发生改变,返回false,即使两个集合完全相同,也会返回false。
            A.retainAll(B);
    
            // 注意这里是 Collection collection,A,B  名字没起好
            collection.remove(1);
            collection.removeAll(A);
            collection.addAll(A);
            
            
            
            System.out.println(collection);  // [0, 2, 3, 4]
            Integer[] integers=new Integer[6];
            // 集合转数组
            Object[] integers2=collection.toArray(integers);
            // Arrays.toString ,按一定格式将数组转化成字符串的
            System.out.println(Arrays.toString(integers)); // [0, 2, 3, 4, null, null]
            System.out.println(Arrays.toString(integers2));// [0, 2, 3, 4, null, null]
    
            Object[] integers3=  collection.toArray();
            System.out.println(Arrays.toString(integers3));// [0, 2, 3, 4]
    
            
            
            // 清空 
            collection.clear();
        }
        
    }
    
    

    集合转数组部分订正

            
            // 列表转换数组,
            // 数组类型不能强制转换,Object数组 类 和 Integer数组 类 是无法互相转换的
            // 这样用的时候不方便操作,   (Integer)obejects[0]
            Object[] objects=list.toArray();
    
           
            Integer[] nums2;
            // 可以传入一个指定类型的数组 将返回同类型数组
            // 如果传入数组长度不够,会新建一个同类型数组并返回
            nums2=list.toArray(new Integer[0]);
            // 如果传入数组长度够用,则直接填入到这个数组中并返回
            nums2=list.toArray(new Integer[10]);
    
    LinkList
    import org.junit.Test;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.ListIterator;
    
    public class ListTest {
    
        @Test
        public void t1(){
            List<Integer> list=new LinkedList<>();
            for (int i = 0; i < 5; i++) {
                list.add(i);
            }
    
            ListIterator<Integer> iterator=list.listIterator();
    
            ///  以下都是 函数生效前 游标位置
            // next     返回游标 后面的元素,同时游标右移  之后如果使用 remove/set 操作的是 游标左侧元素
            // previous 返回游标 前面的元素,同时游标左移  之后如果使用 remove/set 操作的是 游标右侧元素
            // nextIndex返回游标 后面的元素位置
            // public int previousIndex() { return nextIndex - 1;}
            // previousIndex返回游标 前面的元素位置 = 后面的元素位置-1
            System.out.println(iterator.previousIndex());// -1
            System.out.println(iterator.nextIndex()); //0
            System.out.println(iterator.next()); // 0
            iterator.add(7);                     //
            System.out.println(iterator.next());// 1  在添加元素后 iterator previous() 是刚添加的元素,next() 是 刚添加的元素后面的元素
            System.out.println(iterator.previous());// 1
            iterator.remove();
            System.out.println(iterator.next()); // 2
            System.out.println(iterator.nextIndex());// 3       0 7 2 | 3  游标在这个位置
    
    
            iterator.set(6);
    
            iterator.hasNext();
            iterator.hasPrevious();
    
            System.out.println("*************");
            iterator=list.listIterator();
            for (int i = 0; i < 20; i++) {
                if (iterator.hasNext()){
                    System.out.println(iterator.next());
                }
            }
    
            System.out.println(list.get(1));
    
            // 元素第一次出现的位置 没有找到返回 -1
            System.out.println(list.indexOf(4));
            // 元素最后一次出现的位置 没有找到返回 -1
            System.out.println(list.lastIndexOf(4));
            
            LinkedList<Integer> list2=new LinkedList<>();
            list2.addFirst(1);
            list2.addLast(3);
            list2.getFirst();
            list2.getLast();
            list2.removeFirst();
            list2.removeLast();
            
        }
    }
    
    
    ArrayList

    Vector 也可以实现动态数组

    Vector 所有方法都是同步的,耗时

    ArrayList 所有方法都是不同步的

    HashSet
    • 桶:收集具有相同散列值的数据结构,具有相同散列值得放在一个桶里

    • 桶数:桶的数目,通常将桶的数目设置为预计元素个数的 75%~150%

    • 填装因子:(默认为 0.75) 当表中超过 75% 的位置已经被填充,就会用双倍的桶数进行再散列

    • 再散列:重新创建一个桶数更多的表,将原表元素插入新表,并舍弃旧的表

    Map 的 key 是唯一的,从 HashSet 的源码中可以看到,HashSet 实际上是使用了 HashMap 的 key 作为元素

    注释中说明了默认的 桶数为 16 ,填装因子为 0.75

    
        /**
         * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
         * default initial capacity (16) and load factor (0.75).
         */
        public HashSet() {
            map = new HashMap<>();
        }
    
    

    常用方法

    
            HashSet<String> hashSet=new HashSet<>();
    
            //  public HashSet(Collection<? extends E> c) {
            //  将集合全部元素添加到散列集
            new HashSet<String>(new ArrayList<String>());
    
            // 设置桶数
            new HashSet<String>(16);
            // 设置桶数和填装因子
            new HashSet<String>(16,0.75f);
    
    
            // 添加元素
            hashSet.add("zs");
            hashSet.add("ls");
            hashSet.add("zs");
            // 是否包含元素
            hashSet.contains("zs");
            hashSet.remove("zs");
    
            //
            // 对于要存于 散列集的对象要重写 hashCode() 方法 和 equals() 方法,
            // 并且要保证 若果 x.equals(y) 为 true 那么 x.hashCode()==y.hashCode 为true
            //
            "zs".hashCode();
    
            hashSet.iterator().forEachRemaining(System.out::println);
    

    特点:

    • 集合元素可以是null,但只能放入一个null
    • 不是同步的
    TreeSet
            // TreeSet 中的元素 在遍历时,是按顺序排列的,当前是使用 红黑树 实现的
            // TreeSet 实现了 SortedSet 接口
            // 使用树集  元素必须实现 Comparable 接口,(元素不能比较的话,没办法排序的),或者 new 的时候传入比较器
            TreeSet<String> set=new TreeSet<>();
    
            set.add("john");
            set.add("bob");
            set.add("carlos");
            set.add("bob");
            set.add("a");
            set.add("z");
    
            // 遍历
            for (String s : set) {
                System.out.println(s);
            }
    
            set.iterator().forEachRemaining(System.out::println);
    
    
            // 常用方法
            set.first();
            set.last();
    
            // NavigableSet 接口的方法
            // 返回 比  bz 大的元素中 最小的
            System.out.println(set.higher("bz"));  // carlos
            // 返回 比  bz 小的元素中 最大的
            System.out.println(set.lower("bz"));   // bob
    
            // 返回 比  bob 大或等于的元素中 最小的
            System.out.println(set.ceiling("bob"));
            // 返回 比  bob 小或等于的元素中 最大的
            System.out.println(set.floor("bob"));
    
            // 删出最小元素并返回
            set.pollFirst();
            // 删出最大元素并返回
            set.pollLast();
    
            System.out.println("*********");
            //  descendingIterator() 返回一个反向迭代器,用于逆序遍历
            set.descendingIterator().forEachRemaining(System.out::println);
    
    Queue
    • 双端队列 Deque

      左往右的队列操作,右往左也支持

    • 优先级队列 PriorityQueue

      可以以任意顺序插入,但是始终是按顺序检索

      使用数据结构 堆(heap):一个可以自我调整的二叉树

    常用方法

            Queue<Integer> queue=new ArrayDeque<>();
            queue.add(999);
    
    
            // 以下方法,每组 前面的 出错抛出异常,后面的 返回 false 或者 null
    
            // 添加元素 ,队列满了则无法添加
            queue.add(1); //抛出异常
            queue.offer(2); // 返回 false
    
    
            // 移除并返回头部元素
            queue.remove();
            queue.poll();
    
            // 返回头部元素,但不删除
            queue.element();
            queue.peek();
            
            
            // 双端队列
            // public interface Deque<E> extends Queue<E> {
            // ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>
            Deque<Integer> deque=new ArrayDeque<>();
            deque.add(999);
    
            // 头插 和  尾插
            deque.addFirst(1);  // 出错抛出异常
            deque.addLast(2);
            deque.offerFirst(3); // 出错返回 false
            deque.offerLast(4);
    
            // 移除并返回头/尾部元素
            deque.removeFirst();
            deque.removeLast();
            deque.pollFirst();
            deque.pollLast();
    
    
            // 返回头/尾部元素,但不删除
            deque.getFirst();
            deque.getLast();
            deque.peekFirst();
            deque.peekLast();
    
            // 设置初始容量,无参构造器 默认为 16
            // ArrayQueue 在满了的时候能自动扩充容量
            Queue<Integer> queue2=new ArrayDeque(8);
    
    
            // 优先级队列
            // 元素可以按任意顺序插入,但是总是按照排序的顺序进行检索
            // 使用了 数据结构 堆 (heap 一个可以自我调整的二叉树)
            // PriorityQueue<E> extends AbstractQueue<E>
            // AbstractQueue<E> extends AbstractCollection<E> implements Queue<E>
            Queue<Integer> queue3=new PriorityQueue<>();
            queue3.add(9);
            queue3.add(8);
            queue3.add(2);
            queue3.add(7);
            System.out.println(queue3); // [2, 7, 8, 9]
    
    
    HashMap
    Map<Integer,String> map=new HashMap<>();
            map.put(1,"1zs");
            map.put(2,"2ls");
            String name;
            name=map.get(1);
            // 获取失败时,用 无名氏 当作默认值返回
            name=map.getOrDefault(99,"无名氏");
    
            map.put(2,"2ls cover");
    
            //  void putAll(Map<? extends K, ? extends V> m);
            map.putAll(new HashMap<>());
    
            boolean b;
            // 查看 是否包含 键
            b=map.containsKey(1);
            // 查看 是否包含 值
            b=map.containsValue("2ls");
    
            // 对map中每组 键值对 都执行 某操作
            map.forEach((k,v)->{
                System.out.println("key "+ k+" : "+v);
            });
    
            new HashMap<>();
            // 初始化时设置桶数,和 Set 相同
            new HashMap<>(16);
            // 初始化时设置桶数和填装因子
            new HashMap<>(16,0.75f);
    
    TreeMap

    能自动按键排序的 Map

    
            TreeMap<Integer,String> treeMap=new TreeMap<>();
    
            // public TreeMap(Comparator<? super K> comparator) {
            // 注意 是  Comparator  不是  Comparable
            // 是  tor !  不是  able!
            // 可以传入一个比较器,作为 key 的比较方法,可以用 lambda 表达式写法,也可以 匿名类 或者写一个实现接口的类
            new TreeMap<Integer,String>((a,b)->{return 1;});
            
            treeMap.put(3,"3ww");
            treeMap.put(1,"1zs");
            treeMap.put(2,"2ls");
            treeMap.put(2,"2ls cover");
    
    
            // SortedMap 接口的方法
            // 返回最小 key 和 最大 key
            treeMap.firstKey();
            treeMap.lastKey();
    
            // 当原先 key 对应 value 不存在时 才放入新 value
            treeMap.putIfAbsent(1,"1 putIfAbsent");
    
    
            // 参数 1 key
            // 参数 2 新值
            // 参数 3 处理旧值和新值 的过程
            // get(key) 为 null 时,直接将参数 2 作为新value
            // 不为空的时候 用参数 3 处理 原value 和 参数 2 ,结果作为新value
            // 如果参数 3 处理结果为 null 则移除value 即 remove(key)
            // 不为空则  put(key ,newValue)
            // 详情见源码
            treeMap.merge(1,"merger",String::concat);  // 1:1zsmerger
    
            treeMap.merge(4,"--merger",(a,b)->{return a+b+"lambda";});// 4:--merger
    
            // 将 key 和 value 做一定处理 作为新的 value
            // 及时 get(key) 为空也会处理
            // 处理结果为 null 则会移除 remove(key)
            // 不为空则  put(key ,newValue)
            treeMap.compute(3,(k,v)->{return k+v+"lambda";}); // 3:33wwlambda
    
            treeMap.compute(9,(k,v)->{ return k+v+"lambda";});// 9:9nulllambda
    
            // 当 key 对应值 不为 null 时才做处理,否则直接返回
            treeMap.computeIfPresent(10,(k,v)->{ return k+v+"computeIfPresent";});
    
            // 当 key 对应值 为 null 时才做处理
            treeMap.computeIfAbsent(11,(k)->{ return k+"computeIfPresent";});
    
    
            // 将所有映射 都用函数处理
            treeMap.replaceAll((k,v)->{ return v;});
    
            treeMap.forEach((k,v)-> System.out.println(k+":"+v));
    
            System.out.println(null+"-hello"); // 输出 null-hello
    
    Map映射视图
    map.keySet();
    map.values();
    map.entrySet();
    

    对 Map 做映射方便做一些操作,有点像 SQL 的视图

    对三种映射视图通过 iterator 做 remove() 操作,都会同时影响到原 map

    若能修改视图,则也会使 原来的 map 做出相应的修改

    但是都不能使用 add() 方法,会抛异常

            Map<Integer,String> map=new HashMap<>();
            map.put(2,"2ls");
            map.put(3,"3ww");
            map.put(1,"1zs");
            map.put(4,"4zl");
    
            // 返回 键 集
            Set<Integer> integers = map.keySet();
            // 返回 值 的集合
            Collection<String> values = map.values();
            // 返回  键值组合的结构 Entry 的集
            Set<Map.Entry<Integer, String>> entries = map.entrySet();
    
            // 1=1zs
            // 2=2ls
            // ··········
            // 输出结果如上, toString() 方法会将  key 和  value 中间以等号拼接
            // static class Node<K,V> implements Map.Entry<K,V> {
            //       public final String toString() { return key + "=" + value; }
            entries.forEach(System.out::println);
    
            //  在键集视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
            Iterator iterator=integers.iterator();
            while (iterator.hasNext()){
                Integer i= (Integer) iterator.next();
                if (i==2){
                    iterator.remove();
                }
            }
    
    
            //  在值集合视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
            Iterator<String> iterator2 = values.iterator();
            while (iterator2.hasNext()){
                String next = iterator2.next();
                if ("1zs".equals(next)){
                    iterator2.remove();
                }
            }
    
    
            // 在键值集视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
            // 修改键对应的值,会使原来的 map 被相应的修改
            Iterator<Map.Entry<Integer, String>> iterator3 = entries.iterator();
            while (iterator3.hasNext()){
                Map.Entry<Integer, String> next = iterator3.next();
    
                if (3==next.getKey()){
                    next.setValue("通过 Entry 被修改了");
                }
                if (4==next.getKey()){
                    iterator3.remove();
                }
    
    
            }
    
            // map 中的 1 2 4 都被 被删除了
            // 键 3对应的值被修改为 : 3通过 Entry 被修改了
            map.forEach((k,v)-> System.out.println(k+v));
    
            // 三种映射视图都不能使用 add() 方法,会抛异常
            //integers.add(5);
            //values.add("5");
            //entries.add(null);
    
    
    weakHashMap弱散列映射

    能与垃圾回收器协同工作的 Map

    
            // WeakHashMap 使用弱引用保存键
            // 当对键的唯一引用,来自散列条目时,这一数据结构将与垃圾回收器协同工作,一起删除键值对
            WeakHashMap<Integer, String> weakHashMap = new WeakHashMap<>();
    
            // WeakHashMap 不是线程安全的 ,在并发场景下使用,可以使用  Collections.synchronizedMap(weakHashMap);
            // Map<Integer, String> map = Collections.synchronizedMap(weakHashMap);
            weakHashMap.put(1,"1zs");
            weakHashMap.put(2,"2zs");
    
    
    LinkedHashSet LinkedHashMap

    能按顺序(插入顺序或受影响顺序)遍历的 HashSet 和 HashMap

    
            // LinkedHashMap 会记住数据插入的顺序,可以按插入顺序迭代
            LinkedHashMap<Integer,String>  linkedHashMap = new LinkedHashMap<>();
            linkedHashMap.put(1,"1zs");
            linkedHashMap.put(3,"3zs");
            linkedHashMap.put(2,"2zs");
            linkedHashMap.put(4,"4zs");
    
            // 1 3 2 4
            linkedHashMap.forEach((k,v)->{System.out.println(k+" : "+v);});
    
    
    
            // 第三个参数  accessOder 默认为false,若设置为true ,
            // 则每当 调用get 或 put ,会将受影响的条目从当前位置移除,放置到条目链表的尾部
            // 这种特性可以应用于高速缓存的最近最少用原则,最前面的元素说明最近没有访问,有新数据要加入缓存可以移除靠前面的元素
            linkedHashMap = new LinkedHashMap<Integer,String>(16,0.75f,true);
            linkedHashMap.put(1,"1zs");
            linkedHashMap.put(3,"3zs");
            linkedHashMap.put(2,"2zs");
            linkedHashMap.put(4,"4zs");
            linkedHashMap.get(1);
            linkedHashMap.get(2);
            linkedHashMap.get(3);
            linkedHashMap.get(4);
            // 1 2 3 4
            linkedHashMap.forEach((k,v)->{System.out.println(k+" : "+v);});
    
    
    
            // 会记住插入顺序的 集,会以插入顺序进行遍历
            LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
            linkedHashSet.add("1zs");
            linkedHashSet.add("3zs");
            linkedHashSet.add("2zs");
            linkedHashSet.add("4zs");
            linkedHashSet.contains("3zs");
            // 1 3 2 4
            linkedHashSet.forEach(System.out::println);
    
    
    EnumSet EnumMap

    针对枚举类型作为 集的元素 或者 Map 的 key 时

    一种高效的 Set 和 Map

            // EnumSet 是 枚举类型 的集的高效实现
            // 没有 public 构造方法,需要使用静态工厂方法构造集
    
            EnumSet<Size> sizes ;
            // 返回一个包含所有枚举值的集
            sizes= EnumSet.allOf(Size.class);
            // 返回一个空集
            sizes= EnumSet.noneOf(Size.class);
            // 返回一个从 x 到 x 的枚举值的集
            sizes=EnumSet.range(Size.SMALL,Size.LARGE);
            // 返回一个包含 参数 1 ,参数 2,参数 3 ··· 的枚举值得值
            sizes=EnumSet.of(Size.SMALL,Size.LARGE);
            sizes.forEach(System.out::println);
    
    
            // EnumMap 是一个键类型是枚举类型的 map,它直接高效的用数组实现
            // 构造时需要指定键的枚举类型
            EnumMap<Size, Object> enumMap = new EnumMap<>(Size.class);
    
    IdentityHashMap

    根据对象的内存来判断 key 是否一致的 Map

            // IdentityHashMap
            // 这个类中键的散列值是使用 System.identityHashCode(Object o) 计算,(一种根据对象的内存地址来计算散列码的方式)
            // 而不是 hashCode()
            // 对象比较的时候,使用的是 == 而不是 equals
    
            String a="abcd";
            String b= "ab";
    
            // 避免 编译器优化,使 a,b 有不同的内存地址
            if (a.equals("abcd")){
                b+="cd";
            }
    
            System.out.println(a==b); //false
            System.out.println(a.equals(b));// true
            System.out.println(System.identityHashCode(a)==System.identityHashCode(b));//false
    
    
            IdentityHashMap<String,Integer> identityHashMap=new IdentityHashMap<>();
            identityHashMap.put(a,1);
            identityHashMap.put(b,2);
    
    
            identityHashMap.forEach((k,v)->{
                System.out.println(k+":"+v);
            });
    
            /*
            输出结果
            false
            true
            false
            abcd:1
            abcd:2
    */
    
    视图与包装器

    类似 HashMap 的 keySet() values() 还有一些其他视图

    轻量级集合包装器
    • 数组包装成 List : Arrays.asList()
            // List = Arrays.asList()  可以修改数据,不能改变数组大小(不能对list添加删除)
            String[] strings={"1zs","2ls","3ww"};
            List<String> list = Arrays.asList(strings);
    
            list.set(0,"1-set");
            list.get(0);
    
            // 输出  1-set  2ls  3ww
            list.forEach(s -> {System.out.println(s);});
            //  [1-set, 2ls, 3ww]
            System.out.println(Arrays.toString(strings));
    
            // 任何改变 数组大小的操作如 add remove 都会抛出异常
            //list.add("ss");
    
    • 单一元素
    
    
            // List = Collections.nCopies(重复次数,要重复的Object)
            // 不允许修改数据
            List<String> list2 = Collections.nCopies(15, "你好");
            // 输出 15 组你好
            list2.forEach(System.out::print);
            System.out.println();
    
            // 不允许修改数据
            //list2.set(2,"不好");
    
            // 不允许改变
            //list2.add("xx");
    
            // 获得一个单一的元素集、列表、映射
            Set<String> singleton = Collections.singleton("1");
            List<String> singletonList = Collections.singletonList("1");
            Map<Integer, String> singletonMap = Collections.singletonMap(1, "1");
    
    子范围
    • List 子范围

      获得一个范围内的子列表

            // List 子范围  List.subList(int fromIndex, int toIndex);
            // 可以将任何操作应用到子范围(包括 remove add)
            List<Integer> list3=new LinkedList<>();
            list3.add(1);
            list3.add(2);
            list3.add(3);
            list3.add(4);
    
            // 返回一个索引从 参数 1 (包括此索引) ,到 参数 2 (不包括此索引) 的 List
            // 用函数定义域理解就是 [ 1 ,3 )
            List<Integer> list4 = list3.subList(1, 3);
    
            list4.clear(); //  list3:[1,  4]
            list4.add(6);  //  list3:[1, 6, 4]
    
            System.out.println(list3);
    
    • SortedSet 子范围

      获得一个范围内的子元素集

    // SortedSet 子范围
    // 可以在 子范围内进行操作
    SortedSet<String> sortedSet = new TreeSet<>();
    sortedSet.add("az");
    sortedSet.add("za");
    sortedSet.add("gg");
    
    // 返回 大于等与参数1 小于 参数2 的元素子集
    // ["bb" ,"qq")
    SortedSet<String> sortedSet2 = sortedSet.subSet("bb", "qq");
    System.out.println(sortedSet2);// [gg]
    sortedSet2.remove("gg");   // sortedSet:[az, za]
    
    // 可以在子范围内操作 添加
    sortedSet2.add("qa");  // sortedSet: [az, qa, za]
    
    // "zz">"qq" 添加了超出子范围的 数据 会抛出异常
    //sortedSet2.add("zz");
    System.out.println(sortedSet);
    
    // 返回 小于 "qa" 的元素子集
    sortedSet.headSet("qa"); // [az]
    // 返回 大于等于 "az" 的元素子集
    sortedSet.tailSet("az"); // [az, qa, za]
    
    不可修改的视图、同步视图、受查视图
    • 同步视图
      转换获得一个具有同步访问方法的 集合
    • 受查视图
      插入时检查 插入对象是否为给定类
            // 不可修改的视图
    //        Collections.unmodifiableCollection
    //        Collections.unmodifiableList
    //        Collections.unmodifiableSet
    //        Collections.unmodifiableSortedSet
    //        Collections.unmodifiableNavigableSet
    //        Collections.unmodifiableMap
    //        Collections.unmodifiableSortedMap
    //        Collections.unmodifiableNavigableMap
    
            Collection<String> strings1 = Collections.unmodifiableCollection(list);
            System.out.println(strings1);
    
    
            // 同步视图
            // 转换获得一个具有同步访问方法的 集合
            Collections.synchronizedCollection(list);
    
            // Collections.synchronizedList()
            // 其他方法同上省略 ········
    
    
            // 受查视图 
            // 插入时检查 插入对象是否为给定类
    
            // 如下代码
            ArrayList<String> list8=new ArrayList<>();
            ArrayList list9= list8;
            list9.add(new Date());
    
            // 在这里会报错 ,但是实际上出错的位置是在 add 处,无法正确检测到出错位置
            // String s= (String) list9.get(0);
    
            // 获得一个安全的列表视图,在 add 时候就会检查插入对象是否为给定类
            List<String> safeList=Collections.checkedList(list8,String.class);
    
            // Collections.checkedCollection()
            // 其他方法同上省略 ········
    
    常用算法
    • 排序和混排
    
            List<Integer> list=new ArrayList<>();
    
            list.add(7);
            list.add(9);
            list.add(2);
            list.add(3);
    
            System.out.println(list);
    
            // 排序 默认升序
            Collections.sort(list);
            System.out.println(list); // [2, 3, 7, 9]
    
            // 自定义比较器
            list.sort((a,b)->{ return  b.compareTo(a);}); // [9, 7, 3, 2]
            list.sort(Integer::compareTo);  //  [2, 3, 7, 9]
    
    
            // public int compare(Comparable<Object> c1, Comparable<Object> c2) {return c2.compareTo(c1);}
            // 传入了一个 用 c2.compareTo(c1) 实现 compare 方法的比较器,因此排出来就是降序的了
            // 降序排列
            list.sort(Comparator.reverseOrder());
            System.out.println(list);   // [9, 7, 3, 2]
    
            // 将原数据转化成 double 在做逆序排列
            // Integer::doubleValue 是传入的 转化成 double 数据的方法
            list.sort(Comparator.comparingDouble(Integer::doubleValue).reversed());
            System.out.println(list);
    
            // 根据员工的工资 逆序排列员工
            //staff.sort(Comparator.compari ngDoubl e(Empl oyee::getSalary) . reversedO)
    
            // 打乱数组顺序
            Collections.shuffle(list);
    
    
            // 集合类库中使用的是归并排序,可以保证 通过比较器认为相同的两个元素,会按原有顺序排列
            // 比如 已经按照总分排好的 学生成绩表,在按照数学成绩排序时,数学成绩相同的学生,总分高的仍会排在前面
    
    • 二分查找

      使用二分查找必须先排序,否则会得出错误的结果

      二分查找 对支持随机访问的数据结构才有意义,如果提供的是 LinkedList 将自动变为线性查找

    
            List<Integer> list=new ArrayList<>();
    
            Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
            //for (Integer num : nums) { list.add(num);}
            list=Arrays.asList(nums);
    
            // 使用二分查找必须先排序,否则会得出错误的结果
            Collections.sort(list);
            System.out.println(list); // [2, 3, 4, 4, 6, 6, 6, 7, 7, 8, 9]
    
            // 返回找到元素的下标,如果元素重复,返回的是根据二分查找算法首次找到元素的下标,而不是重复元素的首个下标
            // 查找失败时返回负数 i ,-i-1 为查找元素应插入位置
            int index = Collections.binarySearch(list, 6);
            System.out.println(index);  // 5
    
            // 二分查找 对支持随机访问的数据结构才有意义,如果提供的是 LinkedList 将自动变为线性查找
    
    • Collections 提供的一些简单算法
    
            List<Integer> list=new ArrayList<>();
            Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
            //list=Arrays.asList(nums);
            for (Integer num : nums) { list.add(num);}
    
            int i;
            i=Collections.max(list);
            i=Collections.min(list);
    
    
            // Collection.copy 是浅拷贝
            // Collection.copy 方法要求目标列表的 size 必须大于 源列表的 size ,否则直接抛出IndexOutOfBoundsException
            // list 的size() 只有在 add 和 remove 的时候才会发生改变,所以先用空对象填充
            List<Integer> newList=new ArrayList(Arrays.asList(new Object[list.size()]));
    
            Collections.copy(newList,list);
            System.out.println(newList);
    
    
    
            // boolean List.addAll(Collection<? extends E> c);
            // 把一个集合的所有元素添加到 list 中 ,浅拷贝  添加的是引用
            List list3=new ArrayList();
            list3.addAll(list);
    
    
            // 将列表中所有位置,都用相同的元素填充
            Collections.fill(newList,2); // [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
    
            Collections.replaceAll(list,2,-2); // [9, 7, 6, -2, 6, 4, 7, 8, 6, 3, 4]
    
            Integer[] sub={2,2};
            List subList=Arrays.asList(sub);
            // 查找 子列表 首次出现时的索引
            i=Collections.indexOfSubList(newList,subList); // 0
            // 查找 子列表 最后一次出现时的索引
            i=Collections.lastIndexOfSubList(newList,subList);// 9
    
            // 交换 给定两个索引的元素 位置
            Collections.swap(list,0,1); // [7, 9, 6, -2, 6, 4, 7, 8, 6, 3, 4]
    
            // 逆置 列表中的元素
            Collections.reverse(list); // [4, 3, 6, 8, 7, 4, 6, -2, 6, 9, 7]
    
            // 旋转列表中的元素,将所有元素向右循环移动 d(2) 个位置, 即 i 移动到 (i+d)%list.size() 位置
            Collections.rotate(list,2); // [9, 7, 4, 3, 6, 8, 7, 4, 6, -2, 6]
    
            // 元素出现的次数
            i=Collections.frequency(list,6); // 3
    
            // 两个集合是否有没有交集 ,两个集合没有共同元素 返回 true
            boolean b=Collections.disjoint(list,newList);
    
    
            Collection<Integer> collection=list;
    
            // 条件删除
            collection.removeIf(t -> { return   t<0?true:false;}); // [9, 7, 4, 3, 6, 8, 7, 4, 6, 6]
    
            // 自定义对列表所有元素进行操作
            list.replaceAll( t->{ return -t;});  // [-9, -7, -4, -3, -6, -8, -7, -4, -6, -6]
    
            System.out.println(list);
    
    • 按批量作
    
            List<Integer> list=new ArrayList<>();
            Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
            //list=Arrays.asList(nums);  // 涉及到 add remove 不能使用!
            for (Integer num : nums) { list.add(num);}
    
    
            Collection collection=list;
            Collection collection2=new ArrayList();
            collection2.add(6);
    
            // 移除所有 出现在 collection2 的元素
            collection.removeAll(collection2); // [9, 7, 2, 4, 7, 8, 3, 4]
    
            // 移除所有 没有出现在 collection2 的元素
            collection.retainAll(collection2);  // []
    
            // 添加所有 collection2 的元素,浅拷贝
            collection.addAll(collection2); // [6]
    
            System.out.println(collection);
    
    • 集合和数组转换
    
            Integer[] nums={1,3,5,4,8};
    
            // 数组转换列表,详情参考视图和包装
            List<Integer> list=Arrays.asList(nums);
    
    
            // 列表转换数组,
            // 数组类型不能强制转换,Object数组 类 和 Integer数组 类 是无法互相转换的
            // 这样不方便操作,   (Integer)obejects[0]
            Object[] objects=list.toArray();
    
    
            Integer[] nums2;
            // 可以传入一个指定类型的数组 将返回同类型数组
            // 如果传入数组长度不够,会新建一个同类型数组并返回
            nums2=list.toArray(new Integer[0]);
            // 如果传入数组长度够用,则直接填入到这个数组中并返回
            nums2=list.toArray(new Integer[10]);
            nums2=list.toArray(new Integer[list.size()]);
    
    

    相关文章

      网友评论

          本文标题:十、集合

          本文链接:https://www.haomeiwen.com/subject/ukrqkktx.html