Java集合

1.java集合的概述

1.1 前言概述

1.这是我第二次看文档看视频带着分析去学习的记录,主要针对的不是入门,所以方法就不具体介绍了,但是会有演示代码,当然也有注解。只是都是初学时记录,希望大家可以指正。

2.主要记录的是一些原理,可能不够具体,所以也希望各位谏言之。

1.2 集合简要概述

(1)集合主要有两组(单列集合和双列集合)
(2)集合实际上就是一个容器,也是一个载体,可以容纳其他的类型数据
(3)集合中不能存基本数据类型,也不能存对象,它存的是对象的地址
(4)集合在java中本身是一个容器,是一个对象,是一个引用
(5)下面两张图看看继承实现情况

PS:Collection集合在这里插入图片描述
PS:Map集合
在这里插入图片描述

1.3集合和数组的区别

(1)首先是长度区别:数组长度大小固定,集合可变
(2)存储的类型:数组可以存基本类型,也能存引用类型。而集合只能存引用类型
(3)存储的元素内容:数组只能存储同一种类型,也就是一旦创建初始化,那就只能往里面添加定义好的类型。而集合可以存储不同的类型

PS:其实本质上来说,集合存储的也是同一种类型。因为它存的是对象的地址。

2.Collection集合的方法

2.1 迭代器方法

(1)别的方法我就不一一介绍了,主要是理解一些它的注意点
(2)当我们使用iterator迭代器遍历数据的时候,有两点需要注意:

1.在遍历之前需要先判断还有没有下一个元素即:iterator.hasNext
2.在遍历完之后,如果还想再遍历一次,要记得重置即:再获取一遍迭代器即可:iterator = collection.iterator();

PS:看图深入理解一下
在这里插入图片描述

PS:先科普一点,增强for循环底层原理其实就是迭代器。

3.List集合

3.1 List集合的简述

(1)先了解一点,因为List集合继承了Collection集合,所以Collection集合有的方法List也有。比如迭代器方法
(2)List集合是一个有序集合(即先进先出),可重复的。
(3)list集合方法代码演示

package com.shallow.collection.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@SuppressWarnings({"all"})  
public class List集合方法演示 {
    public static void main(String[] args) {
        // 因为List类是一个接口类,所以依赖于它的子类创建对象
        List list = new ArrayList(); // 创建list集合
        List list1 = new ArrayList(); // 创建list1集合

        // 1.添加元素
        list.add("aaa");
        list.add(41);  // 突出与数组的区别
        list.add("123");
        System.out.println(list); // 打印:[aaa, 41, 123]

        // 2.在指定位置插入元素
        list.add(1, 16); // [aaa, 16, 41, 123]
        System.out.println(list);

        // 3.将list1集合的元素添加至list集合
        list1.add("李白"); // 先给list1集合添加元素
        list1.add("杜甫");
        list.addAll(list1);
        System.out.println(list); // [aaa, 16, 41, 123, 李白, 杜甫]

        // 4. 在list集合指定位置插入list1集合全部的元素
        list.addAll(2, list1);
        System.out.println(list); // [aaa, 16, 李白, 杜甫, 41, 123, 李白, 杜甫]

        // 5. 返回list集合指定索引位置的元素
        Object o = list.get(3);
        System.out.println(o);  //杜甫

        // 6. 返回list集合中第一次出现“李白”对象的索引位置
        int i = list.indexOf("李清照");
        System.out.println(i);  // 2 补充,如果没有该元素,则返回 -1

        // 7. 删除指定索引的对象
        list.remove(2);
        System.out.println(list); // [aaa, 16, 杜甫, 41, 123, 李白, 杜甫]

        // 8. 更改指定索引位置的元素
        list.set(0, "李白");
        System.out.println(list); // [李白, 16, 杜甫, 41, 123, 李白, 杜甫]

        // 9. 返回指定开始到结束的所有元素
        List list2 = list.subList(1, 5);
        System.out.println(list2);  // [16, 杜甫, 41, 123]

        // 10. 迭代器遍历
        Iterator iterator = list.iterator();  // 获取 Iterator对象
        while (iterator.hasNext()) {  // 判断是否还有下一个元素
            Object next = iterator.next(); // 如果有指针移动并返回数据
            System.out.println(next);

        }

        // 11. 增强for循环遍历
        for (Object o1 : list) {
            System.out.println(o1);

        }

        // 12.普通for循环遍历
        for (int j = 0; j < list.size(); j++) {
            System.out.println(list.get(j));

        }

    }
}

