Java容器合集

关于这些容器的底层实现、大小扩容机制、线程安全性等可以查看这个:

 JAVA容器详解

  

浅谈

  • 容器: 容器是能够盛放物质的器皿,比如水杯,可以盛放水

  • 编程语言中的容器: 用于存储数据的数据结构,称为容器

Java中的容器可以整体认为三类: Array、Collection 和 Map,图中加粗是重点学习对象

(Collection里其实还有一个Queue队列,在开头的跳转链接里,很少提到)

泛型类

ArrayList:实现了List接口的动态数组。可以根据需要动态增加或减少元素的大小,并提供快速的随机访问操作。常用于存储一组元素,支持按索引访问、插入和删除操作。
LinkedList:实现了List接口的双向链表。可以用作队列、栈或双端队列。
HashSet:实现了Set接口的哈希集合。用于存储不重复元素,提供快速的查找和插入操作。
TreeSet:实现了Set接口的红黑树集合。用于存储不重复元素,并按照元素的自然顺序或自定义顺序进行排序。
HashMap:实现了Map接口的哈希表。用于存储键值对,提供快速的查找和插入操作。
TreeMap:实现了Map接口的红黑树。用于存储键值对,并按照键的自然顺序或自定义顺序进行排序。

其它语言的类似容器

1. Python:
   - 列表(List):类似Java中的ArrayList,可以动态增加或删除元素。
   - 字典(Dictionary):类似Java中的HashMap,使用键值对存储数据。
2. C++:
   - 向量(Vector):类似Java中的ArrayList,可以动态增加或删除元素。
   - 映射(Map):类似Java中的HashMap,使用键值对存储数据。
3. JavaScript:
   - 数组(Array):类似Java中的ArrayList,可以动态增加或删除元素。
   - 对象(Object):类似Java中的HashMap,使用键值对存储数据。

   

Array数组

初始化(动与静)

动态初始化

// 动态初始化:数组动态初始化就是只给定数组长度, 由系统给出默认初始化值
// 动态初始化格式:数组类型[] 数组名 = new 数组类型[数组长度]
// 示例: int[] arr = new int[3] // 数组类型为int类型, 数组名为arr, 数组长度为3

// 示例: 动态初始化一个长度为5的数组, 并循环打印内部的所有元素
public class DemoTh {
    public static void main(String[] args) {
        // 动态初始化一个长度为5的数组
       int[] arr = new int[5];
       System.out.println(arr);
    }
}

静态初始化

// 静态初始化: 数组静态初始化是指在创建数组时, 直接将元素确定.
// 静态初始化格式:
    // 完整版:数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, ....}
    // 简化版:数组类型[] 数组名 = {元素1, 元素2, ....}
    
// 示例:创建一个包含1, 3, 5, 7, 9的数组, 并遍历输出其每一个元素
public class DemoOneToNine {
    public static void main(String[] args) {
        // 静态初始化数组
        int[] arr = {1, 3, 5, 7, 9};
        System.out.println(arr);
    }
}

CRUD

增:看初始化

查:

1、索引取值

  • 索引即下标,代表元素在数组中的位置。Java数组的索引从0开始
public class DemoOneToNine {
    public static void main(String[] args) {
        // 静态初始化数组
        int[] arr = {1, 3, 5, 7, 9};
        System.out.println(arr[3]);  // 7
    }
}

2、遍历

// for循环示例: 按索引取出元素
public class DemoOneToNine {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        for (int i = 0;i<arr.length;i++) {
            System.out.println(arr[i]);
        };
    }
}
// for循环示例: 直接遍历出元素
public class DemoOneToNine {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        for(int item:arr) {
            System.out.println(item);
        }
    }
}

改:

public class Test01 {
    public static void main(String[] args) {
        // 静态初始化数组
        int[] arr = {1, 3, 5, 7, 9};
        arr[0] = 666;
        for(int item:arr) {
            System.out.println(item);
        }
    }
}

删:

走进底层

栈与堆

  • 栈:保存局部变量的值,包括:基本数据类型的值、类的实例(堆区对象的引用(指针)。
  • 堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。

一个数组的诞生

public class JavaMem {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        System.out.println(arr);
    }
}

多数组

arr2 = arr1

public class JavaMem {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3};
        int[] arr2 = arr1;
        System.out.println(arr1[0]);  // 1
        System.out.println(arr2[0]);  // 1
        arr1[0] = 66;
        System.out.println(arr1[0]);  // 66
        System.out.println(arr2[0]);  // 1
    }
}

arr2 = {arr1[0], arr1[1], arr1[2]}

public class JavaMem {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3};
        int[] arr2 = {arr1[0], arr1[1], arr1[2]};
        System.out.println(arr1[0]); // 1
        System.out.println(arr2[0]); // 1
        arr1[0] = 666;
        System.out.println(arr1[0]);  // 66
        System.out.println(arr2[0]);  // 66
    }
}

