Java容器

容器简单来说,就像是我们生活中用来容纳东西的物体,他可以容纳和管理数据,数组也是一种容器,他的优势可以快速的访问元素,效率高,但是他也有他的不足,他有固定的长度,不灵活,不会随着需求的变化而进行扩容操作,不能满足管理和组织的需求,所以引进了一种可以扩容更加灵活的容器,也成为集合(Collection)

容器的结构

容器的整体结构
单例集合:将数据一个一个进行存储
单例集合的整体结构

下面就分析单例集合的具体实现

单例集合的整体框架分析

总的单例集合的接口Collection,他有两个子接口,分别是List接口和Set接口
Collection接口中定义的抽象方法
在这里插入图片描述

List接口分析

List接口具体一下几个特点:

  1. 有序:存入集合的顺序和取出集合的顺序一致
  2. 可重复:允许添加重复的元素

List接口中在Collection基础上定义的抽象方法(List多了一些跟顺序也就是索引有关的方法)
在这里插入图片描述

List接口的具体实现类

  1. ArrayList(ArrayList底层是用数组实现的存储,查询效率高,增删效率低,线程不安全)
    ArrayList类的使用
public class ArrayListTest {

    public static void main(String[] args) {
        //实例化一个容器
        //在实例化一个对象的时候,对象引用应该用其接口类型,
        //如果用接口定义对象的引用,之后发生改变对代码的改变较小
        //下面使用的show方法是自定义遍历集合的方法,在最下面
        List<String> list=new ArrayList<>();

        //添加元素
        //这个是Collection接口中定义的add方法,返回一个布尔类型
        boolean flag=list.add("bjsxt");
        boolean flag2=list.add("itbaizhan");
        list.add("java全系列");
        //这个是List接口中的add方法,在给定的索引位置插入指定的元素,索引的数值不可以超过现在集合中元素的个数
        list.add(2,"wujunhong");

        //获取元素  size() 方法,返回集合中元素的个数和get() 方法,获得集合中指定位置的元素的使用
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }

        //替换指定位置的元素,返回被替换掉的元素
        String str=list.set(0,"北京尚学堂");
        System.out.println(str);
        show(list,"替换指定位置的元素");

        //删除元素:根据索引删除元素,返回删除的元素,之后的元素都会向前移位
        list.remove(0);
        show(list,"根据索引删除元素");
        //删除指定元素,返回布尔类型的值,如果删除成功就返回true
        list.remove("java全系列");
        show(list,"删除指定元素");

        //清空集合中所有元素
        list.clear();
        show(list,"清空集合中所有元素");

        //判断该集合是否为空,如果为true,则是空集合
        System.out.println(list.isEmpty());
        list.add("武俊宏");
        list.add("苏青旭");
        list.add("朱九思");
        list.add("苏青旭");
        show(list,"添加了四个元素");
        //判断该集合中是否含有指定元素
        System.out.println(list.contains("武俊宏"));

        //查找元素在容器中的位置、、只可以找到第一次出现和最后一次出现的位置,中间的位置找不到
        //如果元素查找不到 则返回-1
        System.out.println(list.indexOf("苏青旭"));//该元素在容器中第一次出现的位置
        System.out.println(list.lastIndexOf("苏青旭"));//该元素在容器中最后一次出现的位置

        //将一个单例集合转化成数组
        System.out.println("----------将单例集合转换成数组-----------");
        Object[]arr=list.toArray();
        for (int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }

        System.out.println("----------将单例集合转换成指定泛型的数组---------");
        String[] str2=list.toArray(new String[list.size()]);
        for (int i=0;i<str2.length;i++){
            System.out.println(str2[i]);
        }

        System.out.println("-------集合的并集操作--------- ");
        List<String> a=new ArrayList<>();
        a.add("a");
        a.add("b");
        a.add("c");
        List<String> b=new ArrayList<>();
        b.add("b");
        b.add("c");
        b.add("d");
        //a并b 并好的元素在a中
        boolean flag7=a.addAll(b);
        show(a,"集合并集操作");
        //集合的交集操作
        List<String> a1=new ArrayList<>();
        a1.add("a");
        a1.add("b");
        a1.add("c");
        List<String> b1=new ArrayList<>();
        b1.add("b");
        b1.add("c");
        b1.add("d");
        boolean flag8=a1.retainAll(b);
        show(a1,"集合交集操作");

        a.removeAll(a1);
        show(a,"容器的差值操作");

    }
    public static void show(List list,String operation){
        System.out.println("----------"+operation+"------------");
        //获取元素  size() 方法,返回集合中元素的个数和get() 方法,获得集合中指定位置的元素的使用
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}

