美文网首页一些收藏
[Java]重学Java-集合

[Java]重学Java-集合

作者: AbstractCulture | 来源:发表于2021-09-18 17:29 被阅读0次

    容器

    很多时候,我们写程序需要进行批量的操作,比如说,新增一批学生列表.那么就需要有容器来装下这10个对象。
    Java提供了许多容器来装对象,在JDK的java.util包下,编程中最常用的有:

    1. List: 有序列表,有序地存储元素
    2. Set: 集合,存储的元素不可重复
    3. Map: 映射,建立key->value关系
    4. Quene: 队列,先进先出
    5. Stack: 栈,先进后出

    泛型

    使用Java的集合类最好配合泛型进行使用,以免发生以下的错误:

    package com.tea.modules.java8.generic;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * com.tea.modules.java8.generic <br>
     * 不要使用原始类型
     * @author jaymin
     * @since 2021/6/4
     */
    @SuppressWarnings("all")
    public class DoNotUseRawType {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add(1);
            list.add("2");
        }
    }
    

    例子中,我们对list没做任何类型限制,添加了数字1和字符串2,那么在遍历取值使用的时候,我们可能会将字符相关的操作用在数字上,从而引发了异常。

    equals 和 hashcode

    如果存储的是对象,那么需要重写equals和hashcode,否则你的集合操作会产生意外的异常.

    package com.tea.modules.java8.collection.prevent;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.*;
    
    /**
     * com.xjm.collection.prevent<br>
     * 对集合对象进行操作,最好重写equals和hashcode,避免带来不好的影响<br>
     *
     * @author xiejiemin
     * @create 2020/12/25
     */
    @Slf4j
    public class EqualsAndHashCode {
    
        public static class Student implements Comparable<Student>{
            /**
             * 姓名
             */
            private String name;
            /**
             * 性别:0-man|1-women
             */
            private Integer gender;
    
            public Student(String name, Integer gender) {
                this.name = name;
                this.gender = gender;
            }
    
            public Student() {
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public Integer getGender() {
                return gender;
            }
    
            public void setGender(Integer gender) {
                this.gender = gender;
            }
    
            @Override
            public boolean equals(Object obj) {
                if (obj instanceof Student) {
                    Student student = (Student) obj;
                    return Objects.equals(this.name, student.name);
                }
                return false;
            }
    
            @Override
            public int hashCode() {
                int result = name.hashCode();
                result = 31 * result + gender;
                return result;
            }
    
            @Override
            public int compareTo(Student student) {
                return this.gender - student.gender;
            }
        }
    
        /**
         * <h2>实现/不实现equals和hashcode对于判等的影响</h2>
         */
        private static void beforeOverrideEqualsAndHashCode() {
            Student jackA = new Student("Jack", 20);
            Student jackB = new Student("Jack", 20);
            log.info("Before override equals and hashcode->{}", Objects.equals(jackA,jackB));
            Set<Student> studentSet = new HashSet<>();
            studentSet.add(jackA);
            studentSet.add(jackB);
            studentSet.forEach(System.out::println);
            Map<Student,Integer> studentMap = new HashMap<>();
            studentMap.put(jackA,20);
            studentMap.put(jackB,20);
            System.out.println(studentMap.size());
        }
    
        /**
         * <h3>类实现了compareTo方法,就需要实现equals方法</h3>
         * <h3>compareTo与equals的实现过程需要同步</h3>
         */
        private static void compareToWithEquals(){
            ArrayList<Student> students = new ArrayList<>();
            students.add(new Student("Jack",10));
            students.add(new Student("Jack",20));
    
            Student jack = new Student("Jack", 20);
            // equals
            int studentIndex = students.indexOf(jack);
            // compareTo
            int index = Collections.binarySearch(students, jack);
    
            System.out.println(studentIndex+"   "+index);
        }
        public static void main(String[] args) {
            beforeOverrideEqualsAndHashCode();
            compareToWithEquals();
        }
    }
    

    接口与实现

    以列表为例子,我们通常这样使用:

    List<String> strings = new ArrayList<>();
    

    List是列表这个数据结构的顶层接口,ArrayList是具体的实现,ArrayList底层基于数组实现,可以动态扩容。
    如果此时,我们需要更换队列的实现类,那么直接更换即可,依然返回List接口类型的结果.

    List<String> strings = new LinkedList<>();
    

    Collection

    java中的绝大多数集合类都实现了Collection接口:

    collection

    来看看这些接口分别代表的含义:

    method description
    size 返回集合中元素的个数
    isEmpty 判断当前集合中是否存储了元素
    contains 集合中是否包含某个对象
    iterator 获取迭代器
    toArray 将集合转换成Object数组
    toArray(T[]) 将集合转换成对象数组
    add 往集合中添加元素
    remove 从集合中移除元素
    containsAll 集合中是否有子集
    addAll 将两个集合合并
    removeAll 求集合的差集
    removeIf 如果符合Predicate的条件,将元素从集合中删除
    retainAll 从该集合中移除所有未包含在指定集合中的元素
    clear 移除集合中所有元素
    stream 将集合转为流
    parallerStream 将集合转为并行流

    Iterator-迭代器

    关于迭代器,可以看看我之前的文章,这里不重复描述
    点我前往

    • 关于为什么要设计迭代器的解释

    要使用集合,必须对集合的确切类型编程。这一开始可能看起来不是很糟糕,但是考虑下面的情况:如果原本是对 List 编码的,但是后来发现如果能够将相同的代码应用于 Set 会更方便,此时应该怎么做?或者假设想从一开始就编写一段通用代码,它不知道或不关心它正在使用什么类型的集合,因此它可以用于不同类型的集合,那么如何才能不重写代码就可以应用于不同类型的集合?
    迭代器(也是一种设计模式)的概念实现了这种抽象。迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。

    摘自《Java编程思想》

    集合实现

    • 队列与集合

      abstractCollection
    • 映射

      map

    说到映射,很多朋友可能觉得很迷糊,其实就是key->value的关系.

    package com.tea.modules.java8.collection.maps;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * com.tea.modules.java8.collection.maps <br>
     * 用于测试Map的API
     * @author jaymin
     * @since 2021/9/18
     */
    public class WhatIsMap {
    
        public static void main(String[] args) {
            Map<String,Object> hashMap = new HashMap<>();
            hashMap.put("男","man");
            hashMap.put("女","woman");
            System.out.println(hashMap.get("男"));
        }
    }
    
    collection description
    ArrayList 可以动态扩容和缩减的有序列表
    LinkedList 可以在任一位置进行插入和删除的有序列表,基于链表
    HashSet 无重复元素的无序集合
    HashMap 存储键值对的映射表,无序
    EnumSet 枚举集合
    LinkedHashSet 记录插入顺序的无重复元素集合
    PriorityQueue 一种允许高效删除最小元素的集合
    TreeMap Key根据自然排序的映射表
    LinkedHashMap 记录插入顺序的映射表
    WeakHashMap 对象引用为弱引用的映射表
    IdentityHashMap 用==来对比键值的映射表

    快速失败机制

    package com.tea.modules.java8.collection.prevent;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.ListIterator;
    
    /**
     * com.tea.modules.java8.collection.prevent <br>
     * 触发集合的ConcurrentModificationException <br>
     * 可以想象, 如果在某个迭代器修改集合时, 另一个迭代器对其进行遍历,一定会出现
     * 混乱的状况。例如,一个迭代器指向另一个迭代器刚刚删除的元素前面,现在这个迭代器
     * 就是无效的,并且不应该再使用。链表迭代器的设计使它能够检测到这种修改。如果迭代
     * 器发现它的集合被另一个迭代器修改了, 或是被该集合自身的方法修改了, 就会抛出一个
     * ConcurrentModificationException 异常 <br>
     * JDK文档:   <br>
     * 例如,通常不允许一个线程修改一个集合,而另一个线程正在对其进行迭代。  <br>
     * 通常,在这些情况下迭代的结果是不确定的。   <br>
     * 如果检测到此行为,某些迭代器实现(包括 JRE 提供的所有通用集合实现的实现)可能会选择抛出此异常。   <br>
     * 这样做的迭代器被称为快速失败迭代器,因为它们快速而干净地失败,  <br>
     * 而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。<br>
     * @author jaymin
     * @since 2021/9/18
     */
    public class ConcurrentModificationExceptionDemo {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("A");
            list.add("B");
            ListIterator<String> stringListIteratorA = list.listIterator();
            ListIterator<String> stringListIteratorB = list.listIterator();
            stringListIteratorA.next();
            stringListIteratorA.remove();
            stringListIteratorB.next();
        }
    }
    

    相关文章

      网友评论

        本文标题:[Java]重学Java-集合

        本文链接:https://www.haomeiwen.com/subject/sjrygltx.html