避坑指南

索引越界

        索引越界是指使用索引获取数组元素时, 使用的索引不能超过数组元素的最大索引值, 超过后无法通过改索引获取指定的元素, 就会出现索引越界错误, 异常名称位: ArrayIndexOutOfBoundsException

空指针异常

        当一个数组对象被创建了, 当使用的过程中如果让数组对象指向了null, 即将null赋值给数组, 意味着变量arr将不会再保存数组的内存地址,也就不允许再操作数组了,因此运行的时候会抛出 NullPointerException 空指针异常。

小试牛刀

找出数组的最大值

public class GetMax {
    public static void main(String[] args) {
        
        int[] my_arr = {1, 5, 2, 0, -9, 8, 4};
        
        int max = my_arr[0];
        for (int i = 1; i < my_arr.length; i++) {
            if (my_arr[i] > max) {
                max = my_arr[i];
            }
        }
        System.out.println(max);
    }
}

返回两个元素的和等于定值,的这两个元素的索引

public class FindTarget {
    public static void main(String[] args) {
        
        int target = 8;
        int[] arr = {1, 3, 5, 7, 9};
        
        for (int i = 0; i < arr.length; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] + arr[j] == target) {
                    System.out.println("索引分别为: " + i + "," + j);
                }
            }
        }
    }
}

   

Collection

List部落

  • List集合为有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素, 并搜索列表中的元素
  • 列表通常允许重复的元素
  • 特点:(1) 有索引 (2) 可以存储重复元素 (3) 元素存取有序

方法

方法名

描述

void add(int index,E element)

在此集合中的指定位置插入指定的元素

E remove(int index)

删除指定索引处的元素,返回被删除的元素

E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

E get(int index)

返回指定索引处的元素

ArrayList

        在java中ArrayList提供一种存储空间可变的存储模型, 存储的数据容量是可以发生改变的. ArrayList底层是「数组」实现的, 长度可以变化.

方法

方法名

说明

public boolean add(E e)

将指定的元素追加到此集合的末尾

public void add(int index,E element)

在此集合中的指定位置插入指定的元素

public boolean remove(Object o)

删除指定的元素,返回删除是否成功

public E remove(int index)

删除指定索引处的元素,返回被删除的元素

public E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

public E get(int index)

返回指定索引处的元素

public int size()

返回集合中的元素的个数

import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        //创建ArrayList集合对象, 添加四个元素, 并输出在控制台中
        ArrayList<String> al = new ArrayList<>();
        al.add("Hello");
        al.add("World");
        al.add("Hello");
        al.add("Java");
        System.out.println("al的值:" + al);

        //获取索引为1的元素, 输出在控制台中
        System.out.println("al索引为1的元素: " + al.get(1));
        //修改索引为3的元素为"Hi"
        al.set(3,"Hi");
        //查看修改后的al
        System.out.println("修改索引为3的元素后的al: " + al);
        //移除索引为0的元素后输出al
        al.remove(0);
        System.out.println("移除索引为0的元素后的al: " + al);
        //移除内容为"Java"的元素后输出al
        al.remove("Java");
        System.out.println("移除Java后的al: " + al);
    }
}
遍历
import java.util.ArrayList;

public class ArrayListIterDemo {
    public static void main(String[] args) {
        
        ArrayList<String> al = new ArrayList<>();
        al.add("I");
        al.add("love");
        al.add("you");
        
        //普通for遍历
        for (int i=0;i<al.size();i++){
            System.out.println(al.get(i));
        };
        // 增强for遍历
        for (String item : al) {
            System.out.println(item);
        }
    }
}

LinkedList

        LinkedList功能与ArrayList一样,同样是一个List的实现类,功能也是存储数据使用。但LinkedList与ArrayList本质完全不同。

  • ArrayList:底层有数组实现
  • LinkedList:底层有链表实现
方法(特有)

方法名

说明

public void addFirst(E e)

在该列表开头插入指定的元素

public void addLast(E e)

将指定的元素追加到此列表的末尾

public E getFirst()

返回此列表中的第一个元素

public E getLast()

返回此列表中的第一个元素

public E removeFirst()

从此列表中删除并返回第一个元素

public E removeLast()