2、LinkedList容器(LinkedList容器底层采用的是双向链表的存储,他查询效率低,增删效率高,线程不安全)

public class LinkedListTest {

    public static void forEach(LinkedList<String> list){
        System.out.println("-----------------------");
        for (String str:list) {
            System.out.println(str);
        }
    }

    public static void main(String[] args) {
       //非List标准,如果使用List标准,对象的引用使用List就可以,方法使用List接口中定义的方法
        LinkedList<String> list=new LinkedList<>();
        list.addFirst("a");
        list.addFirst("b");
        list.addFirst("c");
        //返回列表中第一个元素
        System.out.println(list.getFirst());
        //返回此列表中最后一个元素
        System.out.println(list.getLast());
        forEach(list);
        LinkedList<String> list1=new LinkedList<>();
        list1.addLast("a");
        list1.addLast("b");
        list1.addLast("c");
        forEach(list1);
        //移除此列表中第一个元素
        System.out.println(list1.removeFirst());
        //移除此列表中最后一个元素
        System.out.println(list1.removeLast());
        //等效于removeFirst()
        System.out.println(list1.pop());
        //等效于addFirst
        list1.push("f");
        forEach(list1);
        //判断是否包含元素,返回一个Boolean类型的值
        System.out.println( list1.isEmpty());
    }
}

3、Vector容器
和ArrayList的区别和联系

  • Vector底层也是数组实现的,和ArrayList的所有操作都是一样的,但是Vector是单线程,串行的,加了同步检查,所以线程安全,效率低,而ArrayList是并行的,所以线程不安全
  • 操作和ArrayList都一样
  • ArrayList底层采用的是延迟初始化,在扩容的时候是1.5倍扩容
    Vector底层采用的是立即初始化,在扩容的是时候是2倍扩容

4、Stack容器
Stack栈容器,是Vector的一个子类,他实现了标准的先进后出原则
Stack容器的使用以及案例

public class StackTest {

    public static void main(String[] args) {
        //实例化一个栈对象,对象的引用只能用Stack 因为list接口中没有操作栈的方法
        Stack<String> s=new Stack<>();
        //在栈中添加元素
        s.push("a");
        s.push("b");
        s.push("c");
        //查看栈顶元素
        System.out.println(s.peek());
        //判断栈是否为空
        System.out.println(s.empty());
        //返回元素在栈当中的位置
        System.out.println(s.search("b"));
        //获取栈容器中的元素,从栈顶移除元素
        String str1=s.pop();
        String str2=s.pop();
        String str3=s.pop();
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
        //再次判断栈是否为空
        System.out.println(s.empty());
        System.out.println("---------------------");
        symmetry();

    }
    public static void symmetry(){
        //Stack使用案例,判断元素的对称性
        String str="...[...(...{......}...).....].....(....).....[...]..(.";
        //创建一个Stack实例
        Stack<String> stack=new Stack<>();
        //假设修正法
        boolean flag=true;
        //拆分字符串,获取字符
        for (int i=0;i<str.length();i++){
            char c=str.charAt(i);
            if (c=='{'){
                stack.push("}");
            }
            if (c=='['){
                stack.push("]");
            }
            if (c=='('){
                stack.push(")");
            }
            if (c=='}'||c==']'||c==')'){
                if (stack.empty()){
                    flag=false;
                    break;
                }
                String x=stack.pop();
                if(x.charAt(0)!=c){
                    flag=false;
                    break;
                }
            }
        }
        if (!stack.empty()){
            flag=false;
        }
        System.out.println(flag);
    }
}

Set接口分析

特点:无序、不可重复。无序是指Set中的元素没有索引,我们只可以遍历查找,不可重复是指,新元素如果和Set中某个元素通过equals()方法对比为true,只能保留一个。

Set接口具体实现类

  1. HashSet
    HashSet是一个没有重复元素的集合,不保证元素的顺序,HashSet允许有null元素,HashSet 是采用哈希算法实现的,底层是用HashMap实现的,下来介绍一下Hash原理:
    哈希算法也是散列算法
    存储的数据通过进行模9运算,然后存储到对应的位置,如果这个位置上有元素了,会调用对应的equals方法,判断两个元素是否相同,如果相同则不会添加,如果不相同,则会使用单向链表保存该元素。
    HashSet类的使用
public class HashSetTest {

