Java常见集合框架

集合框架

  • 集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用
    • 为了满足不同的业务场景,Java提供了很多不同特点的集合给我们选择

image-20230728162848596

Collection

  • 所有单列集合的祖宗,每个元素只包含一个值

image-20230728162956409

常用方法:

image-20230728163643000

  • 使用:

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    
    public class CollectionTest2API {
        public static void main(String[] args) {
            Collection<String> c = new ArrayList<>();//多态
    
            //1.public boolean add(E e) 添加元素,成功返回true
            c.add("java1");
            c.add("java2");
            c.add("java1");
            c.add("java2");
            c.add("java3");
            System.out.println(c);
    
            //2.public void clear() 清空集合元素
    //        c.clear();
    //        System.out.println(c);
    
            //3.public boolean isEmpty() 判断集合是否为空
            System.out.println(c.isEmpty());
    
            //4.public int size() 获取集合大小
            System.out.println(c.size());
    
            //5.public boolean contains(Object obj) 判断集合中是否包含某个元素
            System.out.println(c.contains("java"));
            System.out.println(c.contains("java1"));
    
            //6.public boolean remove(E e) 删除某个元素:如果有多个重复元素,则默认删除最前面的一个
            System.out.println(c.remove("java1"));
            System.out.println(c);
    
            //7.public Object[] toArray() 把集合转换成数组
            Object[] objects = c.toArray();
            System.out.println(Arrays.toString(objects));
            String[] s = c.toArray(new String[c.size()]);
            System.out.println(Arrays.toString(s));
    
            System.out.println("----------------------------------");
    
            //把一个集合的全部数据拷贝一份复制到另一个集合中去
            Collection<String> c1 = new ArrayList<>();
            c1.add("java1");
            c1.add("java2");
            Collection<String> c2 = new ArrayList<>();
            c2.add("java1");
            c2.add("java2");
            c1.addAll(c2);//把c2集合的全部数据拷贝一份到c1集合中
            System.out.println(c1);
            System.out.println(c2);
        }
    }
    

Collection的遍历方式:

  • 迭代器

    • 迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator

      image-20230728164302422

  • 增强for

    • 格式:

      for(元素的数据类型 变量名 : 数组或者集合){
      	
      }
      
      Collection<String> c = new ArrayList<>();
      for(String s:c){
          System.out.println(s);
      }
      
    • 增强for可以用来遍历集合或者数组

    • 增强for遍历集合,其本质就是迭代器遍历集合的简化写法

  • Lambda表达式遍历集合

    • 得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合

    • 格式:

      Collection<String> list = new ArrayList<>();
      list.forEach(new Consumer<String>{
      	@Override
      	public void accept(String s){
      		System.out.println(s);
      	}
      });
      
      list.forEach( s -> System.out.println(s));
      
      list.forEach(System.out::println);
      
      

*ps:*Collection不能普通for循环遍历,因为没有规定索引,但是其子接口list规定了索引,故可以使用普通for循环根据索引遍历

集合存储对象的原理:

image-20230728165057852

*ps:*集合是泛型的,只能存储引用数据类型的对象,故实际存储的都是元素对象的地址

不同单列集合的应用场景

image-20230730154618962

List

特点:

  • 添加的元素是有序、可重复、有索引

ArrayList & LinkedList不同:

  • 底层实现不同,优缺点不同,适合的场景不同

List集合的特有方法:

image-20230728170142633

  • 使用:

    import java.util.ArrayList;
    import java.util.List;
    
    public class ListTest1 {
        public static void main(String[] args) {
            //1.创建一个ArrayList集合对象(有序 可重复 有索引)
            List<String> list = new ArrayList<>();
            list.add("蜘蛛精");
            list.add("至尊宝");
            list.add("至尊宝");
            list.add("牛夫人");
    
            //2.public void add(int index,E element) 在某个索引位置插入元素
            list.add(2,"紫霞仙子");
            System.out.println(list);
    
            //3.pulbic E remove(int index) 删除当前索引位置处的元素,返回被删除的元素
            System.out.println(list.remove(2));
            System.out.println(list);
    
            //4.public E get(int index) 返回集合中指定位置的元素
            System.out.println(list.get(3));
    
            //5.public E set(int index,E element) 修改当前索引位置处的元素,修改成功后,会返回原来的数据
            System.out.println(list.set(3, "牛魔王"));
            System.out.println(list);
        }
    }
    
    