3.2 List的实现子类

3.2.1 ArrayList集合

1)底层采用数组这种数据结构:ArrayList中维护了一个Object类型的数组elementData

2)当创建ArrayList对象时,如果采用的是无参构造器,则初始化elementData容量为0,第1次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍.

3)如果使用的是指定大小的构造器,则elementData容量为指定的大小,如需再次扩容,则扩容elementData为1.5倍.

4)增删效率低,改查效率高

5)ArrayList集合是非线程安全的

6)补充知识点:transient关键字修饰的属性或者成员被它修饰了,表示该属性或者成员不会被序列化

3.2.2 LinkedList集合

1)底层采用双向链表数据结构:它维护了两个属性,first(首节点)和last(尾节点),每个节点(Node对象)里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表

2)LinkedList元素的添加和删除,不是通过数组完成的,相对来说效率较高

3)因为它实现了List接口,所以它的元素也可以重复,包括null值

4)增删效率高,改查效率低

5)线程不安全

3.2.3 Vector集合

1)底层采用数组这种数据结构:ArrayList中维护了一个Object类型的数组elementData

2)增删效率低,改查效率高

3)Vector是线程安全的

4)当创建Vector对象时,如果采用的是无参构造器,底层调用会默认给个初始值10,如需再次扩容,则扩容elementData为2倍.

5)如果使用的是指定大小的构造器,则elementData容量为指定的大小,如需再次扩容,则扩容elementData为2倍.

4. Set集合

4.1 Set集合的简述

(1)无序:即添加和取出的顺序不一样,没有索引

(2)不允许重复值,所以最多也只能包含一个null值

(3)Set因为没有索引,所以它遍历不能使用普通for循环

(4)虽然它是无序的,但是一旦你取出一次的顺序出来了,然后你怎么取都是这个顺序

4.2 Set集合的实现子类

4.2.1HashSet集合

1)HashSet底层其实实际上是一个HashMap

2)HashSet可以放null,但是不能放多个null,即不可能重复

3)HashSet不保证元素是有序的,取决于Hash后,再确认索引结果

4)添加元素时,先得到hash值,会转成索引值,找到存储数据表table,看这个索引位置是否有元素,如果没有,则直接加入。如果有元素,即调用equals比较(记得要重写equals),如果相同,就放弃添加,如果不相同,就将其放到最后。如果添加时发现容量不够,开始扩容

5)查询元素时(map.get(key)):先调用k的hashCode()方法得出哈希值,通过哈希算法转化成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么都没有,返回null,如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,如果所有equals都返回false,那么get方法返回null,只要其中有一个节点的k和参数kequals返回true,那么此时这个节点的value就是我们要找的value,get方法最终返回这个要的value

6)HashSet的底层是HashMap,所以在第一次添加的时候,table数组扩容到16,临界值时160.75(即12)。如果table数组使用到了临界值12,就会扩容到162(即32),然后新的临界值就是32*0.75(即24).依此类推。

7)在JDK8中,如果一条链表的元素个数到达(默认8),并且table的大小大于等于(默认64),就会进化成红黑树

4.2.2 TreeSet集合

1)TreeSet跟TreeMap是一样的

2)可以按照元素的大小自定排序称为可排序集合

3)TreeSet去重机制:如果你传入的一个Comparator匿名对象,就使用实现的comparator去重,如果方法返回0,就认为是相同的元素,就不添加,如果你没有传入一个Comparator匿名对象,则以你添加的对象实现的Comparable接口里的compareTo方法去重

PS:看代码理解第三个说明

package com.shallow.collection.set;

import sun.reflect.generics.tree.Tree;