    public static void main(String[] args) {
        //实例化Hashset对象
        HashSet<String> hashSet=new HashSet<>();
        //添加元素,不能保证存放元素的顺序,以及不可以有元素重复
        //线程不安全,可以有null
        //无序:对元素的哈希值进行运算,来决定元素到底存放在哪里
        hashSet.add("a1");
        hashSet.add("c2");
        hashSet.add("d3");
        hashSet.add("a");
        //删除元素
        hashSet.remove("a1");
        System.out.println(hashSet.size());

        //从Hashset中获取元素,在set容器中没有索引,没有对应的get方法
        for(String str:hashSet){
            System.out.println(str);
        }

        System.out.println("***********HashSet存储自定义对象,User类在下面*************");
        /**
         * 如果想要识别存储对象属性的比较,是他们有互异性
         * 需要在JavaBean中重写HashCode以及equals方法
         */
        Set<Users> u=new HashSet<>();
        Users u1=new Users("武俊宏",18);
        Users u2=new Users("武俊宏",18);
        System.out.println(u1.hashCode());
        System.out.println(u2.hashCode());
        u.add(u1);
        u.add(u2);
        for (Users users:u){
            System.out.println(users);
        }
        
    }
}

public class Users implements Comparable<Users>{
    private String userName;
    private int userAge;

    @Override
    public boolean equals(Object o) {
        System.out.println("程序执行到这里了");
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Users users = (Users) o;
        return userAge == users.userAge && Objects.equals(userName, users.userName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userName, userAge);
    }

    public Users() {
    }

    public Users(String userName, int userAge) {
        this.userName = userName;
        this.userAge = userAge;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userName='" + userName + '\'' +
                ", userAge=" + userAge +
                '}';
    }
}

  1. TreeSet(TreeSet是一个可以对元素进行排序的容器,底层实际是用TreeMap实现的,通过Key来存储Set元素,因为他要排序,所以我们要给定排序规则)
    排序规则1:通过元素自身实现比较规则,需要实现Comparable接口中的compareTo方法,该方法用来定义比较规则,TreeSet通过调用该方法来完成对元素的排序处理
public class Users implements Comparable<Users>{
    private String userName;
    private int userAge;

    @Override
    public boolean equals(Object o) {
        System.out.println("程序执行到这里了");
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Users users = (Users) o;
        return userAge == users.userAge && Objects.equals(userName, users.userName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userName, userAge);
    }

    public Users() {
    }

    public Users(String userName, int userAge) {
        this.userName = userName;
        this.userAge = userAge;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userName='" + userName + '\'' +
                ", userAge=" + userAge +
                '}';
    }
    //定义比较规则
    @Override
    public int compareTo(Users o) {
        if (this.userAge<o.getUserAge()){
            return 1;
        }
        if (this.userAge==o.getUserAge()){
            return this.userName.compareTo(o.userName);
        }
        return -1;
    }
}


public class TreeSetTest {

    public static void main(String[] args) {
        //实例化一个TreeSet对象
        Set<String> treeSet=new TreeSet<>();
        treeSet.add("c");
        treeSet.add("f");
        treeSet.add("d");
        treeSet.add("d");
        treeSet.add("a");
        for (String str: treeSet) {
            System.out.println(str);
        }
        System.out.println("******************");
        Set<Users> set=new TreeSet<>();
        Users u1=new Users("wujunhong",15);

        Users u5=new Users("oi",2);
        Users u6=new Users("pi",2);
        set.add(u1);
        set.add(u5);
        set.add(u6);
        for (Users users:set){
            System.out.println(users);
        }
    }
}

排序规则2:通过比较器实现比较排序规则,通过比较器定义比较规则时,我们需要单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则,在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处理,元素此时就不需要实现比较规则了

//定义一个Student类,方便后面的测试
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;
    }

    @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 && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

//定义比较器
public class StudentComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        if (o1.getAge()>o2.getAge()){
            return 1;
        }
        if (o1.getAge()==o2.getAge()){
            return o1.getName().compareTo(o2.getName());
        }
        return -1;
    }
}
public class TreeSetTest {
//必须把比较器对象交给创建好的对象
        Set<Student> s=new TreeSet<>(new StudentComparator());
        Student student=new Student("wu",123);
        Student student1=new Student("jun",15);
        Student student2=new Student("hong",14);
        Student student3=new Student("qong",14);
        s.add(student);
        s.add(student1);
        s.add(student2);
        s.add(student3);

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

双例集合:基于Key和Value的结构存储数据
双例集合的整体结构

下面就分析双例集合的具体实现

双例集合的整体框架分析(底层实现比较难描述,这里不具体展开描述)

总的双例集合的接口Map,整个框架中只有他一个接口,其他的都是接口的具体实现类
Map接口中定义的抽象方法
Map接口中定义的抽象方法

HashMap容器类

HashMap是Map接口的实现类,他采用哈希算法实现。由于底层采用哈希表存储数据,所以要求键不能重复,如果发生重复,新的值会替换旧的值。HashMap在查找、删除、修改方面都有非常高的效率,代码和之前的代码相似,但是具体如何遍历元素的三个方式是非常有意思的

public class HashMapTest {

