JavaSE - 集合(笔记) - 集合总结

集合的关系

集合结构

  • 单列集合 Collection
    • List 可以重复:ArrayList、LindkedList
    • Set 不可以重复:HashSet、TreeSet
  • 双列集合 Map : HashMap、TreeMap

 接口粗体实现类斜体

集合图解:

集合关系图1

以下图自网络(见水印)

集合关系图2

集合图解3集合图解4

Collection 接口

-Collection接口中的方法

方法名说明
int size()返回集合中元素的个数
boolean isEmpty()判断集合是否为空
boolean add(Object o)添加元素
boolean addAll(Collection c)添加集合中的元素
boolean remove(Object o)删除元素
boolean removeAll(Object o)删除集合中的元素
boolean retainAll(Collection o)删除集合中不在参数集合中的元素
boolean contains(Object o)判断集合中是否包含某个元素
Iterator iterator()获得迭代器,用于遍历所有元素。
Object[] toArray()把容器中元素转化成Object数组

List接口

特点:

  • 有存放顺序 ( 有序 )

  • 元素可重复

方法名说明
void add(int index ,Object obj)在指定位置插入元素
boolean addAll(int index,Collection c)在指定位置插入集合
Object set(int index,Object obj)修改指定位置的元素
Object get(int index)获取指定位置的元素
boolean remove(int index)删除指定位置的元素,后面的元素前移
int indexOf(Object obj)返回第一个匹配的元素的索引,没有匹配的元素返回-1
int lastIndexOf(Object obj)返回最后一个匹配的元素的索引,没有匹配的元素返回-1
List subList(int fromIndex,int toIndex)取出集合中的子集合
ListIterator listIterator()为ListIterator接口实例化

1. ArrayList集合

-数组结构 -改查快 增删慢

特点:

  • 底层动态数组 查询快 增删满

  • 线程不安全 效率高

1.8之前创建时,会初始容量为10容量的数组,扩容为1.5倍

1.8之后是初始容量为0,存储第一个数据时,会扩容到10,存满时扩容为1.5倍

底层是数组内存地址是连续的,所以查询快 增删慢 线程不安全

方法名说明
boolean add(E e)将指定元素追加到集合的末尾
boolean addAll(E Object)用于将指定集合中所有元素添加到当前集合中
boolean remove(Object o)删除指定元素,成功则返回true
E remove(int index)删除指定索引位置的元素,返回被删除的元素
E set(int index,E e)修改指定索引位置的元素,返回修改前的元素
E get(int index)获取指定索引位置的元素
int size()返回及合中元素个数

2. LinkedList集合中的方法

-双链表结构 增删块 改查慢

特点:

  • 底层是动态数组 查询快 增删慢

  • 线程不安全 效率高

底层是一个双向链表, 线程不安全

Vector 线程安全的,底层也是数组

方法名说明
void addFirst(Object o)在链表头部添加元素
void addLast(Object o)在链表尾部添加元素
offerFirst(Object o)在链表头部添加元素,返回true或false
offerLast(Object o)在链表尾部添加元素,返回true或false
E getFirst()获取链表头部元素,空则报异常
E getLast()获取链表尾部元素,空则报异常
peekFirst()获取链表头部元素,空则返回null
peekLast()获取链表尾部元素,空则返回null
pollFirst()获取链表头部元素,空则返回null
pollLast()获取链表尾部元素,空则返回null
E removeFirst()移除链表头部元素,空则报异常
E removeLast()移除链表尾部元素,空则报异常
pop()弹出链表头部元素,空则报异常
push(Object o)压入链表头部元素
removeFirstOccurrence(Object o)移除链表中第一次出现的指定元素,返回布尔值
removeLastOccurrence(Object o)移除链表中最后一次出现的指定元素,返回布尔值

3.Vector

特点:

  • 底层是数组 查询快 增删慢

  • 线程安全 效率低

方法和ArrayList几乎一模一样

2-Set

特点

  • 没有存放顺序

  • 不可重复

  • 没有索引的,所以就不能使用普通for循环进行遍历

1.HashSet

特点:

  • 底层是哈希表 ( 底层hashMap )

    • 哈希表唯一依赖两个方法

      • hashCode()

      • equals()

  • 向HashSet集合放入新元素执行顺序:

    • 调用Hashcode()函数,判断是否已有相同的哈希值

    • 没有则放入该新元素,有则调用equals()函数逐个对比 ,没有则放入,有则不执行添加

练习:双色球

/**
 * 双色球 - Set版
 */