import java.util.Comparator;
import java.util.Objects;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class Tree_Set {
    public static void main(String[] args) {
        // 默认无参构造器 默认升序排序
//        TreeSet treeSet = new TreeSet();
//        treeSet.add("a");
//        treeSet.add("abc");
//        treeSet.add("shallow");
//        treeSet.add("zte");
//        treeSet.add("tom");
//        treeSet.add("jack");
//        treeSet.add("smith");
//        System.out.println(treeSet);
        // 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
        // 1.使用匿名内部类定义
//        TreeSet<User> treeSet = new TreeSet<User>(new Comparator<User>() {
//            @Override
//            public int compare(User user1, User user2) {
//                // 按分数(score)排序
                return user1.getScore() - user2.getScore();
//                // 按年龄排序
                return user1.getAge() - user2.getAge();
//                // 按年龄排序
                return user1.getName().compareTo(user2.getName());
//                // 按名字长度排序
//                return user1.getName().length() - user2.getName().length();
//            }
//        });
//
//        User user01 = new User("libai", 18, 98);
//        User user02 = new User("liqingzhao", 16, 96);
//        User user03 = new User("dufu", 20, 92);
//        // 当按名字长度排序的时候,长度一样即为相同,即不会添加进入TreeSet集合
//        // 别的元素相同也不会加入,同理
//        User user04 = new User("dufu", 18, 98); // 不会加入
//        treeSet.add(user01);
//        treeSet.add(user02);
//        treeSet.add(user03);
//        treeSet.add(user04);
//        System.out.println(treeSet);

        // 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
        // 2.定义的类先实现Comparable<T>接口,然后直接在定义的类里重写比较方法
//        User user01 = new User("libai", 18, 98);
//        User user02 = new User("liqingzhao", 16, 96);
//        User user03 = new User("dufu", 20, 92);
//        // 分数相同,不会加入,所以只有三条数据
//        User user04 = new User("dufu", 18, 98);
//        TreeSet<User> treeSet = new TreeSet<>();
//        treeSet.add(user01);
//        treeSet.add(user02);
//        treeSet.add(user03);
//        treeSet.add(user04);
//        System.out.println(treeSet);
        // 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
        // 3.TreeSet构造方法中传入比较器接口实现类的对象,使用Lambda表达式
//        User user01 = new User("libai", 18, 98);
//        User user02 = new User("liqingzhao", 16, 96);
//        User user03 = new User("dufu", 20, 92);
//        // 分数相同,不会加入,所以只有三条数据
//        User user04 = new User("dufu", 18, 98);
//
//        // 按成绩进行排序
//        TreeSet<User> treeSet = new TreeSet<>((user1, user2) ->
//                (user1.getScore() - user2.getScore()));
//        treeSet.add(user01);
//        treeSet.add(user02);
//        treeSet.add(user03);
//        treeSet.add(user04);
//        System.out.println(treeSet);

        // 当使用对象的时候,排序方式需要自己定义,不然报错。自己定义的方式有多种
        // 4.方法引用法 即在当前类写一个排序的方法
        User user01 = new User("libai", 18, 98);
        User user02 = new User("liqingzhao", 16, 96);
        User user03 = new User("dufu", 20, 92);
        // 分数相同,不会加入,所以只有三条数据
        User user04 = new User("dufu", 18, 98);
        TreeSet<User> treeSet = new TreeSet<>(Tree_Set::compare);
        treeSet.add(user01);
        treeSet.add(user02);
        treeSet.add(user03);
        treeSet.add(user04);
        System.out.println(treeSet);

    }

    // 排序的方法(静态)方便调用
    public static int compare(User user1, User user2) {
        return user1.getScore() - user2.getScore();

    }
}

class User implements Comparable<User>{
    private String name;
    private Integer age;
    private Integer score;

    public User() {
    }

    public User(String name, Integer age, Integer score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) &&
                Objects.equals(age, user.age) &&
                Objects.equals(score, user.score);
    }

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

    // 重写比较方法
    @Override
    public int compareTo(User o) {
        // 按分数进行升序排序
        return this.score - o.score;

    }

}

4.2.3 LinkedHashSet集合

1)它其实是HashSet集合的子类

2)它的底层是一个LinkedHashMap,底层维护了一个数组 + 双向链表,每一个节点都有before和after属性,这样可以形成双向链表,再添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表,如果已经存在,则不添加。其实原则还是和hashSet一样。

3)LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的

4)LinkedHashSet不允许添加重复元素

5.Map集合

5.1 Map集合的简述

(1)Map和collection并列存在,用于保存具有映射关系的数据:key-value(双列元素)

(2)Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中

(3)Map中的key不可重复,原因和HashSet一样

(4)Map中的value可以重复

(5)Map中的key和value可以为null,但是key只允许存在一个null,value可以有多个null。

(6)常用String类型数据作为map的key

(7)Key和value之间存在单向一对一关系,即通过key找到对应的value

(8)一对key-value是放在一个HashMap$Node中,又因为Node实现了Entry接口,也可以叫key-value为Entry