    public static void main(String[] args) {
        //实例化一个hashMap容器
        Map<String,String> map=new HashMap<>();
        //添加元素,put方法,只有在容器中有重复的key时,他才会返回原来没有被覆盖的值
        map.put("a","A");
        String value=map.put("a","B");
        System.out.println(value);
        //获取元素,
        //方法一:元素多,需要调用很多次get方法;而且需要提前知道key的值
        System.out.println(map.get("a"));
        System.out.println("---------------");
        map.put("c","C");
        map.put("d","D");
        map.put("e","E");
        map.put("f","F");
        //方法二:获取HashMap容器中所有元素,可以使用keySet方法和get方法进行实现
        Set<String>keys=map.keySet();
        for (String str:keys){
            String values=map.get(str);
            System.out.println(str+"---"+values);
        }
        //方法三:通过entrySet方法获取Map.Entry类型(k-v映射出来的键值对)获取元素
        System.out.println("**************");
        Set<Map.Entry<String,String>> entrySet=map.entrySet();
        for (Map.Entry<String,String> entry:entrySet){
            String k=entry.getKey();
            String v=entry.getValue();
            System.out.println(k+"-----"+v);
        }
        System.out.println("-------HashMap的并集操作--------");
        Map<String,String> map1=new HashMap<>();
        map1.put("l","L");
        map1.put("c","ccc");    // 这个会覆盖掉
        map1.putAll(map);
        Set<String> sets=map1.keySet();
        for (String str:sets){
            String values=map1.get(str);
            System.out.println(str+"---"+values);
        }
        System.out.println("-----HashMap删除元素");
        String v=map.remove("e");
        System.out.println("删除的元素是:"+v);
        Set<String> dele=map.keySet();
        for (String str:dele){
            String values=map.get(str);
            System.out.println(str+"-----"+values);
        }
        System.out.println("------判断k和v是否存在");
        boolean flag=map.containsKey("aaa");
        boolean flag1=map.containsKey("a");
        boolean flag2=map.containsValue("B");
        boolean flag3=map.containsValue("BB");
    }
}

Tree Map容器类

TreeMap和HashMap同样实现了Map接口,HashMap效率高于Tree Map,TreeMap是可以对键进行排序的一种容器,在需要排序的时候可以使用Tree Map,他的底层是基于红黑树实现的。同样需要给定排序规则

  1. 元素自身实现比较规则

public class TreeMapTest {

    public static void main(String[] args) {
        System.out.println("*********通过自身比较规则实现排序***********");
        System.out.println("------存放普通字符类型对象--------");
        //实例化一个terrMap对象
        Map<String,String> treeMap=new TreeMap<>();
        //添加元素
        treeMap.put("a","A");
        treeMap.put("d","D");
        treeMap.put("f","F");
        treeMap.put("b","B");
        //对元素进行遍历
        Set<String> strings=treeMap.keySet();
        for (String str:strings){
            String k=str;
            String v=treeMap.get(k);
            System.out.println(k+"------"+v);
        }
        System.out.println("------存放users对象--------");
        Map<Users,String> map=new TreeMap<>();
        Users u1=new Users("武俊宏",19);
        Users u2=new Users("主机四",12);
        Users u3=new Users("苏青旭",14);
        Users u4=new Users("贾凯奇",33);
        map.put(u1,"武俊宏");
        map.put(u2,"主机四");
        map.put(u3,"苏青旭");
        map.put(u4,"贾凯奇");
        Set<Users>set=map.keySet();
        for (Users users:set){
            System.out.println(users+"---"+map.get(users));
        }
        
    }
}

  1. 通过比较器实现比较规则

public class TreeMapTest {

    public static void main(String[] args) {
//这里必须把比较器给了TreeMap,和TreeSet一样,因为Student没有实现Comparator接口
        Map<Student,String> stringMap=new TreeMap<>(new StudentComparator());
        Student student1=new Student("武俊宏",18);
        Student student2=new Student("主机四",14);
        Student student3=new Student("苏青旭",18);
        Student student4=new Student("贾凯奇",55);
        stringMap.put(student1,"武俊宏");
        stringMap.put(student2,"主机四");
        stringMap.put(student3,"苏青旭");
        stringMap.put(student4,"贾凯奇");
        Set<Student>students=stringMap.keySet();
        for (Student student:students){
            System.out.println(student+"----------"+stringMap.get(student));
        }
    }
}

到这里差不多就可以结束了,还有迭代器和Collections工具类的使用在这里就不一一描述了,容器这里的底层还是很有意思的,多看几遍可以对容器有更深的认识!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值