public class Demo03 {
    public static void main(String[] args) {
        Random rd = new Random();
        int blueBall = rd.nextInt(16)+1;

        //创建HashSet集合用来存储红球
        HashSet<Integer> redBalls = new HashSet<>();
        //TreeSet集合自带排序功能
        //TreeSet<Integer> redBalls = new TreeSet<>();
        while (redBalls.size() < 6){
            //将随机数添加到集合中
            redBalls.add(rd.nextInt(10));
        }
        System.out.println("红球:" + redBalls + "篮球:[" + blueBall+"]");

    }
}

HashSet去重原理

先看代码:

//学生类
public class Student {
    private String name ;
    private int age ;
    
    //省略 setter/getter 无参/有参构造 toStriing 方法

    @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);
    }
}

/**
* HashSet去重原理
*/
public class HashSetTest {
    public static void main(String[] args) {
        HashSet<Student> set = new HashSet<Student>();
        set.add(new Student("111",11));
        set.add(new Student("222",22));
        set.add(new Student("333",55));
        set.add(new Student("444",44));
        set.add(new Student("333",55));
        //从程序底层来讲,两个333不是同一个对象,都有自己的存储对象,所以可以存储。
        
        for (Student stu :
                set) {
            System.out.println(stu);
        }

    }
}

先对比hashCode如果相同,再去对比equals的内容。这两个方法经常需要在实体类中进行重写,帮助我们解决逻辑上的去重关系。

-LinkedHashSet

特点:

  • 底层哈希表 (不可重复 ) + 链表 (有序)

    • 有序指的是存入集合的顺序

  • 用法和HashSet几乎一模一样

2-TreeSet

特点:

  • 底层红黑树 自然平衡二叉树 升序排列

    • 因为二叉树结构所以集合内元素自动按升序排列

  • 元素不可重复

    • TreeSet是对Set集合的实现,而Set集合都是不可重复的

    • TreeSet自动排序,添加元素会调用compareTo(Object o)方法,遇到重复不添加

  • 两种排序方法

    • 自然排序 ( 默认升序 )

    • 定制排序 ( 需重写方法 )

3-Queue

特点:

  • 是一个队列 先进先出

Map接口

1-Map

特点:

  • 双列集合 键值对组成 ( 键 + 值 成对储存 ),键完全和List集合的特点相同(底层一样),值依附于每一个键上,不守规则影响(如TreeMap的键不可重复,但值可重复)

方法名说明
Object put(Object key, Object value)存放键值对
get(Object key)通过键对象获取到值对象
Object remove(Object key)通过键对象删除键值对
boolean containsKey(Object key)判断是否包含键对象对应的键值对
boolean containsValue(Object value)判断是否包含值对象对应的键值对
values()获取所有的值对象
int size()获取键值对的个数
boolean isEmpty()判断是否为空
void clear()清空所有的键值对

2-HashMap

-查找 删除 修改 效率很高

继承 AbstractMap< K , V >

实现 Map< K , V >

特点:

  • JDK1.8之前 底层是哈希表 (哈希表的本质是 数组 + 链表 )

    • 数组初始化长度16 ,负载因子是0.75 ,当储存的元素 * 0.75 大于数组的长度,需要将数组扩容2倍.

    • 其储存数据时判断key是否重复的标准是key的hashCode必须相同,equals方法比较时必须返回true

  • JDK1.8之后 底层是 ( 数组+链表+红黑二叉树 )

    • 当一个链表存储的数据大于8时,链表会自动转换为红黑二叉树,当数据少于6时,再会自动转化为链表

注意:

  • 底层会调用equals和hasnCode方法比较是否相等

  • 所以一般创建对象要重写 equals 和 hashCode 方法

方法名说明
V put(K key,V value)往集合中推送一对映射关系,若键存在,则把新的值赋值,并返回老的值
V remove(K key)根据键删除一对隐射关系,返回键对应的值
boolean remove(Object key, Object value)根据传入的键和值删除元素,并返回删除是否成功
void clear()清空集合中所有的隐射关系
V get(Object key)根据传入的键获取值
boolean containsKey(Object key)判断传入的键是否存在
boolean containsValue(Object value)判断传入的值是否存在
boolean isEmpty()判断集合是否为空
Set<K> keySet()获取集合中所有的键
Collection<V> values()获取集合中所有的值
int size()获取集合中有多少对隐射关系

-LinkedHashMap

继承 HashMap<K,V> 实现 Map<K,V>

特点:

  • 底层是哈希表 (哈希表的本质是 数组 + 链表 ) 和链表

    • 链表可以维护 插入节点 的顺序

  • 存于取的顺序一致

HashMap<K,V> : 元素存取无序的双列集合
LinkedHashMap<K,V> : 元素存取有序的双列集合
  
创建对象和增删改查都一模一样  

3-Hashtable

特点:

  • 底层是哈希表(哈希表本质是 数组 + 链表 ) 类似HashMap

  • 与HashMap唯一区别 线程安全 效率低

    • Hashtable 线程安全 效率低 不允许null键null值

    • HashMap 线程不安全,效率高,允许null值null键

