【JavaSE】之集合框架


前言

本文为Java集合框架相关知识,Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

一、集合框架体系

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

二、List

1.数组扩容

// 扩容方法和位运算
public class Demo01 {
    public static void main(String[] args) {
        int[] arr= new int[4];
        arr[0] = 10;
        arr[1] = 11;
        arr[2] = 12;
        arr[3] = 13;
        arr = grow(arr);
        System.out.println(arr.length);
    }
    public static int[] grow(int[] arr){
        //int[] newArr = new int[arr.length+arr.length/2];
        //  >>1  位运算  二进制向右移一位变成原来的 一半
        //  1<<          二进制向左移一位变成元的的 一倍
        int[] newArr = new int[arr.length+arr.length>>1];
        /**
           方法一:
         */
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        /**
         *  方法二:
         */
        System.arraycopy(arr , 0 ,newArr,0,arr.length);
        /**
         *  方法三:
         */
        newArr = Arrays.copyOf(arr, arr.length+arr.length/2);
        return newArr;
    }
}

2.迭代器 : 只服务于Collection接口下的集合

Iterator

  • hasNext() 如果迭代具有更多元素,则返回 true 。
  • next() 返回迭代中的下一个元素。
  • remove() 从底层集合中删除此迭代器返回的最后一个元素(可选操作)。
    // 使用Iterator 迭代器
    public static void print(ArrayList<Person> list){
        Iterator<Person> iterator = list.iterator();
        while (iterator.hasNext()){
            Person person = iterator.next();
            System.out.println(person.getName() + "\t" + person.getAge());
        }
    }

ListIterator

  • void add(E e) :将指定的元素插入列表(可选操作)。
  • boolean hasNext():如果此列表迭代器在向前方向遍历列表时具有更多元素,则返回 true 。
  • boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回 true 。
  • E next() 返回列表中的下一个元素,并且前进光标位置。
  • int nextIndex() 返回由后续调用返回的元素的索引 next() 。
  • E previous()返回列表中的上一个元素,并向后移动光标位置。
  • int previousIndex() 返回由后续调用返回的元素的索引 previous()。
  • void remove() 从列表中删除 next()或 previous() (可选操作)返回的最后一个元素。
  • void set(E e) 用指定的元素(可选操作)替换 next()或 previous()返回的最后一个元素。
public static void itRepeat(ArrayList<Person> list){
        System.out.println("去除重复元素:");
        // 使用ListIterator 接口可以实现从指定位置迭代集合
        ListIterator<Person> iterator;
        for (int i = 0; i < list.size() ; i++) {
           iterator = list.listIterator(i+1);
           while (iterator.hasNext()){
               Person person = iterator.next();
               if (list.get(i).equals(person)){
                   iterator.remove();
                   System.out.println("即将被删除:..."+person);
               }
           }
        }
    }

Iterator VS ListIterator

  • ListIterator有add()方法,可以向List中添加对象,而Iterator没有
  • ListIterator和Iterator都有hasNext() 和 next() 方法,但ListIterator可以实现指定位置遍历,并且可以顺序遍历和逆序遍历。
  • ListIterator有set方法,可以在遍历中修改对象。

3.ArrayList

  • 有序可重复 ,底层是动态数组实现 —— 线程不安全
  • 添加数据,如果是基本数据类型会自动装箱为包装类,第一次添加数组长度为10
  • 存储的数据超过数组长度,会自动进行数组扩容,扩容为原来的1.5倍
  • 集合长度有限,最大容量默认为: Integer.MAX_VALUE-8
  • 为什么空8位出来: 1、存储Headerwords;2、避免一些机器内存溢出,减少出错几率,所以少分配;3、最大还是能支持到Integer.MAX_VALUE(2^31 -1)
 public class Test{
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        // 1、添加元素  ——  如果数据是基本数据类型会自动装箱为包装类
        list.add(1);
        list.add("a");
        list.add(true);
        list.add(10.1);
        list.add(2);
        // 2、删除元素 —— 根据下标删除
        list.remove(1);
        // 根据对象删除
        list.remove(new Integer(2));
        // 3、修改
        list.set(1,"b");
        // 4、遍历集合
        print(list);
    }
    public static void print(ArrayList list){
        System.out.println("-------- for循环 ----------");
        for (int i = 0; i < list.size() ; i++) {
            System.out.print(list.get(i) + "\t");
        }
        System.out.println();
        System.out.println("=========== 迭代器循环 =========");
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next() + "\t");
        }
        System.out.println();
    }
}

4.LinkedList

  • 底层是 双向链表 实现 —— 线程不安全
  • 第一次添加对象,把对象添加到Node节点中,falst指向第一个节点,不存在索引
  • 集合长度无限
