A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

[石家庄校区]就业班,List与Set,Thread,Map,线程

List集合体系的特点:
        1. 元素存取有序 (存入和取出元素的顺序一致) 321->321  排序: 从小到大
        2. 元素可以重复  1 1 1 1
        3. 有索引
List子体系中的实现类都具有上述特点

java.util.List接口:
        // 常用特有成员方法 (都是按照索引来操作的)   
        void add(int index, E element): 将指定的元素, 添加到该集合中的指定位置上
        E get(int index): 返回集合中指定位置的元素
        E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素
        E set(int index, E element): 用指定元素替换集合中指定位置的元素, 返回值的更新前的元素
ArrayList底层的数据结构:
        数组

ArrayList的特点:
        查询快
        增删慢
        线程不安全, 效率高

ArrayList适用场景:
        存储的数据"查询多, 增删少"的场景. 如用一个ArrayList存储中国城市名称

LinkedList底层的数据结构:
        链表

LinkedList的特点:
        查询慢
        增删快
        线程不安全, 效率高

LinkedList适用场景:
        存储的数据"查询少, 增删多"的场景. 如用LinkedList实现栈或队列

java.util.LinkedList<E>类: 链表结构, 查询慢, 增删快
        // 特有成员方法(主要操作开头和末尾元素)
        void addFirst(E e): 将指定元素插入此列表的开头
        void addLast(E e): 将指定元素添加到此列表的结尾
        E getFirst(): 返回此列表的第一个元素
        E getLast(): 返回此列表的最后一个元素
        E removeFirst(): 移除并返回此列表的第一个元素
        E removeLast(): 移除并返回此列表的最后一个元素
        E pop(): (其实就是removeFirst())从此列表所表示的栈中弹出一个元素
        void push(E e): (其实就是addFirst())将元素添加到此列表所表示的栈中


Set集合体系特点:
        1. 元素不可重复
        2. 没有索引

HashSet特点:
        1. 元素不可重复
        2. 没有索引
        3. 元素存取无序 (存入和取出顺序有可能不一致)
        4. 底层采用 哈希表 结构. (查询快)
                哈希表 = 数组 + 链表或红黑树

.HashSet类:
        boolean add(E e): 添加元素, 根据元素的 hashCode() 和 equals() 方法判断是否重复. 重复则不添加并返回false, 不重复则添加并返回true
HashSet集合保证添加元素不重复的原理:
        调用 add(E e) 添加元素时, 先调用 hashCode() 获取哈希值, 和当前HashSet集合中的元素比较
                如果哈希值不同, 则认为元素不重复, 添加, 并返回true
                如果哈希值相同, 则可能是哈希冲突, 所以继续调用元素的 equals() 方法和所有哈希值相同的元素比较
                        如果 equals() 比较所有元素都没有相同的, 则认为元素不重复, 添加, 并返回true
                        如果 equals() 比较出有相同的元素, 则认为元素重复, 不添加, 并返回false
LinkedHashSet特点:
        1. 元素存取有序 (存入和取出顺序一致)
        2. 元素不可重复
        3. 没有索引

LinkedHashSet底层数据结构:
        哈希表 + 链表   (也就是: 数组 + 链表或红黑树 + 链表)
        其中, 哈希表用于存储数据, 额外的链表用于记录元素添加时的先后顺序, 以便在获取元素时保持顺序一致

可变参数:
        JDK 5 出现. 指同一个类型的参数, "个数可变"
        可变参数的本质就是一个"数组"

        格式: 用在方法的参数中
            修饰符 返回值类型 方法名(int... 变量名) {
                    // 可以直接将 变量名 当作 数组名 使用
            }
            方法名();

注意事项:
        1. 可变参数可以传递的参数个数, 可以是 0个, 1个, 多个
        2. 一个方法的参数列表中, 只能有一个可变参数
        3. 如果方法的参数有多个, 可变参数必须写在参数列表的最后
Collections类: 操作集合的工具类
        // 静态方法
        static <T> boolean addAll(Collection<? super T> c, T... elements):往集合中添加一些元素
        static void shuffle(List<?> list): 打乱集合顺序
        static <T> void sort(List<T> list): 将集合中元素按照默认规则排序
        static <T> void sort(List<T> list,Comparator<? super T> c):将集合中元素按照指定规则排序
compar
sort(List<T> list): 默认按照"升序"将元素排序
        数字, 字母, 都可以按照升序排序

自定义JavaBean对象默认不能排序, 因为不知道如何比较哪个对象大, 哪个对象小
自定义JavaBean对象要想排序, 需要实现 Comparable<E> 接口, 重写 int compareTo(E e) 方法
        规则:
                this-参数: 升序(从小到大)
                参数-this: 降序(从大到小)
