• Java笔记07


    java标准库中被使用最多的类型

    Java集合简介

    • 集合是为了处理一组类似的数据
    • 如果一个Java对象可以在内部持有若干个其他Java对象, 并对外提供访问接口, 就是Java中的集合

    Collection

    除Map外, 其他集合类的根接口

    • java.util主要提供以下三种类型的集合
      • List: 一种序列表的集合.
      • Set: 保证没有重复元素的集合.
      • Map: 通过键值(key-value)查找的映射表集合.
    • 集合的设计特点:
      • 实现了接口和继承类相分离
      • 支持泛型
      • 通过迭代器(Iterator)访问
    • 尽量不适用遗留接口

    使用List

    • List: 是一种有序链表
    • List和数组几乎完全相同:
      • 按照元素的先后顺序存放
      • 每个元素都可以通过索引确定自己的位置
      • 索引从0开始
    • ArrayList内部使用数组来存储元素
      • 添加的使用, 向后挪一位
      • 继续添加, 没空闲的时候, 就再造一个更大的数组, 然后把这个数组复制过去
    • List<E>接口, 主要方法
      • 末尾添加一个元素: void add(E e)
      • 在指定索引添加一个元素: void add(int index, E e)
      • 删除指定元素索引: int remove(int index)
      • 删除某个元素: int remove(Object e)
      • 获取指定索引的元素: E get(int index)
      • 获取链表大小: int size()
    • LinkedList通过链表也实现了List接口
    • LinkedList中, 每个元素都指向下一个元素

    List特点

    • 可以添加重复元素
    • 可以添加null

    创建List

    • 可以通过List.of()进行创建, 根据给定元素快速创建List
    • 不接受null

    遍历List

    • 使用Iterator
    • 由List实例调用iterator()创建
    • hasNext()是否含有下一个元素
    • E next()返回下一个元素
        List<String> list = List.of("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext();) {
          String s = it.next();
          System.out.println(s);
        }
    
    • Iterator遍历List永远都有最高效的方式
    • Java中的for each使用的就是Iterator遍历
    • 只要实现了Iterable接口的集合都可以直接使用for each进行遍历

    List和Array互换

    • List to Array
      • toArray()方法直接返回一个Object[]数组: 造成信息丢失
      • toArray(T[])传入一个相同类型的Array, List自动把元素复制到传入的Array
        • 可以传入向上兼容的类型.
        • 如果传入的数组元素比之前多了, 会再创建一个数组进行存储.
      • Number[] arr = list.toArray(Number[]::new);: 函数式写法
    • Array to List
      • 使用List.of(T...)
      • List.of生成的List是只读的.
      • 调用add(), remove()方法会抛出错误.

    编写equals方法

    • boolean contains(Object o)方法判断List是否包含某个指定元素.
    • int indexOf(Object o)方法可以返回某个元素的索引

    编写equals

    • 编写规则:
      • 自反性: 非null的x. x.equals(x)返回true
      • 对称性: 非null的x, y. x.equals(y)为true, 那么y.equals(x)也必须为true
      • 传递行: 非null的x, y, z. x.equals(y)为true, y.equals(z)为true, x.equals(z)也必须为true
      • 一致性: 非null的x, y, 只要x和y的状态不变, x.equals(y)总是一致返回true或者false
      • 对null的比较: x.equals(null)总是返回false
    • 编写步骤:
      • 先确定实例相等逻辑
      • instanceof判断传入的实例, 是否为当前的类型
      • 对与引用类型, 使用Objects.equals()进行判断, 基本类型, 使用==
      • Objects.equals判断两个null, 他们相等.

    使用Map

    • 高效通过key快速查找value(元素)
    • Map<K, V>是一种键-值映射表
    • 调用put(K key, V value)方法时, 即将keyvalue做了映射关系
    • 调用V get(K key)时, 就可以通过key, 获取value
    • key, 不存在, 返回null
    • boolean containsKey(K key)方法, 判断key是否存在
    • 不存在重复的key, 如果放入同样的key, 会把之前的key替换掉, 存一个新的value
    • value是可以重复的

    遍历Map

    • .keySet()获取key的集合, 在用for each遍历集合
    • .entrySet()同时遍历keyvalue
    • Map不能保证顺序. 遍历MAP, 应该假设输出的key是无序的

    编写equals和hashCode

    • hashMap内部使用空间换时间的方法
    • 作为key的对象, 必须正确覆写equals方法
    • key对象, 进行hashCode(), 返回一个int整数
    • 作为key的对象, 必须正确覆写hashCode()方法
    • 如果两个对象相等, 则hashCode()必须相等
    • 如果两个对象不相等, 则hashCode()尽量不相等
    • 可以使用Objects.hash(a, b, c)进行覆写

    延伸阅读

    • HashMap默认大小只有16, int index = key.hashCode() & 0xf
    • 数组不够用了, 扩容一倍, 重写计算key
    • 频繁扩容影响性能, 所以一开始制定容量, Map<String, Integer> map = new HashMap<>(1000)
    • HashMap实际上不是直接存储的实例对象, 而是对应一个entrylist
    • 如果key.hashCode()以后相同, 就放到不同的两个entry上面.
    • 然后遍历整个list, 找到对应的entry

    使用EnumMap

    • 如果keyenum对象
    • 内部使用非常紧凑的数组存储value
    • 根据enum类型的key, 直接定义到内部数组的索引, 不需要计算hashCode()
    • EnumMap时间和空间都可以保证, 优先使用
    • EnumMap使用的时候, 持有Map接口

    使用TreeMap

    • SortedMap会对key进行排序. SortedMap是接口, TreeMap是实现类
    • 保证遍历时, 以key的顺序进行排序
    • key必须实现Comparable接口
    • String.compareTo()在两个值相同的时候, 返回0, 就是判断一样
    • TreeMapkey不需要实现equalshashCode

    使用Properties

    • 配置文件特定: Key-Value是String-String
    • Properties表示一组配置

    读取配置文件

    • 创建Properties实例
    • 调用load()读取文件
    • 调用getProperty()获取配置
    • 接受一个InputStream, 表示字节流, 所以文件字节流和jar包中的资源流都可以
    • 也可以从内存中直接读取一个字节流
    • 多个.properties文件, 反复load()读取, 后面读取的key-value会覆盖已经读取的
    • 历史问题, 从Hashtable派生, 只使用getPropertysetProperty, 不适用继承的get()put()方法

    写入配置文件

    • setProperty()修改属性, 使用store()进行存储

    编码

    • load(InputStream)默认总是以ASCII编码, 所以会导致读到乱码
    • load(Reader)方法解决, 一个字节流, 一个字符流, 字符在内容已经char类型表示了, 不涉及编码问题

    使用Set

    • 存储不重复的key, 不需要存储映射的value, 使用Set
    • 添加: boolean add(E e)
    • 删除: boolean remove(Object e)
    • 判断是否含有: boolean contains(Object e)
    • Set是只存key, 不存value的map
    • Set中的元素需要实现equals()hashCode()
    • HashSet仅仅是对HashMap的一个简单封装
    • Set接口不能保证有序, 但是SortedSet接口可以保证有序
    • TreeSet可以保证有序, 元素正确实现Comparable接口

    使用Queue

    • 队列只有两个操作:
      • 把元素添加到队列末尾
      • 从队列头部取出元素
    • 方法:
      • int size(): 获取队列长度
      • boolean add(E)/boolean offer(E)添加元素到队尾
      • E remove() / E poll()获取首元素并从队列中删除
      • E element() / E peek()获取首元素但不从队列中删除
    • 区别:
      • add()添加失败, 抛出异常 / offer()添加失败, 返回false
      • 如果空队列: remove(), 抛出异常 / pull(), 返回null 所以不要把null放到队列
      • pull()获取并删除 / peek()获取不删除, 重复使用
    • LinkedList即实现了List接口, 又实现了Queue接口.

    使用PriorityQueue

    • 出队顺序和元素的优先级有关.
    • 调用remove()或者poll()方法, 返回的总是优先级最高的元素
    • 放入其中的元素必须实现Comparable接口, 从而根据优先级进行出队

    使用Deque

    • 双端队列, 允许两头进, 两头出.
    • 可以添加元素到队尾, 也可以添加到队首
    • 可以从队尾获取, 也可以从队首获取
    • addLast() / addFirst()
    • 扩展自Queue, 但最好不要使用add()等方法
    • Deque的实现类ArrayListLinkedList
    • 面向对象编程的原则: 尽量持有接口, 而不是具体的实现类

    使用Stack

    • 栈: 是一种后进先出的数据结构
    • 压栈: push(E) / addFirst(E)
    • 栈顶元素弹出: E pop() / E removeFirst()
    • 取栈顶元素但不弹出E peek() / E peekFirst()
    • 没有Stack接口, 使用Deque进行模拟, 只使用push, pop, peek方法

    Stack的作用

    • jvm使用栈结构维护方法的调用顺序

      调用的时候, 会先把参数压栈, 然后执行对应的方法; 方法返回时, 返回值压栈, 调用方法通过出栈操作获得方法返回值.

    • 调用方法有容量现在, 调用过多会造成栈溢出: StackOverflowError

    • 对整数进行进制转换

    • 计算表达式后缀

    使用iterator

    • Java的集合类都可以使用for each循环
          for (Iterator<String> it = list.iterator();it.hasNext();) {
            String s = it.next();
            System.out.println(s);
          }
    
    • Iterator对象是在内部创建, 对象知道如果高效处理自己的结构
    • 调用方只关心统一的方法调用
    • 实现要求:
      • 集合类实现了Iterable接口, 要求返回一个Iterator对象
      • Iterator对象迭代集合内部数据
    import java.util.*;
    public class Main {
        public static void main(String[] args) {
          ReverseList<String> rList = new ReverseList();
          rList.add("A");
          rList.add("B");
          rList.add("C");
          for (String s : rList) {
            System.out.println(s);
          }
        }
    }
    
    class ReverseList<T> implements Iterable<T> {
      private List<T> list = new ArrayList<>();
      public void add(T t) {
        list.add(t);
      }
      
      @Override
      public Iterator<T> iterator() {
        return new ReverseIterator(list.size());
      }
      
      class ReverseIterator implements Iterator<T> {
        int index;
        ReverseIterator(int index) {
          this.index = index;
        }
        @Override
        public boolean hasNext() {
          return index > 0;
        }
        public T next() {
          index--;
          return ReverseList.this.list.get(index);
        }
      }
    }
    
    
    • 通过内部类实现Iterator接口

    • 这个内部类, 可以直接访问对应外部类的全部字段和方法

    • 使用外部类.this获取

    • 集合类实现了Iterable接口, 提供iterator方法, 返回Iterator实例

    使用Collections

    • Collections工具类, 提供一系列操作集合的方法

    创建空集合

    • 创建空List: List<T> emptyList()
    • 创建空Map: Map<T> emptyMap()
    • 创建空Set: Set<T> emptySet()
    • .of()相同: 空集合是不可变集合, 无法添加或者删除元素.
        List<String> list1 = List.of();
        List<String> list2 = Collections.emptyList();
    

    创建单元素集合

    • List<T> singletonList(T t)

    • Map<K, V> singletonMap(K k, V v)

    • Set<T> singleton(T t)

    • 返回的单元素集合, 或者多个元素集合, 也是不可变的, 还是.of()方便

    排序

    • Collections.sort()进行排序, 修改List中元素的位置

    洗牌

    • Collections.shuffle()进行洗牌, 打乱元素位置

    不可变集合

    • 提供一系列方法, 将集合变成不可变的
    • 不可变List: List<T> uList = Collections.unmodifiableList(list);
    • Set, Map一个样
    • 其实就是拦截修改方法
    • 但是可以操作原来的数组, 所以最好是马上放弃原来的索引

    线程安全集合

    • List<T> synchronizedList(List<T> list)
    • 后面设计度线程, java5开始引入更高效并发类, 线程安全集合没有什么用了.

    疑问

    • 文件流的文件位置始终不对.
  • 相关阅读:
    Extjs打开window窗口自动加载html网页
    CSS预处理器之SASS用法指南
    HmacSHA256摘要算法
    Base64编解码
    孔子困于陈蔡故事(转载)
    我的2019
    给Oracle字段和表加注释
    【JDBC】使用properties连Oracle数据库,使用DatabaseMetaData获取字段的注释
    [JDBC]查询结果集把字段名和字段值一起竖向输出
    [Java/Reflect]使用反射机制获得一个对象的属性名和属性值
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/12678449.html
Copyright © 2020-2023  润新知