List集合支持的遍历方式:

  • for循环(因为List集合有索引)
  • 迭代器
  • 增强for
  • Lambda表达式
ArrayList

特点:

  • 有序、可重复、有索引

底层原理:

  • 基于数组实现

    image-20230728170445432

image-20230728170522938

优缺点:

  • 查询快,增删慢

适合的应用场景:

image-20230728170735406

####LinkedList

特点:

  • 有序、可重复、有索引

底层原理:

  • 基于双链表实现的

    • 链表:

    image-20230728170912867

    • 双链表:

      image-20230728170945100

底层原理:

  • 双链表

优缺点:

  • 查询慢,增删快,但对首尾元素进行增删改查的速度是极快的

常用方法:

image-20230728171109430

应用场景

  1. 队列

    • 只是在首尾增删(首删尾增)元素,用LinkdeList实现正合适

      image-20230728171309511

    • 使用:

              //1.创建一个队列(queue)
              //只是在首尾增删元素,用LinkedList实现很合适
              //先进先出,后进后出,就是排队
              //实际应用:排队,叫号系统
              LinkedList<String> queue = new LinkedList<>();
      
              //入队
              queue.addLast("第1个人");
              queue.addLast("第2个人");
              queue.addLast("第3个人");
              queue.addLast("第4个人");
              System.out.println(queue);
              //出队
              System.out.println(queue.removeFirst());
              System.out.println(queue.removeFirst());
              System.out.println(queue.removeFirst());
              System.out.println(queue);
      
    • 只是在首部增添元素,用LinkedList实现正合适

      image-20230728171458780

    • 使用:

      //2.创建一个栈对象
              //只是在首部增添元素,用LinkedList实现很合适
              //先进后出,后进先出
              LinkedList<String> stack = new LinkedList<>();
              //压栈(push)
              //push <-> addFirst
              stack.addFirst("第一颗子弹");
              stack.addFirst("第二颗子弹");
              stack.push("第三颗子弹");
              stack.push("第四颗子弹");
              //出栈(pop)
              //pop <-> removeFirst
              System.out.println(stack.removeFirst());
              System.out.println(stack.pop());
              System.out.println(stack.pop());
              System.out.println(stack);
      

Set

特点

  • 添加的元素是无序、不重复、无索引

*ps:*Set要用到的常用方法,基本上就是Collection提供的,自己几乎没有额外新增一些常用功能

哈希值

  • 就是一个int类型(-21亿 ~ 21亿)的数值,Java中每个对象都有一个哈希值

  • Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值

    public int hashCode();
    
  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的

  • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)

*ps:*sout打印出来的对象(默认调用toString方法)其实并不是真实物理地址,而是根据对象的哈希值转成十六进制等一系列方法得到的,所以两个对象打印出来显示的“地址”相同,但实际只是哈希值相同并不是真实物理地址相同,所以调用equals方法或者==判断时依然为false

HashSet

特点

  • 无序、不重复、无索引

底层原理:

  • 基于哈希表实现的

    • 哈希表是一种增删改查数据,性能都较好的数据结构

      • JDK8之前,哈希表由数组 + 链表构成

        image-20230730152623843

        ps:占满16*0.75=12个坑数组就扩容2倍,以防止链表过长,导致查询性能降低

      • JDK8开始,哈希表由数组 + 链表 + 红黑树构成

        image-20230730153250875

优缺点:

  • 增删改查都较快