//  创建 LinkedList 对象
LinkedList list = new LinkedList();
list.add("111"); //添加一个新元素,默认尾部添加
list.add("222");
list.add("333");
list.addFirst("444"); //在头部添加一个新元素
list.addLast("555"); //在尾部添加一个新元素
Object data1=list.removeFirst(); //移除头部元素并返回值
System.out.println(data1); 
Object data2=list.removeLast(); //移除尾部元素并返回值
System.out.println(data2);
list.getFirst(); // 获取头部元素并返回值
list.getLast(); // 获取尾部元素并返回值
for(Object object : list){
    System.out.println(object);
}
//  其他方法的使用与 ArrayList一致

5. ArrayList和LinkedList的区别

  • 数据结构的实现不同。 ArrayList是动态数组实现,LinkedList是双向链表实现
  • List item查询的效率。ArrayList有下标查询速度快,而LinkedList底层是链表结构,查询需从头开始依次往下查找
  • 增删的效率。在排除增删前后两头的情况下,LinkedList的速度比ArrayList的快,因为LinkedList的添加和删除节点,只要其他节点的前驱后继指引值发生变化,而ArrayList增加删除会更改数组的结构

6.foreach的本质

  • 使用foreach来遍历集合的话,本质上是将其转换为迭代器来使用
System.out.println("=========== foreach循环 =========");
for (Object obj:list) {
    System.out.print(obj+"\t");
}
System.out.println();

// 等价于下边代码

System.out.println("=========== foreach循环 =========");
Iterator iterator = list.iterator();

while(iterator.hasNext()) {
    Object obj = iterator.next();
    System.out.print(obj+"\t");
}

7.Vector

  • 数组结构实现,查询快、增删慢。
  • JDK1.0版本,线程安全、运行效率比ArrayList较慢。
public class TestVector {
    public static void main(String[] args) {
        //创建集合
        Vector vector=new Vector<>();
        //1添加元素
        vector.add("草莓");
        vector.add("芒果");
        vector.add("西瓜");
        System.out.println("元素个数:"+vector.size());
        //2删除
//        vector.remove(0);
//        vector.remove("西瓜");
//        vector.clear();
        //3遍历
        //使用枚举器
        Enumeration en=vector.elements();
        while(en.hasMoreElements()) {
            String  o=(String)en.nextElement();
            System.out.println(o);
        }
        //4判断
        System.out.println(vector.contains("西瓜"));
        System.out.println(vector.isEmpty());
        //5vector其他方法
        //firsetElement、lastElement、elementAt();        
    }
}

8.Stack

  • 模拟 栈 的实现 —— 底层用数组存储数据
  • 特点是:先进后出
public class Demo05 {
    public static void main(String[] args) {
        Stack stack = new Stack();
        // 添加元素
        // Stack类的add方法与push方法区别:
        //         add方法返回的是boolean类型的值
        //         push方法返回的是当前添加的元素
        stack.push("a");
        stack.push("b");
        stack.add("c");
        stack.push("d");
        // 集合中的元素个数
        System.out.println(stack.size());
        // 弹出栈顶元素
        System.out.println(stack.pop());
        System.out.println(stack.pop());
    }
}

三、Queue

1.队列的定义

  • 队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
  • 队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
  • 队头(first):允许删除的一端,又称队首。
  • 队尾(Rearlsat:允许插入的一端。
  • 空队列:不包含任何元素的空表。

2.队列的使用

public class Demo01 {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        queue.add("小明"); // 向队列添加元素,当队列满时,add方法抛出unchecked异常,而offer方法返回false。
        queue.offer("小黑");
        queue.offer("小蓝");
        queue.offer("小而");
        // 集合中的个数 
        System.out.println(queue.size()); // 4
        // 查询头部元素:如果队列为空,element方法会抛出异常,peek方法会返回null
        System.out.println(queue.element()); // 小明
        System.out.println(queue.peek()); // 小明
        // 打印队列头  
        System.out.println(queue.poll()); // 小明 ,如果队列为空,remove方法会抛出异常,poll方法会返回null。
        System.out.println(queue.size()); // 3
        System.out.println(queue.remove()); // 小黑
    }
}

四、Set

1.TreeSet

定义:

  • 可排序的集合;
  • 不能存储空对象;
  • 底层是红黑树;
  • 使用TreeSet 存储对象必须要使用自然排序或者比较器

自然排序

  • 要存储的对象 必须实现了 Comparable 接口,并重写了 compareTo()
  • 在 compareTo() 方法中 制定了比较规则
  • 存储的对象 通过 与 之前的节点对比,返回正数则在 节点的右子树;负数则存储在左子数;返回 0 不存储对象(重复了)