Map接口: 双列集合的根接口, 规定了共性的方法
        |_ HashMap类: 哈希表=数组+链表+红黑树. key无序不可重复, 可存null键null值, 线程不安全效率高
        |        |_ LinkedHashMap类: 哈希表+链表. 哈希表实现key不可重复, 链表实现key存取有序
        |
        |_ Hashtable类: 哈希表. Hash特性针对key, key无序不可重复, 不可存null键null值, 线程安全效率低
        |_ TreeMap类: 红黑树结构(存入时就排序). Tree特性针对key, 可以按照key排序, 要求key具备比较性
        |_ 遍历
                |_ keySet(): 获取所有key组成的Set集合, 遍历Set集合拿到key, 通过Map的get(key)得到value
                |        |_ 对于Set<Key>的遍历
                |                |_ 增强for
                |                |_ 迭代器
                |_ entrySet(): 获取所有的key和value组成的Entry对象的Set集合, 遍历出entry对象, 通过entry对象的getKey()获取对应的key, 通过Entry对象的getValue方法获取对应的value
                        |_ 对Set<Entry>的遍历
                                |_ toArray()
                                |_ 增强for
                                |_ 迭代器

Map集合特点:
        1. 是双列集合, 一个元素包含两个值 (键key, 值value) 键值对
        2. key和value的类型可以相同, 也可以不同
        3. key不允许重复, value可以重复
        4. key和value是一一对应的, 一个键只能对应一个值

Map集合适合存储"一对一关系"的数据
        夫妻
        String : Phone
        统计次数: 某个字符出现了多少次  "aaababababba"
                'a': 1
        'b': 3
Map接口: 双列集合的顶层
        // 成员方法
        V?put(K?key,?V?value): 添加/修改 键值对.
            如果键存在, 则用新值替换已有值, 返回被替换的值; 如果键不存在, 则添加键值对, 返回null
        V?remove(Object?key): 根据键删除键值对, 返回被删除元素的值
                如果键不存在, 返回null
        V?get(Object?key): 根据键获取值.
            如果键不存在, 则返回null
        boolean containsKey(Object key): 判断是否包含指定的键
        boolean containsKey(Object key): 判断是否包含指定的键
        Set<K>?keySet(): 获取Map集合中所有的键, 存储到Set集合中
        Set<Map.Entry<K,V>>?entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)



// JDK 9对于集合初始化的优化
java.util.List
        // 静态方法
        static List<E> of(E... e): 返回包含指定元素的 不可变List 集合

java.util.Set
        // 静态方法
        static Set<E> of(E... e): 返回包含指定元素的 不可变Set 集合

java.util.Map
        // 静态方法
        static Map<K, V> of(K k1, V v1): 返回包含指定键值对的 不可变Map 集合
keySet()遍历步骤:
        1. Map对象调用 keySet() 方法, 获取包含所有key的Set集合
        2. 遍历Set集合, 获取每个key
        3. 通过Map对象调用 get(Object key) 方法根据key获取到value

        Map<String, String> map = new HashMap<>();
        // keySet()遍历
        Set<String> keys = map.keySet();
        for (String key : keys) {
                // 通过每个键获取值
                String value = map.get(key);
                // 打印当前键值对
                System.out.println(key + "=" + value);
        }
java.util.Map接口
        Set<Map.Entry<K,V>>?entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)
   
entrySet()方法遍历Map步骤:
        1. Map对象调用 entrySet() 获取包含所有Entry对象的Set集合
        2. 遍历Set集合, 获取每个Entry对象
        3. 调用Entry对象的 getKey() 和 getValue() 方法获取键和值

        Map<String, String> map = new HashMap<>();
        // keySet()遍历
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            // 通过Entry对象获取每个键值对
            String value = entry.getKey();
            String value = entry.getValue();
            // 打印当前键值对
            System.out.println(key + "=" + value);
        }
HashMap存储自定义数据类型作为键
HashMap存储自定义JavaBean对象作为key保证key唯一不重复, 需要让JavaBean重写 hashCode() 和 equals() 方法
LinkedHashMap类
LinkedHashMap底层: 哈希表 + 链表
        key不允许重复, 但key存取有序

Debug调试程序:
        breakpoint, 在debug模式下, 程序运行到断点会暂停住, 便于我们在程序运行过程中查看
    可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
使用方式:
    在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
    右键,选择Debug执行程序
    程序就会停留在添加的第一个断点处
执行程序:
    f8:逐行执行程序
    f7:进入到方法中
    shift+f8:跳出方法
    f9:跳到下一个断点,如果没有下一个断点,那么就结束程序
    ctrl+f2:退出debug模式,停止程序
    Console:切换到控制台

java.lang.Thread类: 表示线程. 实现了Runnable接口
        // 构造方法
    Thread Thread(): 创建Thead对象
    Thread Thread(String threadName): 创建Thead对象并指定线程名
    Thread Thread(Runnable target): 通过Runnable对象创建Thread对象
    Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名
        // 成员方法
    void run(): 用于让子类重写, 表示该线程要执行的任务.不能直接调用
    void start(): 启动线程, 即让线程开始执行run()方法中的代码
    String getName(): 获取线程的名称
    void setName(String name): 设置线程名称
        // 静态方法
    static Thread currentThread(): 返回对当前正在执行的线程对象的引用
    static void sleep(long millis): 让所在线程睡眠指定的毫秒