(9)一对key-value是放在一个HashMap$Node中,但是为了方便管理,它会将每一个HashMap$NodeNode封装成Entry,然后再把这个Entry放到一个entrySet这个集合中。

(10)当我们想单独的存放key取key的话,我们可以将这个由HashMap$Node封装成的Entry放到Set集合中(存放的方法就是调用Map里的keySet方法),想单独取value的话,同理,只是将Entry放到一个Collection集合中(存放的方法就是调用Map里的values方法)。最后遍历获取即可。
PS:这些方法只是做一个指向,并不是创建一个全新的数据。

5.2 Map集合的实现子类

5.2.1 HashMap集合

1)Hash Map是Map接口使用频率最高的实现类

2)HashMap是以key-value键值对的方式来存储数据的

3)Key不能重复,但是值可以重复,允许使用null,但是键的null值只能存在一次

4)如果添加相同的key,则会覆盖原有的值

5)与HashSet一样,不能保证映射的顺序,因为底层是以hash表的方式存储的

6)HashMap没有实现线程同步,因此是线程不安全的

7)它的扩容机制和hashSet相同

5.2.2 HashTable集合

1)跟HashMap大致一样,但是它是线程安全的

2)最大的区别在于它的键和值都不能为null

3)初始化大小为11,到临界值 11 * 0.75即8,然后扩容,扩容机制为原先11 * 2 + 1

5.2.3 Properties集合

1)Properties继承自HashTable类并实现Map接口,也是以键值对形式保存数据

2)使用特点和HashTable类似

3)Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改

6.工具类Collections

6.1 Collections简述

(1)Collections是操作Set,List,Map等集合的工具类

(2)Collections中提供了一系列静态的方法对集合元素进行排序,查找和修改等操作(排序操作均为static方法)

(3)代码演示

package com.shallow.collections;

import java.util.ArrayList;
import java.util.Comparator;


@SuppressWarnings({"all"})
public class Collections {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("libai");
        arrayList.add("liqingzhao");
        arrayList.add("dufu");
        arrayList.add("smith");
        arrayList.add("libai");

        // 使用Collections工具类操作
        // 1.反转list中的元素
//        java.util.Collections.reverse(arrayList);
//        System.out.println(arrayList);
        for (int i = 0; i < 5; i++) {
            // 2.对list集合随机排序
            java.util.Collections.shuffle(arrayList);
            System.out.println(arrayList);

        }
        // 3.根据元素的自然顺序对指定list集合元素按升序排序
        java.util.Collections.sort(arrayList);
        System.out.println(arrayList);

        // 4.将指定list集合中的元素进行交换
        java.util.Collections.swap(arrayList,0 , 2);
        System.out.println(arrayList);

        // 5.根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println(java.util.Collections.max(arrayList));
        // 6.根据Comparator指定的顺序,返回给定集合中的最大元素
        Object max = java.util.Collections.max(arrayList, new Comparator() {

            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length() - ((String) o2).length();
            }
        });

        System.out.println(max);
        // 7.返回指定集合中指定元素的出现次数
        System.out.println("libai出现的次数:" +
                java.util.Collections.frequency(arrayList, "libai"));

        // 8.将arrayList的内容复制到list中
        /**
         * 这里需要注意的点就是,我们先要给新建的list集合给定跟要复制的arrayList集合
         * 大小一样的空间
         */
        ArrayList list = new ArrayList();
        for (int i = 0; i < arrayList.size(); i++) {
            list.add("");

        }
        // 再进行拷贝
        java.util.Collections.copy(list, arrayList);
        System.out.println(list);

        // 9.使用新值替换list对象的所有旧值

    }
}

7.总结

7.1 java集合的总结

引入话题:在开发中选择什么集合实现类:主要取决于业务操作特点。然后根据集合实现类特性进行选择

1.先判断存储的类型:即单列集合还是双列集合(键值对形式的集合)

2.单列集合:Collection集合接口

2.1 允许重复:List集合
	(1)增删多:LinkedList【底层是双向链表】
	(2)改查多:ArrayList【底层是数组】
2.2 不允许重复:Set
	(1)无序:HashSet【底层是HashMap】
	(2)排序:TreeSet
	(3)有序:LinkedHashSet【底层是数组 + 双向链表】

3.双列集合:Map

3.1 键无序:HashMap【底层是哈希表 JDK8:数组 + 链表 + 红黑树】
3.2 键排序:TreeMap
3.3 有序:LinkedHashMap
3.4 读取文件:Properties
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值