从此列表中删除并返回最后一个元素

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<Integer> numbers_linklist = new LinkedList<>();
        numbers_linklist.add(1);
        numbers_linklist.add(2);
        numbers_linklist.add(3);
        numbers_linklist.add(4);
        numbers_linklist.add(5);

        // addFirst方法
        numbers_linklist.addFirst(0);
        System.out.println(numbers_linklist);

        // addLast方法
        numbers_linklist.addLast(6);
        System.out.println(numbers_linklist);

        // getFirst方法
        System.out.println(numbers_linklist.getFirst());

        // getLast方法
        System.out.println(numbers_linklist.getLast());

        // removeFirst方法
        numbers_linklist.removeFirst();
        System.out.println(numbers_linklist);

        // removeLast方法
        numbers_linklist.removeLast();
        System.out.println(numbers_linklist);

    }
}

Set部落

        Set为集合,不包含重复元素的集合, 并且最多只有一个空元素。

方法

方法名

说明

add(E e)

如果指定的元素不存在,则将其指定的元素添加(可选操作)。

clear()

从此集合中删除所有元素(可选操作)。

contains(Objcet o)

如果此集合包含指定的元素,则返回 true 。

equals(Object o)

将指定的对象与此集合进行比较以实现相等。

hashCode()

返回此集合的哈希码值。

isEmpty()

如果此集合不包含元素,则返回 true 。

remove(Object o)

如果存在,则从该集合中删除指定的元素(可选操作)。

size()

返回此集合中的元素数(其基数)。

toArray()

返回一个包含此集合中所有元素的数组。

HashSet

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历(可以使用增强for循环和迭代器实现遍历)
  • 由于是Set集合,所以是不包含重复元素的集合
唯一性

前置知识:HashTable: 哈希表;HashCode: 哈希值

  • a.根据对象的哈希值计算存储位置(哈希值对16取模即可得到存储位置, 一个HashSet默认存储16个元素)
    • 如果当前位置没有元素则直接存入
    • 如果当前位置有元素存在,则进入第二步
  • b.当前元素和已经存在的元素比较哈希值
    • 如果哈希值不同,则将当前元素进行存储
    • 如果哈希值相同,则进入第三
  • c.通过equals()方法比较两个元素的内容
    • 如果内容不相同,则将当前元素进行存储
    • 如果内容相同,则不存储当前元素

LinkedHashSet

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 由哈希表保证元素唯一,也就是说没有重复的元素

TreeSet

  • TreeSet元素排序方式取决于构造方法
    • Comparable(自然排序): 创建TreeSet对象时, 使用TreeSet()无参构造
    • Comparator(比较器): 创建TreeSet对象时, 使用TreeSet(Comparator comparator)有参构造方法, 传递的参数是Comparator接口的实现类对象, 则根据指定的比较器进行排序
  • TreeSet没有带索引的方法, 所以不能使用普通for循环遍历
  • 由于是Set集合, 所以不能存储重复的元素
public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();
        
        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);
        ts.add(30);
        
        //遍历集合
        for(Integer i : ts) {
                System.out.println(i);
        }
    }
}
自然排序

        自然排序是在添加对象到集合内部时, 让其按照对象内部的比较方法进行自动排序. 构建TreeSet时使用无参构造方法即可.

        上面的例子中, 我们构建ts对象, 并向其中添加一些整型, 遍历ts对象会发现, 打印的顺序是从小到达的. 经查看Integer类源码发现, Integer底层实现了两个方法, 一个compare方法和compareTo方法, 其中compare负责进行比较, 而compareTo内部调用compare方法, 当前数与另一个数进行比较时, 如果当前数小于那个数, 则返回-1, 如果相等则返回0, 如果当前数大于那个数则返回1. 如果是自定义类实例化而来的对象, 需要自己定义compareTo()方法, 在方法内部定义比较规则.

        需求:定义学生类, 学生类内部定义compareTo方法, 按照学生年龄排序. 将学生类添加至TreeSet对象中, 遍历集合对象

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

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

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(Student s) {
        return this.age - s.age;
    }
}
public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>();

        Student s1 = new Student("成成", 18);
        Student s2 = new Student("宝中", 17);
        Student s3 = new Student("金喜", 20);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);

        for (Student stu : ts) {
            System.out.println(stu.getName());
        }
    }
}
比较器排序

        使用比较器排序, 需要自己实现一个比较器, 并在初始化TreeSet时使用有参构造, 并把比较器传入进去, 对象加入TreeSet对象时, 就会按照比较器的规则进行排序了

        需求:定义学生类, 学生类内部定义compareTo方法, 按照学生年龄排序. 将学生类添加至TreeSet对象中, 遍历集合对象

public class Student {
    private String name;
    private int age;

    public Student(){};

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

    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 class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>(
                new Comparator<Student>() {
                    @Override
                    public int compare(Student s1, Student s2) {
                        return s1.getAge() - s2.getAge();
                    }
                }
        );

        Student s1 = new Student("小明", 18);
        Student s2 = new Student("小红", 17);
        Student s3 = new Student("小强", 20);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);

        for (Student stu : ts) {
            System.out.println(stu.getName());
        };
    }
}

   