java.lang.Object类:
        // 成员方法 (只能通过"锁对象"调用)
        void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程
        void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程
        void wait(): 让当前线程处于无限等待状态
        void wait(long timeout): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态
        void wait(long timeout, int nanos): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态
创建线程的第2种方式:
        1. 定义类, 实现Runnable接口
        2. 重写 run() 方法, 要执行的代码(任务)
        3. 创建Runnable实现类对象 (任务对象)
        4. 创建Thread类对象, 在构造方法中传入Runnable实现类对象 (将任务和线程绑定)
        5. 通过Thread对象调用 start() 方法启动线程

java.lang.Thread类: 表示线程. 实现了Runnable接口
        // 构造方法
    Thread Thread(Runnable target): 通过Runnable对象创建Thread对象
    Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名
格式:
    synchronized (锁对象) {
                // 操作共享数据的代码

    }


注意:
        锁对象可以是"任意类型的一个对象"
        锁对象必须是"被多个线程共享的唯一的"对象
        锁对象的作用: 只让一个线程在同步代码块中执行
锁对象, 也称为"同步锁", "对象锁", "对象监视器"

同步的原理:
    线程进入同步代码块前, 会"争夺锁对象", "只有一个线程"会抢到锁对象
    进入同步代码块的线程, 会"持有锁对象", 并执行同步代码块中的代码
    此时同步代码块外的线程, 处于"阻塞"状态, 只能等待
    当同步代码块内的线程执行完代码块, 会离开同步代码块, 并"归还锁对象"给同步代码块
    等在同步代码块外的其他线程就可以继续争夺锁对象
java.util.concurrent.locks.Lock接口: JDK 5 新增的Lock接口
        // 成员方法
        void lock(): 获取锁
        void unlock(): 释放锁

java.util.concurrent.locks.ReentrantLock类: Lock的实现类

使用方式:

public class RunnableImpl implements Runnable {
    // 成员变量创建锁对象, 该锁对象也要所有线程共享唯一一个
    Lock lock = new ReentrantLock();  // 成员变量

    @Override
    public void run() {
        // 加锁
        lock.lock();
        try {
            // 操作共享变量的代码...
        } finally {
            // 在finally中保证释放锁
            lock.unlock();  
        }
    }
}
线程的生命周期中, 可以出现有6种状态:
    1. "NEW 新建"
       线程被创建, 但没有调用 start() 启动
    2. "RUNNABLE 可运行"
       调用 start()方法后已启动, 但可能正在执行 run() 方法的代码, 也可能正在等待CPU的调度
    3. "BLOCKED (锁)阻塞"
       线程试图获取锁, 但此时锁被其他线程持有
    4. "WAITING 无限等待"
       通过锁对象调用无参的 wait() 进入此状态.
       等待其他线程通过锁对象执行 notify() 或 notifyAll() 才能结束这个状态
    5. "TIMED_WAITING 计时等待"
       如通过锁对象调用有参的 wait(long millis) 或 sleep(long millis), 则进入此状态.
       直到时间结束之前被其他线程通过锁对象执行 notify()或 notifyAll()唤醒, 或时间结束自动唤醒
    6. "TERMINATED 终止"
       run()方法结束(执行结束, 或内部出现异常), 则进入此状态
格式:
线程
    try {
        // 可能产生异常的代码
    } catch (异常类名 变量名) {
        // 处理异常的代码
        // 一般会将异常信息存储到日志中
    }
        ...
        } catch (异常类名 变量名) {
        // 处理异常的代码
        // 一般会将异常信息存储到日志中
// JDK 7增加
try {
        // 可能发生异常的代码
} catch (异常类型1 | 异常类型2 | 异常类型3 | ... 异常对象名) {
        // 处理任意一个异常的代码
}

try {
        // 可能发生异常的代码
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
        // 处理异常的代码
}
java.lang.Throwable
        // 成员方法
        String getMessage(): 异常的信息. 没有原因返回null
        String toString(): 异常的类型和原因信息
        void printStackTrace(): 使用标准错误输出流打印异常信息
格式:

    try {
        // 可能发生异常的代码
    } catch(异常类型 异常变量名) {
        // 处理异常
    }
        ...
        catch(异常类型 异常变量名) {
        // 处理异常
    } finally {
        // 无论是否发生异常, 是否捕获, 最后都会执行的代码.
        // 通常在这里执行释放资源的操作
    }
实现多线程的第一种方式:
        1. 定义类, 继承 Thread 类
        2. 重写 run() 方法, run方法内部是线程要执行的任务
        3. 创建Thread子类的对象, 调用 start() 方法启动线程





0 个回复

您需要登录后才可以回帖 登录 | 加入黑马