2026-02-09

02月09日

一、今日完成情况

  • Java学习完成循环、结构体的基本语法上手,实现项目

二、今日感悟

  • 核心业务数据​:
    • Java项目上手基础,是Java容器,并且梳理出理解容器的基础要求,这就要求数据结构和算法我需要快速回忆,并且上手基本的数据结构算法题(力扣基本要求)
    • 完成链表的学习,自行编写函数。
  • ​今日工作总结:​
    • 今天效率不高,其实是在桐树林的缘故,不过放松和学习不可兼得。
  • ​明日工作计划:
    • 图书馆完成双向链表和快慢指针的Java实操,Java这方面的语法和C++确实非常接近。
  • ​今日学习成长:​
    • 语法和数据结构,在实践当中理解Java的类的管理。

三、备注

四、Java容器了解

C++ STL = 模板 + 值语义
Java 容器 = 泛型 + 引用语义

还是类比C++ 的STL容器,准确来说,Java的使用在工程上,不会使用基础语法当中的数组等方法的,而是使用封装完毕的Java容器,实现增删查改。

https://docs.oracle.com/javase/8/docs/api/java 这是Javase的官方文档,还是建议直接看官方文档,慢慢习惯英语即可。下面我将开始上手文档,还有简单了解常见的数据结构在Java容器当中的封装。

1、List、Set、Map

接口 特点 常用实现类 场景建议
List 有序、可重复、有索引 ArrayList 默认首选。频繁随机访问数据时最快。
Set 允许重复、无序 HashSet 需要去重时使用(比如统计独立访客 IP)。
Map 键值对 (Key-Value) HashMap 需要快速查找(比如通过 ID 找用户信息)。
这里分别是数组,集合,图的数据结构,所以我需要快速了解这几个数据结构的定义:

List:你需要一个“动态数组”

  • ArrayList (首选):底层是数组。查询极快(get(index) 是常数时间),但在中间插入或删除元素较慢(需要移动数据)。
  • LinkedList:底层是链表。适合频繁在头部/中间插入或删除,但随机访问速度慢。

Set:你需要“去重”或“检查是否存在”

  • HashSet (首选):基于哈希表,速度极快,但不保证存储顺序。
  • TreeSet:元素会自动排序(按自然顺序或指定比较器)。
  • LinkedHashSet:既能去重,又能记住你插入的顺序。

Map:你需要“查字典”或“键值对应”

  • HashMap (首选):查找、插入都是瞬时完成。
  • TreeMap:按 Key 的顺序自动排序。

综上,可以看出来,这上面的数据结构,本质上使用的是下面的几个基本类型:

数据、链表、哈希表、树。关于哈希表我存储我知之甚少,所以需要打一下基础。至于数组是我们最先学习的,读取非常快,可是插入数据非常慢,因为要平移数据,这个是非常清楚的,可是关于二叉树的高级用法,还有哈希表的高级用法,我需要快速学习一下:

特性 ArrayList (数组) HashSet (哈希表)
底层实现 Object[] HashMap (数组 + 链表/红黑树)
查询效率 极快($O(1)$,通过下标) 极快($O(1)$,通过 Hash 算位置)
插入效率 尾部快,中间慢 很快(不需要移动其他元素)
数据顺序 保证顺序 不保证顺序
去重能力 无(可重复) (通过 Hash 和 Equals 判定)

2、哈希表原理

要学会哈希表就要学会红黑树,要学会红黑树就要学会二叉树的原理。

背景: 我使用C++在学习数据结构和算法的时候,使用的是STL容器的,vector方法,vector编写二叉树红黑树的原理,还是非常丝滑的,现在使用Java了,因此我需要学会Java的“vector”。

功能 C++ (STL) Java (Collections)
动态数组 vector<int> v List<Integer> list = new ArrayList<>();
哈希表 unordered_map<K, V> HashMap<K, V> map = new HashMap<>();
有序映射 map<K, V> (红黑树) TreeMap<K, V> map = new TreeMap<>();
取值 v[i]m[key] list.get(i)map.get(key)
判空/大小 empty(), size() isEmpty(), size()
我打算从链表开始实现一遍,顺便快速上手Java的数据结构。

然后使用Java编写二叉树的原理,不使用现成的库,就是用List的方法进行编写,把二叉树,红黑树,哈希表的原理都编写一遍,在了解其底层原理之后,开始快手上手其简单封装用法,所以我下面将梳理Java的最基本的数据结构–链表。

3、链表任务分解

第一阶段:定义“灵魂”—— Node 节点
第二阶段:必须亲手实现的四大基本功
第三阶段:Java 特有的“避坑”指南
第四阶段:力扣(LeetCode)实战秘籍

  • 快慢指针
  • 虚拟头节点 (Dummy Head)

因为Java没有指针这个说法,因此我在学习的时候,使用this的用法,感觉和python差不多。

A、MyLinkedList手写

addAtHead, addAtTail, deleteAtIndex 等方法,完成力扣一下了例题:

  • 206. 反转链表 (入门必做)
  • 203. 移除链表元素 (练习虚拟头节点)
  • 141. 环形链表 (练习快慢指针)