Map

Map为一个接口, 将键映射到值的对象。 Map不能包含重复的键; 每个键可以映射到最多一个值。

interface Map<K,V> K:键的类型;V:值的类型

特点:

  • 键值对映射关系
  • 一个键对应一个值
  • 键不能重复, 值可以重复
  • 元素存取无序

基本使用:

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

public class MapDemo01 {
    public static void main(String[] args) {
        //创建HashMap对象
        Map<String, String> m = new HashMap<String, String>();
        //添加键值对
        m.put("name_1", "Jeremy");
        m.put("name_2", "Fish");
        //输出
        System.out.println(m);  //{name_2=Fish, name_1=Jeremy}
    }
}

方法的介绍

//增:
    - put(key, value): 添加键值对, 返回键对应的值

//删:
    - remove(key, value): 如果指定的键对应的值为value, 则进行删除
    - remove(key): 根据键删除键值对元素, 返回键对应的值
    - clear(): 清空所有键值对元素

//改:
    - put(key, value): 同则修改, 异则添加

//查:
    - containsKey(key): 判断是否存在某个特定的key, 返回值为布尔值
    - containsValue(Value): 判断是否包含指定的值, 返回值为布尔值
    - isEmpty(): 判断Map对象是否为空, 返回值为布尔值
    - size(): 获取Map对象键值对个数, 返回值为整型
    - get(key): 获取指定键的值
    - keySet(): 获取所有的键, 返回值为Set类型
    - Values(): 获取所有值的集合
    - enterSet(): 获取所有键值对对象的集合

示例

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

public class MapDemo01 {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<String, String>();
        m.put("name_1", "Jeremy");
        m.put("name_2", "Fish");
        System.out.print("put增加键值对后的m为: ");
        System.out.println(m);  //put增加键值对后的m为: {name_2=Fish, name_1=Jeremy}

        m.remove("name_1", "Jer");
        System.out.print("如果键name_1对应的值为Jer, 则移除: ");
        System.out.println(m);  //如果键name_1对应的值为Jer, 则移除: {name_2=Fish, name_1=Jeremy}

        //直接移除name_1的键值对
        m.remove("name_1");
        System.out.print("移除name_1的键值对后, m的值: ");
        System.out.println(m);  //移除name_1的键值对后, m的值: {name_2=Fish}

        //clear()后的m
        m.clear();
        System.out.print("clear()后m的值为: ");
        System.out.println(m);  //clear()后m的值为: {}

        //空对象m重新添加name_1键值对
        m.put("name_1", "Jeremy");
        m.put("name_2", "Lucy");
        System.out.print("重新添加name_1键值对的m为: ");
        System.out.println(m);  //重新添加name_1键值对的m为: {name_2=Lucy, name_1=Jeremy}
        //重复添加name_1的键, 但是改变对应的值
        m.put("name_1", "LiLi");
        System.out.print("使用name_1键的新值覆盖原有值后m的值: ");
        System.out.println(m);  //使用name_1键的新值覆盖原有值后m的值: {name_2=Lucy, name_1=LiLi}

        //判断是否包含指定的key
        System.out.println(m.containsKey("name_2"));  //true

        //判断是否包含指定的value
        System.out.println(m.containsValue("Lucy"));  //true

        //判断是否为空
        System.out.println(m.isEmpty());  //false

        //获取Map对象的长度
        System.out.println(m.size());  //2

        //获取指定键对应的值
        System.out.println(m.get("name_1"));  // LiLi

        //获取所有键的集合
        System.out.println(m.keySet());  //[name_2, name_1]

        //获取所有值的集合
        System.out.println(m.values());  //[Lucy, LiLi]

        //获取所有的键值对
        System.out.println(m.entrySet());  //[name_2=Lucy, name_1=LiLi]
    }
}

遍历方式

方式一

使用增强for, 遍历集合的所有键或值进行操作
import java.util.HashMap;
import java.util.Map;

public class MapDemo02 {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<String, String>();
        m.put("name_1", "金喜");
        m.put("name_2", "帅帅");
        m.put("name_3", "晨阳");

        for(String key: m.keySet()){
            System.out.println(key);
        }
        for (String value:m.values()){
            System.out.println(value);
        }
    }
}

方式二

通过增强for遍历键值对组成的EntrySet对象
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapDemo03 {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<String, String>();
        m.put("name_1", "Jeremy");
        m.put("name_2", "刘桂宁");
        m.put("name_3", "毕奎成");

        Set<Map.Entry<String, String>> items = m.entrySet();
        for(Map.Entry<String, String> item:items) {
            System.out.println("键值对为: " + item);
            System.out.println("键为: " + item.getKey());
            System.out.println("值为: " + item.getValue());
            System.out.println("-------------------------");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值