ps:

  • 如果希望Set集合认为2个对象内容一样就是重复的,必须在该对象对应的类下重写hashCode()和equals()方法

    image-20230730153428412

  • 对于equals方法:Object、 Objects类的equals()方法都是比较地址(针对引用数据类型),但是Objects多了一层判断是否是null,避免了空指针异常,但其为static静态方法不支持重写;而Object为所有类的父类,equals支持重写,但Objects归根还是调用的Object的equals方法,所以重写对Objects也有影响(?

LinkedHashSet

特点

  • 有序、不重复、无索引

底层原理

  • 依然是基于==哈希表(数组 + 链表 + 红黑树)==实现的

  • 但是,它的每个元素都额外多了一个双链表的机制记录它前后元素的位置

    image-20230730153837465

TreeSet

特点

  • 排序(默认升序)、不重复、无索引

底层原理

  • 基于红黑树实现的排序

ps:

  • 对于数值类型:Integer、Double,默认按照数值本身的大小进行升序排序
  • 对于字符串类型:默认按照首字母的编号(字典序)升序排序
  • 对于自定义类型如Student对象,TreeSet默认是无法排序的,需要自己定义排序规则
    • TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持以下两种方式来制定比较规则
      • 1.让自定义的类实现Comparable接口,重写里面的compareTo方法来制定比较规则
      • 2.通过调用TreeSet集合的有参构造器,可以设置Comparator对象(比较器对象,用于制定比较规则)
      • *ps:*如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序

集合的并发修改异常

  • 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误
  • 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的问题;由于Lambda表达式遍历调用forEach方法本质上也是调用增强for循环,因此,使用Lambda表达式遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的问题

解决方法

  • 使用迭代器遍历集合,使用迭代器自己的删除方法删除数据,而不用集合的删除方法删除

    List<String> list = new ArrayList<>();
    Iterator it = list.Iterator();
    /*
    集合中增添数据...
    */
    while(it.hasNext()){
    	if(...){
    		it.remove();//迭代器当前指向的元素删除,并在底层实现类似于普通for循环i--的类似操作,指向当前位置的新的元素
    	}
    }
    
  • list接口的实现类由于存在索引,故可以使用for循环遍历:

    • 倒着遍历并删除
    • 从前往后遍历,删除元素后做i–操作

Collections及其相关知识

前置知识:可变参数

  • 可变参数就是一种特殊形参,定义在方法、构造器的形参列表里,本质是某个数据类型的数组,格式是:数据类型…参数名称
  • 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它
  • 好处:可以用来灵活的接收数据
  • 注意事项:
    • 一个形参列表中,只能有一个可变参数
    • 可变参数必须放在形参列表的最后面

Collections

  • 用来操作单列集合Collection的工具类

    image-20230812112956054

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CollectionsTest1 {
    public static void main(String[] args) {
        //掌握Collections集合工具类的使用
        //1.public static<T> boolean addAll(Collection<? super T> c, T...elements) 为集合批量添加元素
        List<String> names = new ArrayList<>();
        Collections.addAll(names,"wxy","xiaoming","xiaohong");
        System.out.println(names);

        //2.public static void shuffle(List<?> list) 打乱List集合中的元素顺序
         Collections.shuffle(names);
        System.out.println(names);

        //3.public static <T> void sort(List<T> list) 对List集合中的元素进行升序排序
        List<Integer> nums = new ArrayList<>();
        Collections.addAll(nums,5,35,2,79,1616,8,71,6);
        System.out.println(nums);
        Collections.sort(nums);
        System.out.println(nums);

        //4.public static <T> void sort(List<T> list, Comparator<? super T> c )
        // 对List集合中元素,按照比较器对象指定的规则进行排序,一般应用于集合中存储自定义类型对象的qing
        Collections.sort(nums, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return 0;
            }
        });

        System.out.println(nums);
    }
}

Map

  • 双列集合,每个元素包含两个值(键值对),格式:{key1=value1,key2=value2…},一次需要存入一对数据作为一个元素
  • Map集合的每个元素”key = value“ 被称为一个键值对/键值对对象/Entry对象,Map集合也被叫做==”键值对集合”==
  • Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
  • 需要存储一一对应的数据时,就可以考虑使用Map集合来存储

image-20230913194356430

Map集合的常见方法

image-20230913194459226

代码:

