万字详解 | Java中length、length()、size()的“爱恨情仇“(附高频面试题)

「灵魂拷问」 为什么数组用length,字符串用length(),集合却用size()?这看似简单的三个方法背后,藏着Java设计者的哪些深意?本文将带你深入源码,解密这三个"尺寸测量器"的本质区别!


🎯 三者的本质区别(速查表)

特性数组.lengthString.length()Collection.size()
数据类型属性方法方法
可变性固定不变字符串不可变动态变化
时间复杂度O(1)O(1)多数集合O(1)
底层实现JVM直接支持char数组长度集合内部计数器
空值处理不会为nullNPE风险可能返回0
适用场景基础数组字符串所有集合类

📦 数组的length属性:最原始的长度测量

1.1 基本用法

int[] numbers = new int[5];
System.out.println(numbers.length); // 输出5

String[][] matrix = new String[3][4];
System.out.println(matrix.length);   // 3(第一维长度)
System.out.println(matrix[0].length);// 4(第二维长度)

1.2 核心特性

  • 编译期确定:数组长度在初始化时确定,无法修改
  • 内存直接访问:JVM通过数组对象头直接获取长度
  • 多维数组的陷阱:每个维度的length独立计算

1.3 底层原理(HotSpot源码解析)

// arrayOop.hpp
class arrayOopDesc : public oopDesc {
  ...
  int length() const { 
    return *(int*)(((char*)this) + length_offset_in_bytes()); 
  }
  ...
}

数组对象在内存中的布局:

| 对象头 | 长度(4字节) | 元素数据... |

📜 String的length()方法:字符序列的守护者

2.1 基础应用

String str = "Hello世界";
System.out.println(str.length()); // 输出7(注意中文占1个char!)

String empty = "";
System.out.println(empty.length()); // 0

String nullStr = null;
System.out.println(nullStr.length()); // 抛出NullPointerException

2.2 关键知识点

  • Unicode代码单元:一个char不一定等于一个Unicode字符
  • 不可变性:字符串长度在创建后不可改变
  • 内存优化:JDK9后使用byte[]存储(LATIN1/UTF-16编码)

2.3 源码追踪(JDK17)

public final class String {
    private final byte[] value;
    private final byte coder; // 0-LATIN1, 1-UTF16
    
    public int length() {
        return value.length >> coder; // 妙用位运算!
    }
}

编码方式对长度计算的影响:

  • LATIN1编码:每个字符1字节,直接返回数组长度
  • UTF-16编码:右移1位(相当于除以2)

🗃 集合的size()方法:动态容器的智能标尺

3.1 典型用法

List<String> list = new ArrayList<>();
System.out.println(list.size()); // 0

list.add("Java");
list.add("Python");
System.out.println(list.size()); // 2

Map<Integer, String> map = new HashMap<>();
map.put(1, "One");
System.out.println(map.size()); // 1

3.2 实现原理对比

集合类型size()实现原理时间复杂度
ArrayList维护size变量O(1)
LinkedList遍历节点计数(JDK8优化)O(n) → O(1)
ConcurrentHashMap分段计数求和O(1)
PriorityQueue维护size变量O(1)

3.3 线程安全陷阱

// 错误示例!
List<String> unsafeList = new ArrayList<>();
ExecutorService executor = Executors.newFixedThreadPool(10);

for (int i = 0; i < 1000; i++) {
    executor.submit(() -> {
        if (unsafeList.size() < 500) { // 这里size()可能被其他线程修改
            unsafeList.add("data");
        }
    });
}

正确做法:使用并发集合或同步块


💥 高频踩坑案例集锦

4.1 空指针三连击

// 案例1:未初始化的数组
int[] arr = null;
System.out.println(arr.length); // NullPointerException

// 案例2:未初始化的字符串
String s = null;
System.out.println(s.length()); // NullPointerException

// 案例3:集合size()的NPE保护
List<String> list = null;
System.out.println(list.size()); // NullPointerException

4.2 多维数组的迷宫

int[][] matrix = new int[3][];
matrix[0] = new int[2];
matrix[1] = new int[5];

System.out.println(matrix.length);    // 3
System.out.println(matrix[0].length); // 2
System.out.println(matrix[2].length); // NullPointerException

4.3 集合的"薛定谔的size"

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();

while (it.hasNext()) {
    String s = it.next();
    if (s.equals("B")) {
        list.remove(s); // 抛出ConcurrentModificationException
    }
}

正确姿势:使用迭代器的remove方法


🚀 性能优化指南

5.1 数组length的缓存技巧

// 优化前
for (int i = 0; i < array.length; i++) { ... }

// 优化后(适用于超长数组)
int len = array.length;
for (int i = 0; i < len; i++) { ... }

实测性能提升约5%(JMH测试)

5.2 字符串长度的预处理

// 需要频繁调用length()时
String bigString = "..."; // 超长字符串
int len = bigString.length();

for (int i = 0; i < len; i++) {
    // 避免每次循环都调用length()
}

5.3 集合size()的智能判断

// 低效写法
if (collection.size() > 0) { ... }

// 高效写法
if (!collection.isEmpty()) { ... }

原理:isEmpty()通常直接返回size() == 0,但部分集合有优化实现


📚 高频面试题解析

Q1:为什么数组用属性而字符串用方法?

参考回答

  • 数组是Java的基础数据结构,length作为属性可以提升访问效率
  • 字符串的length需要封装计算逻辑(如JDK9后的压缩存储)
  • 符合面向对象设计原则(封装变化)

Q2:如何正确获取集合的实时大小?

参考答案

  • 对非并发集合:需要同步块保护
  • 推荐使用并发集合的size()方法
  • 注意size()的时间复杂度(如LinkedList在JDK8前后的差异)

Q3:字符串length()是否等于字符数组长度?

参考答案

  • 对于普通字符串:是
  • 包含Unicode代理对时:可能不等
String emoji = "😀";
System.out.println(emoji.length());        // 输出2
System.out.println(emoji.codePointCount(0, emoji.length())); // 输出1

🌟 总结与最佳实践

  1. 数组.length:固定不变,直接访问
  2. String.length():注意编码影响,防范NPE
  3. Collection.size():关注线程安全,优选isEmpty()

黄金法则

  • 操作数组前检查null
  • 字符串处理注意Unicode
  • 集合size()要配合同步机制

「实战建议」 在IDEA中安装"SonarLint"插件,可以自动检测length相关常见错误!


✍️ 本文持续修订于:2023-08-20
🏷️ 相关标签:#Java基础 #集合框架 #数组 #字符串 #性能优化
🔗 延伸阅读

📢 互动话题:你在开发中遇到过哪些关于length的"坑"?欢迎在评论区分享你的故事!如果觉得本文有帮助,请点赞⭐收藏支持作者~ 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值