用法与HashMap几乎一样

4-TreeMap

特点:

  • 底层是红黑树 自然平衡二叉树

    • 键的储存类似TreeSet 每个键都附带了其各自的值

  • 自动排序

注意:

  • TreeMap在比较key时,使用的时自己实现Comparable 接口的compareTo方法

    • 这个方法就是判断key是否时一样的方法

  • 返回0说明两者相等 ( 一般不会出现这种业务逻辑 )

    • 在某些业务情况下,不能随意返回0,需要使用equals和hashCode方法进行比较后在判断

方法参考Map接口方法

比较器

1-内部比较器

源码:

public interface Comparable<T> {
    /**
     * 比较this和obj的大小。
     *    如果this大于obj,则返回正数
     *    如果this小于obj,则返回负数
     *    吐过this等于obj,则返回零即可
     */
    public int compareTo(T obj);
}

当compareTo方法返回值为负数,表示放在红黑树的左边,即逆序(降序)输出。

当compareTo方法返回值为零,表示元素相同,仅存放第一个元素(保证元素的唯一)。

当compareTo方法返回值为正数,表示放在红黑树的右边,即顺序(升序)输出。

对于Java提供的类(String Integer等) 都默认实现了 Comparable 接口

- 自定义排序

自定义类中必须继承 Comparable 接口,并重写compareTo方法

示例:声明自定义Student类,按照老师的年龄升序排序

public class Student implements Comparable<Student> {
    // 成员变量
    String name;
    int age;
    double score;
    // 构造方法
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    /**
     * 比较this.age和stu.age的大小,按照学生年龄升序排序
     *    如果this大于stu,则返回正数
     *    如果this小于stu,则返回负数
     *    如果this等于stu,则返回零
     */
    @Override
    public int compareTo(Student stu) {
        return this.age - stu.age;
    }

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

因为TreeMap要对元素进行排序,那你排序的依据是什么,姓名还是年龄还是其它的,得告诉它,怎么告诉?需要让Student类实现Comparable接口并重写compareTo()方法。

  • 当compareTo方法返回值为负数,表示放在红黑树的左边,即逆序(降序)输出。

  • v 当compareTo方法返回值为零,表示元素相同,仅存放第一个元素(保证元素的唯一)。

  • 当compareTo方法返回值为正数,表示放在红黑树的右边,即顺序(升序)输出。

2-外部比较器

如果某个类想实现多种排序规则,那么就必须采用外部比较器java.util.Comparator接口来实现。

要想改变TreeMap的自然排序操作,我们还可以在其构造方法中传入一个Comparator比较器,那么TreeMap就会按照外部比较器中的规则进行排序,TreeMap的比较器构造方法如下:

public TreeMap(Comparator<? super K> comparator){
    this.comparator = comparator ;
}

因此,我们使用外部比较器实现TreeMap的排序时,必须创建一个Comparator的实现类对象,在实现类中重写int compare(T o1, T o2)方法,返回的int值的正负表示两个对象的大小,其返回值规则和Comparable接口中的compareTo()方法保持一致。

示例:声明自定义Student类

public class Student {
    // 成员变量
    String name;
    int age;
    double score;
    // 构造方法
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{name=" + name + ", age=" + age + ", score=" + score + "}";
    }
}

接下来,我们使用Comparator接口,先实现按照学生的年龄升序排序,然后在按照学生的成绩降序排序,具体的代码实现请看以下示例代码。

示例:使用Comparator比较器,实现按照学生的年龄升序排序

public class Test01 {
    public static void main(String[] args) {
        // 创建一个TreeMap对象
        Map<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            /**
             * 比较o1.age和o2.age对象的大小,按照学生年龄升序排序
             *   如果o1大于o2,则返回正数
             *   如果o1小于o2,则返回负数
             *   如果o1等于o2,则返回零即可
             */
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;
            }
        });
        // 添加键值对
        map.put(new Student("张三", 38, 99.5), "武汉");
        map.put(new Student("李四", 20, 58), "北京");
        map.put(new Student("王五", 27, 77), "上海");
        map.put(new Student("赵六", 18, 100), "南京");
        map.put(new Student("王五", 27, 89), "重庆");
        map.put(new Student("刘七", 29, 90), "深圳");
        // 遍历map
        Iterator<Student> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            Student key = iterator.next();
            System.out.println("key:" + key + ", value:" + map.get(key));
        }
    }
}

示例:使用Comparator比较器,实现按照学生的成绩降序排序

