• Java集合类(笔记)


    笔记

    一、简介

    Java集合类

    1. Java标准库自带的java.util包提供了集合类:Collection。它是除Map外所有其他集合类的根接口。
    2. Java主要包括以下三种集合:
      • List:一种有序列表的集合。
      • Set:一种保证没有重复元素的集合。
      • Map:一种通过键值(key-value)查找的映射表集合。
    3. 存在一些历史遗留类,尽量不要去使用:
      • Hashtable:一种线程安全的Map实现;
      • Vector:一种线程安全的List实现;
      • Stack:基于Vector实现的LIFO的栈。
      • Enumeration<E>:已被Iterator取代。

    二、List

    1. List类就像数组一样,提供两个实现:

      • ArrayList(数组实现)
      • LinkedList(链表实现)
    2. 具体区别:

      image-20220619155302706

    3. LinkedList 即实现了 List 接口,又实现了 Queue 接口。

    4. of()方法:jdk9之后才开始存在

      List.of(1, 2, "1", true);
      
      • 1
    5. 我们应当坚持使用迭代器==iterator或者forEach==循环遍历 List,因为它俩总能根据不同的类型提供最快的遍历速度。

    6. Iterator对象有两个方法:

      • boolean hasNext()判断是否有下一个元素
      • E next()返回下一个元素
    7. 数组与 List 之间的相互转换

      • 数组转 List (两种方法):
      Integer[] num={1,2,3};
      
      // 1. asList()
      List<Integer> list = Arrays.asList(num);
      // 2. of()
      List<Integer> list = List.of(num);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • List 转数组:
      List<Integer> list = List.of(1,2,3);
      // toArray()的参数标识:类型 + 大小
      Integer[]     array = list.toArray(new Integer[list.size()]);
      
      • 1
      • 2
      • 3

    三、Map

    1. Map 跟 List一样,只是一个接口,最常用的实现类是 HashMap

    2. HashMap不保证数据存储的顺序,TreeMap保证。

    3. 如果 key 不存在,将返回 null

    4. 简单遍历 Map

      • ==keySet()==遍历:
      Map<Integer,String > maps=new HashMap<>();
      maps.put(1,"a");maps.put(2,"b");maps.put(3,"c");
      
      for (Integer key:maps.keySet()){
          String value = maps.get(key);
          System.out.println(value);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • ==entrySet()==遍历
      Map<Integer,String > maps=new HashMap<>();
      maps.put(1,"a");maps.put(2,"b");maps.put(3,"c");
      
      for (Map.Entry<Integer, String > map : maps.entrySet()){
          System.out.println(map.getKey()+map.getValue());
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    5. HashMap原理

      • 内部使用数组,数组长度总是 2n(初始大小为16。创建时可以手动指定,以免频繁扩容,但是当指定为1000时,容量并不是真正的1000,而是210 = 1024 ),通过计算 key 的 hashCode( ) 直接定位 value 所在的索引。
      • 空间换时间
      • 如果key的值为"a",计算得到的索引总是1,因此返回value为Person(“Xiao Ming”),这样就不必遍历整个数组,即可直接读取key对应的value。(内部的一套指定算法)

    6. EnumMap:一种基于枚举类的 Map。

    7. TreeMap

      • 基于接口SortedMap,不使用equals()hashCode()(因为有序无需此)。
      • 有序,会在内部对Key进行排序。(放入的Key必须实现 Comparable 接口,如果作为Key的class没有实现Comparable接口,那么必须在创建TreeMap时同时指定一个自定义排序算法)
      Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
                  public int compare(Person p1, Person p2) {
                      return p1.name.compareTo(p2.name);
                  }
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
    8. Properties

      • 一种特殊的 Map ,主要用来读写配置文件。
      • 内部本质上是一个Hashtable,它的设计实际上存在问题,但是为了保持兼容性,现在已经无法更改。
      last_open_file     =    /data/hello.txt
      auto_save_interval =    60
      
      • 1
      • 2
      • 读取步骤
        1. 创建实例;
        2. 调用load()读取文件;
        3. 调用getProperty()获取配置。
      String f = "setting.properties";
      Properties props = new Properties();
      // load()默认总是以ASCII编码读取字节流,所以会导致读到乱码
      props.load(new FileReader("settings.properties", StandardCharsets.UTF_8));
      
      String filepath = props.getProperty("last_open_file");
      String interval = props.getProperty("auto_save_interval", "120");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 写入步骤
      Properties props = new Properties();
      
      props.setProperty("url", "http://www.liaoxuefeng.com");
      props.setProperty("language", "Java");
      
      props.store(new FileOutputStream("./setting.properties"), "这是写入的注释");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    四、Set

    1. Set只是接口,具体实现有hashSetTreeSet(前者不保证顺序)。

    2. Set用于存储不重复的元素集合,实际上相当于只存储key、不存储value的Map

    3. HashSet仅仅是对HashMap的一个简单封装。


    五、Queue

    1. Queue只是一个接口,具体的实现有PriorityQueue、Deque、Stack。

    2. Queue接口定义了几个方法,分别为:

      • int size():获取队列长度
      • add() 或 offer():添加元素到队列:
      • remove() / poll():获取队首元素并删除
      • element() / peek() :获取队首元素但不删除

      注意到三次存在两种方法,不同之处在于

      throw Exception返回false或null
      添加元素到队尾add(E e)boolean offer(E e)
      取队首元素并删除E remove()E poll()
      取队首元素但不删除E element()E peek()

      使用 remove()之类的操作需要编写 try语句,而 poll()则需要编写if语句。

    3. PriorityQueue

      • 对PriorityQueue类每次调用remove()或者poll()方法总是返回优先级最高的元素
      • 放入PriorityQueue的元素,必须实现Comparable接口。例如我们存放String类型的元素,不管当初我是怎么放进去的,到最后弹出的时候只按照Comparable中定义的规则(这里是字母表的顺序)输出。(这点需要十分的重要)
      • 如果不实现Comparable接口,则要编写Comparator(比较器)。
      PriorityQueue<String> queue = new PriorityQueue<>();
      queue.offer("C");
      queue.offer("B");
      queue.offer("A");
      System.out.print(queue.poll() + queue.poll() + queue.poll());
      
      // 输出:ABC
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    4. Deque

      • Deque也是一个接口,并不能直接使用。
      • 相应的实现类有==ArrayDequeLinkedList==。
      • Deque是双端队列,允许两头都进,两头都出(Queue只允许单头进出)。
      • 没有优先队列的优先级概念。
      • 实际上扩展自Queu,但是使用Deque最好不要调用offer(),而是调用offerLast()

      image-20220620084610992

    5. LinkedList全能选手,既可以作链表List,也可以作QueueDequeStack

    6. Stack

      • 栈只有入栈和出栈两种操作。
        • 压栈:push(E)
        • 出栈 + 删除:pop()
        • 出栈 + 不删除:peek()
      • 栈有容量限制,超出将触发StackOverflowError(栈溢出)异常。
      • 栈可以用来进行进制之间的转换。
      • 计算机在执行表达式的时候,并不是直接计算中缀表达式(例如 1 + 2 +3),而是把中缀表达式转为后缀表达式之后才开始计算(例如:123++),用到了

    六、Iterator

    1. Java的集合类都可以使用for each循环。

    2. ListSetQueue会迭代每个元素,Map会迭代每个key

    3. forEach循环和iterator是同源的:

      • 编译前
      List<String> list = List.of("Apple", "Orange", "Pear");
      for (String s : list) {
          System.out.println(s);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 编译后
      List<String> list = List.of("Apple", "Orange", "Pear");
      for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
           String s = it.next();
           System.out.println(s);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    4. 自定义iterator迭代器

      // 总是倒序输出
      class ReverseIterator implements Iterator<T> {
              int index;
      
              ReverseIterator(int index) {
                  this.index = index;
              }
      
              @Override
              public boolean hasNext() {
                  return index > 0;
              }
      
              @Override
              public T next() {
                  index--;
                  return ReverseList.this.list.get(index);
              }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    七、Collections

    1. 注意是Collections,而不是Collection。

    2. JDK提供的工具类

    3. 常用方法

      • 排序:Collections.sort(list);

      • 洗牌:Collections.shuffle(list);

      • 封装成不可变集合:Collections.unmodifiableList/Map/Set(mutable);

        但是,继续对原始的可变List进行增删是可以的,并且,会直接影响到封装后的“不可变”List。所以在转换过后,我们最好立刻扔掉原始的List引用(赋值为 null )。

  • 相关阅读:
    [学习笔记]多元线性回归的梯度下降
    设备树覆盖:概念与术语
    JS逆向系列之某瓜数据解密
    Java中的StringTable常量池
    docker 第二次学习笔记
    【算法挨揍日记】day09——35. 搜索插入位置、69. x 的平方根
    js+贝塞尔曲线+animate动画
    动态代理,XML,Dom4j
    基于 Qt控制开发板 LED和C语言控制LED渐变亮度效果
    Unittest(单元测试)
  • 原文地址:https://blog.csdn.net/qq_35760825/article/details/125477440