package d1_map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest2 {
    public static void main(String[] args) {
        //1.public V put(); 添加元素
        Map<String, Integer> map = new HashMap<>();//一行经典代码,无序,不重复,无索引
        map.put("鼠标", 100);
        map.put("鼠标", 250);//后面的数据会覆盖前面的数据(根据键)
        map.put("电脑", 100);
        map.put("Java书籍", 1000000000);
        map.put(null, null);
        System.out.println(map);

        //2.public int size();获取集合的大小
        System.out.println(map.size());

        //3.public void clear();清空集合
//        map.clear();
//        System.out.println(map);

        //4.public boolean isEmpty(); 判断集合是否为空
        System.out.println(map.isEmpty());

        //5.public V get(Object key) 根据键获取对应值
        System.out.println(map.get("Java书籍"));
        System.out.println(map.get("Java"));//返回null

        //6.public V remove(Object key); 根据键删除整个元素(删除键会返回键的值)
        System.out.println(map.remove("Java书籍"));//1000000000
        System.out.println(map.remove("Java"));//null

        //7.public boolean containsKey(Object key); 判断是否包含某个键
        System.out.println(map.containsKey("Java书籍"));//false
        System.out.println(map.containsKey("鼠标"));//true

        //8.public boolean containsValue(Object value); 判断是否包含某个值
        System.out.println(map.containsValue(10000));//false
        System.out.println(map.containsValue(null));//true

        //9.public Set<K> keySet(); 键不可重复故用Set集合存储,获取Map集合的全部键
        Set<String> keys = map.keySet();
        System.out.println(keys);

        //10.public Collection<V> values(); 值可以重复故用Collection集合存储,获取Map集合的全部值
        Collection<Integer> values = map.values();
        System.out.println(values);

        //11.把其他Map集合的数据倒入到自己集合中去(拓展)
        Map<String,Integer> map1 = new HashMap<>();
        map1.put("java1",10);
        map1.put("java2",20);
        HashMap<String, Integer> map2 = new HashMap<>();
        map2.put("java2",200000);
        map2.put("java3",30);
        map1.putAll(map2);//putAll:把map2集合中的元素全部复制粘贴一份到map1集合里面
        System.out.println(map1);
        System.out.println(map2);
    }
}

Map集合的遍历方式

image-20230913203810711

  1. 键找值

image-20230913203842040

package d2_map_bianli;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
    Map集合的遍历方式:
    1.键找值
 */
public class MapTest1 {
    public static void main(String[] args) {
        //准备一个Map集合
        Map<String,Double> map = new HashMap<>();
        map.put("蜘蛛精",162.5);
        map.put("蜘蛛精",169.5);
        map.put("紫霞",167.5);
        map.put("至尊宝",177.5);
        map.put("牛魔王",172.5);
        System.out.println(map);
        // {蜘蛛精=169.5, 牛魔王=172.5, 至尊宝=177.5, 紫霞=167.5}

        //1.获取Map集合的全部键存入Set集合中
        Set<String> keys = map.keySet();
        //2.遍历Set集合根据键找值
        for (String key : keys) {
            Double value = map.get(key);
            System.out.println(key + "--->" + value);
        }
    }
}
  1. 键值对

image-20230913203909164

package d2_map_bianli;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
    Map集合的遍历方式:
    2.键值对
 */
public class MapTest2 {
    public static void main(String[] args) {
        //准备一个Map集合
        Map<String,Double> map = new HashMap<>();
        map.put("蜘蛛精",162.5);
        map.put("蜘蛛精",169.5);
        map.put("紫霞",167.5);
        map.put("至尊宝",177.5);
        map.put("牛魔王",172.5);
        System.out.println(map);
        // map = {蜘蛛精=169.5, 牛魔王=172.5, 至尊宝=177.5, 紫霞=167.5}

        //1.调用Map集合提供的entrySet()方法,把Map集合转换成键值对类型的Set集合
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        // entries = {(蜘蛛精=169.5), (牛魔王=172.5), (至尊宝=177.5), (紫霞=167.5)}
        //2.遍历Set集合的每个键值对对象,分别调用getKey、getValue取到键、值
        for (Map.Entry<String, Double> entry : entries) {
            String key = entry.getKey();
            Double value = entry.getValue();
            System.out.println(key + "--->" + value);
        }
    }
}
  1. Lambda

image-20230913203957228

package d2_map_bianli;

import java.util.HashMap;
import java.util.Map;