// 实体类实现接口
public class Person implements Comparable<Person> {
    private int id;
    private String name;
    private int age;
    /**
     * 重写CompereTo 接口
     *      要求:  id 从大到小
     *             id相同,age从小到大
     * @param o
     * @return
     */
    @Override
    public int compareTo(Person o) {
        if (this.id == o.getId()){
            return this.age-o.getAge() ;
        }
        return o.getId() - this.id ;
    }
}
// 测试
public class Demo02 {
    public static void main(String[] args) {
        TreeSet<Person> people = new TreeSet<>();
        people.add(new Person(1,"ls",44));
        people.add(new Person(1,"zs",12));
        people.add(new Person(8,"t7",14));
        people.add(new Person(7,"t7",14));
        people.forEach(item -> System.out.println(item));
    }
}

比较器

  • 在创建TreeSet集合的时候,通过构造方法传递实现了Comparator接口的实现类对象
  • 实现类对象要重写了compare()方法,在方法里面制定排序规则
// 比较器
public class MyCompartor implements Comparator<Person> {
    /**
     * id从小到大
     * id相同,age从大到小
     * @param o1 存储的对象
     * @param o2 上一个节点的对象
     * @return
     */
    @Override               // 11         88
    public int compare(Person o1, Person o2) {
        if (o1.getId() != o2.getId())
            return o1.getId()-o2.getId();
        return o2.getAge()-o1.getAge();
    }
}
// 测试
public class Demo03 {
    public static void main(String[] args) {
        // 通过构造方法传入 比较器
/*
        TreeSet<Person> people = new TreeSet<>(new MyCompartor());
        people.add(new Person(1,"ls",44));
        people.add(new Person(1,"zs",12));
        people.add(new Person(8,"t7",14));
        people.add(new Person(7,"t7",14));
        people.forEach(item -> System.out.println(item));
*/
        // 匿名内部类
        TreeSet<Person> people = new TreeSet<>(new Comparator<Person>(){
            @Override
            public int compare(Person o1, Person o2) {
                if (o1.getId() != o2.getId())
                    return o1.getId()-o2.getId();
                return o2.getAge()-o1.getAge();
            }
        });
        people.add(new Person(1,"ls",44));
        people.add(new Person(1,"zs",12));
        people.add(new Person(8,"t7",14));
        people.add(new Person(7,"t7",14));
        people.forEach(item -> System.out.println(item));
    }
}

自然排序 VS 比较器

  • 比较器的优先级别高于自然排序
  • 自然排序需要存储的对象类结构发生改变(实现了 Comparable 接口,并重写compereTo方法)
  • 自然排序的可读性更高,并且排序规则不可见
  • 比较器会创建多一个类

2.HashSet

什么是有序和无序?

  • 有序: 根据添加的顺序先后,遍历时以同样的顺序输出
  • 无序: 遍历输出 与 添加时的顺序无关(HashSet是无序的

版本:

  • JDK1.7 : 底层 hash表(不可变) + 链表
  • JDK1.8 : 底层 hash表(动态数组) + 链表 + 红黑树

重点:

  • 存储的对象通过hashCode()得到hash值,根据hash值找到在hash表中对应的位置
  • 再根据equals() 判断是否内容相等,内容相等则不存储
  • 当hash表中的对象超过容量的负载因子数时,进行数组扩容,容量为原来的2倍
  • 当一个链表中的对象超过8个,则会转换为红黑树
public class Demo02 {
    public static void main(String[] args) {
        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(12);
        hashSet.add(11);
        hashSet.add(22);
        hashSet.add(22);
        // lambda表达式 ,打印结果
        hashSet.forEach(item -> System.out.println(item));
        // 输出:22 11 12 ,说明HashSet是无序的
    }
}

五、Map

1.HashMap

特点:

  • 无序,key不可重复
    简单使用:
  • key,value都可以为null, key为null的时候存储在HashMap的第一个位置
  • 当key相同时,新的value会覆盖旧的value
public class Demo01 {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        // 1、添加
        map.put("a",13);
        map.put("b",12);
        map.put("c",11);
        map.put("d",678);
        map.put("a",99);  // key重复,后面添加的会覆盖前面的
        System.out.println(map); //{a=99, b=12, c=11, d=678}
        // 2、删除
        // 根据key删除
        map.remove("a");
        // 根据key value 删除
        map.remove("c",11);
        // 3、修改
        // 根据key 修改value
        map.replace("b",88);
        // 4、打印
        System.out.println(map); //{b=88, d=678}
        // 5、遍历 for不可用
        System.out.println("----------- 使用keyset方法转换为保存key值的set集合 ------------");
        // 将 map 中的key值存储到 set集合中,set能调用的遍历方法他也能
        Set<String> set1 = map.keySet();
        Iterator<String> iterator1 = set1.iterator();
        while (iterator1.hasNext()){
            String key = iterator1.next();
            System.out.println("key:"+key +",value:"+map.get(key));
        }
        System.out.println("----------- 使用entrySet方法转换为保存key,value 的Set集合 ------------");
        // 将key,value 保存再Map类的内部类 Entry 类中
        Set<Map.Entry<String, Integer>> set2 = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator2 = set2.iterator();
        while (iterator2.hasNext()){
            Map.Entry<String, Integer> entry = iterator2.next();
            System.out.println("key:"+entry.getKey() + ",value:"+entry.getValue());
        }
    }
}