package io.github.luduihang.learning.list;  
  
class ListNode {  
    int val;  
    ListNode next;  
    ListNode(int val) {this.val = val;}  
}  
  
public class MyLinkedList {  
    public static void main(String[] args) {  
        ListNode n1 = new ListNode(1);  
        ListNode n2 = new ListNode(2);  
        ListNode n3 = new ListNode(3);  
        n1.next = n2;  
        n2.next = n3;  
        n3.next = null;  
        printList(n1);  
        ListNode rev = reverseList(n1);  
        printList(rev);  
        rev = addNode(rev, 0);  
        printList(rev);  
        System.out.println(deleteNode(rev, 6));  
        printList(rev);  
    }  
  
    static void printList(ListNode head) {  
        ListNode curr = head;  
        while (curr != null) {  
            System.out.print(curr.val + " -> ");  
            curr = curr.next;  
        }  
        System.out.print("null");  
        System.out.println();  
    }  
  
    static ListNode reverseList(ListNode head) {  
        ListNode preNode = null;  
        ListNode curr = head;  
        while (curr != null) {  
           ListNode nextNode = curr.next;  
           curr.next = preNode;  
           preNode = curr;  
           curr = nextNode;  
        }  
        return preNode;  
    }  
  
    static ListNode addNode(ListNode head, int val) {  
        if (head == null) {  
            head = new ListNode(val);  
        }  
        else {  
            ListNode curr = head;  
            while (curr.next != null) {  
                curr = curr.next;  
            }  
            curr.next = new ListNode(val);  
        }  
        return head;  
    }  
  
    static boolean deleteNode(ListNode head, int val) {  
        ListNode virtual = new ListNode (-1);  
        virtual.next = head;  
        ListNode curr = virtual;  
        while (curr.next != null) {  
            if (curr.next.val == val) {  
                curr.next = curr.next.next;  
                return true;  
            }  
            else {  
                curr = curr.next;  
            }  
        }  
        return false;  
    }  
}

B、静态关键词使用

在Java中,方法(函数)分为两种:

查看你的MyLinkedList类:

public class MyLinkedList {
    public static void main(String[] args) {
        // ...
        printList(n1);  // ❌ 错误:尝试直接调用实例方法
    }
    
    void printList(ListNode head) {  // 实例方法(没有static)
        // ...
    }
    
    ListNode reverseList(ListNode head) {  // 实例方法(没有static)
        // ...
    }
}

问题原因main方法是static的,但printListreverseList是实例方法。在静态方法中,不能直接调用实例方法,因为:

  1. 静态方法属于类,不需要对象就能调用
  2. 实例方法属于对象,需要先有对象才能调用
  3. 在静态的main方法中,没有MyLinkedList的实例(对象)

方案1:将方法改为静态(推荐用于工具类)

static void printList(ListNode head) {  // 添加static关键字
    // ...
}
static ListNode reverseList(ListNode head) {  // 添加static关键字
    // ...
}

优点:简单直接,不需要创建对象
缺点:如果方法需要访问类的实例变量就不行(但你的方法不需要)

方案2:创建对象实例

public static void main(String[] args) {
    MyLinkedList list = new MyLinkedList();  // 创建对象
    // ...
    list.printList(n1);  // 通过对象调用
    ListNode rev = list.reverseList(n1);
    list.printList(rev);
}

优点:保持了方法的实例特性,符合面向对象设计
缺点:需要多写一行创建对象的代码

对于你的MyLinkedList类,由于printListreverseList只是操作ListNode参数,不访问类的实例变量,我推荐方案1(改为静态方法)。这样代码更简洁,符合工具类的设计。

C、静态关键词 C++/Java区别

关键差异:在 C++ 中,你习惯把工具函数写在类外面作为全局函数。但在 Java 中,没有类外函数。如果你想写一个“工具函数”(比如反转链表),你必须把它写在类里,并加上 static 来模拟全局函数的行为。

之所以我在 Java 里感到“难受”,是因为 Java 强制要求所有代码必须写在 class 里,包括程序入口 main

使用 static 的场景:工具属性

如果这个方法只依赖于传入的参数,不依赖类里的其他成员变量。

  • 例子reverseList(ListNode head)。它只管处理传入的 head,不管 MyLinkedList 类里现在有多少个元素。
  • 地位:它更像是一个“函数式”的工具。

D、单向链表函数梳理

public static void main(String[] args)
static void printList(ListNode head)
static ListNode reverseList(ListNode head)
static ListNode addNode(ListNode head, int val)
static boolean deleteNode(ListNode head, int val)

以上函数是常见的函数,对于链表的节点,明天我尝试双向链表,如果时间有余,尝试二叉树的快速上手,特别是后天关于红黑树需要系统性的上手,这是充满智慧的。

五、无筛选视频网站

检索南京大屠杀的视频的时候,这个链接比较好看:
https://www.dailymotion.com/video/x2toixb


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 kipleyarch@gmail.com
Obsidian