/**
    Map集合的遍历方式:
    3.Lambda
 */
public class MapTest3 {
    public static void main(String[] args) {
        //准备一个Map集合
        Map<String,Double> map = new HashMap<>();
        map.put("蜘蛛精",162.5);
        map.put("蜘蛛精",169.5);
        map.put("紫霞",167.5);
        map.put("至尊宝",177.5);
        map.put("牛魔王",172.5);
        System.out.println(map);
        // map = {蜘蛛精=169.5, 牛魔王=172.5, 至尊宝=177.5, 紫霞=167.5}

//        map.forEach(new BiConsumer<String, Double>() {
//            @Override
//            public void accept(String k, Double v) {
//                System.out.println(k + "--->" + v);
//            }
//        });

        //Lambda表达式简化代码,源码其实还是调用了键值对遍历的方法
        map.forEach((String k, Double v) -> { System.out.println(k + "--->" + v); });

    }
}

Student类(后续会使用到)

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private double height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public Student() {
    }

    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        //根据对象的内容生成hashcode值,内容一样hashcode值则一样,
        //存放在HashMap集合中所对应的哈希表的位置一样,进而进行equals判断
        return Objects.hash(name, age, height);
    }

    @Override
    //调用该方法的this,传入的参数o
    public int compareTo(Student o) {
        return this.getAge() - o.getAge();
    }
}

HashMap

特点

  • 无序、不重复、无索引

底层原理:

image-20230914162833001

  • 基于哈希表实现的

    • 哈希表是一种增删改查数据,性能都较好的数据结构

      • JDK8之前,哈希表由数组 + 链表构成

        image-20230730152623843

        ps:占满16*0.75=12个坑数组就扩容2倍,以防止链表过长,导致查询性能降低

      • JDK8开始,哈希表由数组 + 链表 + 红黑树构成

        image-20230914162906668

        image-20230730153250875

优缺点及使用:

image-20230914162957891

ps:

  • 如果希望Map集合认为2个对象内容一样就是重复的,必须在该对象对应的类(Student类)下重写hashCode()和equals()方法

    • 重写前的hashCode根据对象在内存中所处位置分配hash值,重写后根据对象的内容分配hash值;重写前的equals比较hash值是否相等,重写后的equals比较内容是否相等。
    import java.util.HashMap;
    import java.util.Map;
    
    public class Test1HashMap {
        public static void main(String[] args) {
            Map<Student,String> map = new HashMap<>();
            map.put(new Student("蜘蛛精",18,165.5),"水帘洞");
            map.put(new Student("蜘蛛精",18,165.5),"盘丝洞");
            map.put(new Student("至尊宝",21,175.5),"水帘洞");
            map.put(new Student("牛魔王",24,178.9),"牛头山");
    
            System.out.println(map);
        }
    }
    

    image-20230730153428412

LinkedHashMap

特点

  • 有序、不重复、无索引

底层原理

import java.util.LinkedHashMap;
import java.util.Map;

public class Test2LinkedHashMap {
    public static void main(String[] args) {
        Map<String,Integer> map = new LinkedHashMap<>();//有序、不重复、无索引
        map.put("手表",100);
        map.put("手表",200);
        map.put("Java",100000000);
        map.put(null,null);
        System.out.println(map);
    }
}

image-20230914164910395

TreeMap

特点

  • 有序、不重复、无索引

底层原理

  • 基于红黑树实现的排序

image-20230914165027280

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

public class Test3TreeMap {
    public static void main(String[] args) {
        Map<Student,String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o1.getHeight(),o2.getHeight());
            }
        });//排序,不重复,无索引

        //Lambda简化
//        Map<Student,String> map = new TreeMap<>((Student o1, Student o2) -> { return Double.compare(o1.getHeight(),o2.getHeight()); });//排序,不重复,无索引

        map.put(new Student("蜘蛛精",18,165.5),"水帘洞");
        map.put(new Student("蜘蛛精",18,165.5),"盘丝洞");
        map.put(new Student("至尊宝",21,175.5),"水帘洞");
        map.put(new Student("牛魔王",24,178.9),"牛头山");

        System.out.println(map);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值