2.TreeMap

特点:

  • 底层是红黑树 ,无序 key不可重复
    简单使用:
  • 存储映射的关系过程中,需要key使用 自然排序或传递比较器(比较器根据key进行排序)
  • key不能为null ,value可以为null
  • 添加相同 key 时,新的value会覆盖旧的value(底层:自然排序的compareTo() 或比较器compare()
    方法返回值为0,调用t.setValue(value),实现新的值覆盖旧的值)
public class Demo02 {
    public static void main(String[] args) {
        System.out.println("--------------TreeMap-----------------");
        TreeMap<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("a",1);
        treeMap.put("a",2);
        // treeMap.put(null,1);
        treeMap.put("b",null);
        System.out.println(treeMap);
        //{a=2, b=null}
        System.out.println("--------------HashMap-----------------");
        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put("a",11);
        hashMap.put(null,11);
        hashMap.put("b",null);
        System.out.println(hashMap);
        //{null=11, a=11, b=null}
    }
}

3.HashTabl

特点:

  • 无序,key不可重复

简单使用:

  • key,value都不能为空
public class Demo03 {
    public static void main(String[] args) {
        Hashtable<String, Integer> hashtable = new Hashtable<>();
        hashtable.put("a",1);
        hashtable.put("c",2);
        //hashtable.put(null,2);    NullPointerException
        //hashtable.put("d",null);  NullPointerException
        System.out.println(hashtable);
    }
}

六.集合工具类Collections

Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具类里提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象实现同步控制等方法。

排序操作:排序相关的、顺序相关的功能

  • reverse():反转指定列表中元素的顺序
  • shuffle():使用默认随机源对指定列表进行置换(每一次的顺序都不一样)或者打乱顺序 :打乱集合顺序
  • sort():根据指定比较器产生的顺序对指定列表进行排序(默认从小到大)或者在指定列表的指定位置处交换元素。注意:sort(list)有使用前提:被排序的集合元素必须实现了Comparable接口,重写接口中的compareTo方法定义排序的规则;
  • swap():交换一下顺序 rotate():根据指定的距离轮换指定列表中的元素

查找和替换操作

  • max():根据元素的自然顺序,返回给定 collection 的最大元素
  • min():根据元素的自然顺序,返回给定 collection 的最小元素
  • replaceAll():使用另一个值替换列表中出现的所有某一指定值 frequency()返回指定 collection中等于指定对象的元素数
  • binarySearch():使用二分搜索法搜索指定列表,以获得指定对象。
  • indexOfSubList():查找子列表在列表中第一次出现的位置,没有返回-1
  • lastIndexOfSubList():查找子列表在列表中最后一次出现的位置,没有返回-1
  • fill():使用指定元素替换指定列表中的所有元素

同步控制

  • Collections类中提供了多个synchronized…()方法,这些方法可以将指定集合包装成线程同步(线程安全)的集合,从而可以解决多线程并发访问集合时的线程安全问题。
  • Java中常用的集合框架中的实现类 ArrayList、Linkedlist、 HashSet、TreeSet、HashMap和TreeMap 都是线程不安全的。如果有多个线程访问它们,而且有超过一个的线程试图修改它们,则存在线程安全的问题。
  • Collections提供了多个类方法可以把它们包装成线程同步的集合。

代码示例:

public class CollectionsTest {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(9);
        list1.add(2);
        list1.add(2);
        Integer[] arr = {7,6,9};
// addAll(Collection<? super T> c, T... elements) 将所有指定的元素添加到指定的集合。
        Collections.addAll(list1,4,5,6);
        Collections.addAll(list1,arr);
        // 打印集合
        System.out.println(list1);//[9, 2, 2, 4, 5, 6, 7, 6, 9]
        // 对集合元素进行排序
        Collections.sort(list1);
        System.out.println(list1);//[2, 2, 4, 5, 6, 6, 7, 9, 9]
    }
}

后记

Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小新要变强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值