public class Test01 {
    public static void main(String[] args) {
        // 创建一个TreeMap对象
        Map<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            /**
             * 比较o1.score和o2.score对象的大小,按照学生成绩降序排序
             *   如果o1大于o2,则返回负数
             *   如果o1小于o2,则返回正数
             *   如果o1等于o2,则返回零即可
             */
            @Override
            public int compare(Student o1, Student o2) {
                //三元运算符写法
                //return o1.score>o2.score?-1:o1.score<o2.score?1:0;
                if (o1.score > o2.score){
                    return -1;
                else if (o1.score < o2.score)
                    return 1;
                else
                    return 0;
                }

            }
        });
        // 添加键值对
        map.put(new Student("张三", 38, 99.5), "武汉");
        map.put(new Student("李四", 20, 58), "北京");
        map.put(new Student("王五", 27, 77), "上海");
        map.put(new Student("赵六", 18, 100), "南京");
        map.put(new Student("王五", 27, 89), "上海");
        map.put(new Student("刘七", 29, 90), "深圳");
        // 遍历map
        Iterator<Student> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            Student key = iterator.next();
            System.out.println("key:" + key + ", value:" + map.get(key));
        }
    }
}

使用TreeMap要点

1、由于是二叉树,需要对元素做内部排序。所以要么实现Comparable接口,要么实现Comparator接口,否则会抛出java.lang.ClassCastException异常。

2、TreeMap的key不能为null,否则抛出java.lang.NullPointerException异常

Iterator迭代器

1-Iterator

方法名说明
boolean hasNext()判断集合中是否有下一个元素可以迭代,如果有,则返回 true。
Boject next()返回迭代的下一个元素,并把指针向后移动一位。
void remove()将迭代器当前返回的元素删除(可选操作)。

2-ListIterator

Iterator的子接口,比Iterator更强大,是它的扩展

方法名说明
boolean hasNext();以正向遍历列表时,判断迭代器后面是否还有元素。
Object next()返回列表中ListIterator指向位置后面的元素。
boolean hasPrevious();以逆向遍历列表,判断迭代器前面是否还有元素。
Object previous();返回列表中ListIterator指向位置前面的元素。
void remove();从列表中删除next()或previous()返回的最后一个元素。
void set(Object e);从列表中将next()或previous()返回的元素更改为指定元素。
void add(Object e);将指定的元素插入列表,插入位置为迭代器当前位置之前。

3-keySey()

Set<K> keySet()方法,该返回此映射中包含的键的Set视图将map中所有的键存入到Set集合,因为set具备迭代器,所有迭代方式取出所有的键再根据get()方法,获取每一个键对应的值。

4-entrySet()

entrySet()方法,该方法取出的是关系(Set<Map.Entry<K, V>>),关系中包含key和value。其中Map.Entry<K,V>来表示这种数据类型,即将Map集合中的映射关系存入到Set集合中,这个关系的数据类型为Map.Entry接口。

Map.Entry接口在java.util包中,它是Map接口中的一个内部接口,getKey()和getValue()是接口Map.Entry<K,V>中的方法,返回对应的键和对应的值。

方法名说明
getKey()取出键
getValue()取出值

并发性修改异常

public class Demo05 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("JavaSE");
        list.add("MySql");
        list.add("Linux");
        list.add("Redis");

        
        /*//创建迭代器,迭代器会有一个预期的迭代个数
        Iterator<String> it = list.iterator();

        //判断下一个位置是否有元素
        while (it.hasNext()){

            //获取下一个位置的元素存储到str变量中
            String str = it.next();
            //当遍历到MySql时,添加数据
            if (str.equals("MySql")){
                list.add("JDBC");
            }
        }//ConcurrentModificationException,并发性修改异常*/

        //解决办法一:不使用迭代器
        /*for (int i = 0; i < list.size(); i++) {
            if ("MySql".equals(list.get(i))){
                list.add(i + 1,"JDBC");
            }
        }*/

        //解决办法二:ListIterator

        //创建迭代器对象,可以修改集合预期的迭代个数
        ListIterator<String> lit = list.listIterator();
       
        while (lit.hasNext()){
            if ("MySql".equals(lit.next())){

                //通过自己的add方法添加元素到集合中
                lit.add("JDBC");
            }
        }
        System.out.println(list);
    }
}

Collections工具类

方法名说明
void sort( List<T> list )按照 T 类型排序
void sort( List<T> list , Comparator< ? super T > c)使用外部比较器排序
void shuffle(List<?> list)打乱顺序( 混排 )
void reverse(List<?> list)顺序的反向排序(反转)
void fill(List<? super T> list, T obj)替换所有的元素为 T
boolean addAll(Collection<? super T> c, T... elements)添加所有

以上就是目前整理的集合相关的笔记,集合在javase中还是很重要的,每个集合都有各自的特点,还是要根据实际的业务来选择!

集合图解中部分图源自网络,作者见水印!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值