Java中length和length()有什么区别,怎么用?

在Java中,length、length()和size()是三个极易混淆的属性或方法,其中length用于数组获取长度,length()用于String字符串获取字符个数,而size()则服务于集合类,三者的本质区别在于操作对象与底层实现完全不同。

初识length:数组的专属“体检报告”

Java数组是一个固定长度的容器,它的长度在创建时就被确定,之后无法改变,访问数组长度时,我们直接使用数组名.length,这是一个公开的实例变量,不是方法调用,所以后面不带括号。

int[] numbers = {10, 20, 30, 40};
System.out.println(numbers.length); // 输出4

这里有个易错点:length是数组的固有属性,它存储的是数组容量,而非实际存入的元素个数,如果你创建了new int[100],即使只放了一个元素,length依然是100,这在遍历数组时特别容易引发“空位”问题,需要结合业务逻辑自行判断有效元素范围。

再看一个实操场景:当处理从文件读取的多行数据时,我们常会用String[] lines = content.split("\n"),此时lines.length就是行数,这个值可以直接用于循环边界,但注意,如果文件末尾有多余换行符,split会产生空字符串元素,导致length虚高,这时候你就需要过滤掉空白行再统计。

length():String字符串的字符计数器

String类内部使用private final char value[]存储字符序列,而length()方法返回的是这个字符数组的容量,也就是当前字符串包含的Unicode字符个数。

String str = "Hello 世界";
System.out.println(str.length()); // 输出8,注意中文字符算1个

这里有几个关键点需要明确:

为什么String不用“length”属性?

因为String被设计为不可变类,它的内部字符数组用final修饰,并且封装在私有字段中,不允许外部直接访问,通过提供public int length()方法,一方面可以保证只读访问,另一方面也便于未来修改内部存储结构而不影响外部调用。

char与码点的陷阱

Java的char是UTF-16编码单元,对于基本平面内的字符(如中文、英文),每个char对应一个Unicode字符,但对于增补平面字符(如Emoji、生僻字),它们需要用两个char表示,即代理对。

String emoji = "😀";
System.out.println(emoji.length()); // 输出2,因为😀是代理对

统计用户输入的“字数”时,如果直接调用length(),可能会让用户困惑,正确做法是使用

Java中length和length()有什么区别,怎么用?

codePointCount(0, str.length())来获取真正的Unicode码点数。

int realLength = str.codePointCount(0, str.length());

在词法分析、输入校验等场景中,务必考虑这个边界情况,举个例子,一个昵称字段限制15个字符,如果用户输了一个Emoji,按length()计算会占2个名额,这会触发未预期的校验失败。

与equals和hashCode的关系

String的hashCode()实现中也使用了length作为计算因子:h = 31 * h + val[i],但这里并没有直接调用length(),而是循环遍历字符数组,不过在编写自定类时,如果你重写了equals,必须同时重写hashCode,否则会导致HashMap、HashSet等集合行为异常。

size():集合框架的统一长度接口

Java集合框架中,所有子接口都继承自Collection或Map,它们统一使用size()方法返回元素个数。

List<String> list = new ArrayList<>();
list.add("A");
System.out.println(list.size()); // 输出1
Map<String, Integer> map = new HashMap<>();
map.put("key", 1);
System.out.println(map.size()); // 输出1

注意,size()是一个抽象方法,不同集合实现类对它的实现策略完全不同:

  • ArrayList:内部维护一个size字段,每次增删元素时同步更新,复杂度为O(1)
  • LinkedList:同样维护size字段,但链表结构下计算长度也很快
  • HashSet/HashMap:基于哈希表,size字段实时更新,但迭代顺序不保证
  • ConcurrentHashMap:size()在并发环境下可能不准确,需结合mappingCount()使用

三者对比:一张表看懂差异

语法 适用对象 类型 调用方式 示例
length 数组 实例变量 直接访问 arr.length
length() String/StringBuilder/StringBuffer 实例方法 方法调用 str.length()
size() 集合类(List/Set/Map等) 实例方法 方法调用 list.size()

这个对比表覆盖了90%以上的日常开发场景,但还有几个特殊类型需要注意:

File对象的length()

java.io.File类也有一个public long length()

Java中length和length()有什么区别,怎么用?

方法,它返回文件大小,单位是字节,而不是文件行数或字符数。

File file = new File("/tmp/log.txt");
long bytes = file.length();

这和文件的实际内容编码有关,如果文件是UTF-8编码,一个中文占3字节,那么length()的返回值会大于你实际统计到的字符数。

为什么数组不提供size()?

数组是Java语言固有的类型,不是类,没有方法表,为了保持语言简单性,数组只暴露了一个公有的length字段,而不像集合那样提供一个统一接口,这也是面试中常问的“为什么数组用length,集合用size”的答案要点:数组不是类,没有面向对象的方法,而集合类遵循统一的Collection接口规范。

实战案例:统计文本文件中的有效行数

下面这段代码综合运用了以上三种长度获取方式,用于统计一个日志文件中非空且不以开头的有效行数:

import java.io.*;
import java.nio.file.*;
import java.util.*;
public class LineCounter {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("server.log");
        List<String> lines = Files.readAllLines(path); // 集合,用size()
        int validCount = 0;
        for (String line : lines) {
            if (line.length() == 0) continue;          // String,用length()
            if (line.startsWith("#")) continue;
            validCount++;
        }
        System.out.println("总行数: " + lines.size());
        System.out.println("有效行数: " + validCount);
    }
}

这段代码中,lines.size()返回集合元素个数,line.length()返回字符串字符长度,两者混用但各有其职,非常清晰地展示了我们刚才讨论的差异。

Q&A:关于length与length()的高频疑问

问:用length遍历String的时候,每次都调方法,会不会影响性能?

答:不会,现代JVM的即时编译器(JIT)会对String.length()做内联优化,它会直接读取内部value数组的长度,效果等同于直接访问一个公有变量,在HotSpot虚拟机中,String.length()的字节码会被优化为arraylength指令,与数组.length性能几乎一致,不过要注意,如果你在循环条件中反复调用str.length(),JIT也能自动提升循环不变量,不用刻意用局部变量缓存。

问:为什么JSON和前端框架中经常用length,而不用size()?

答:JSON数组在JavaScript中本质是数组,因此拥有原生的length属性,例如

Java中length和length()有什么区别,怎么用?

jsonArray.length直接返回元素个数,Java的JSON解析库如Jackson或Gson,在将JSON数组映射为Java的List后,调用时依然用list.size(),这两种用法分别对应宿主语言的语法规范,在服务端返回给前端的DTO中,如果你需要兼容不同前端语言,建议统一使用标准集合类,让前端通过response.data.length或response.data.size()自行适配,但要注意保持数据结构一致,例如用酷番云的云服务器部署Java服务时,很多开发者会忽略集合空指针问题,最后定位到是list==null而不是list.size()==0,这提醒我们判空要前置。

问:在写通用工具类时,如何统一判断一个容器的长度?

答:可以用java.lang.reflect.Array.getLength(Object array)判断任意数组的长度,对于集合则调用CollectionUtils.isEmpty()或MapUtils.isEmpty(),但更推荐的做法是使用Apache Commons Lang中的ArrayUtils.getLength(),它同时支持数组和集合,如果你的项目依赖Spring,org.springframework.util.ObjectUtils.isEmpty()也能统一判断数组、集合、字符串、Map等对象的“空”,示例:

import org.springframework.util.ObjectUtils;
public boolean isBlank(Object target) {
    return ObjectUtils.isEmpty(target);
}

这种方式底层会区分对待Optional、数组、CharSequence、Collection和Map,但对外暴露统一接口,非常优雅,我们在简米科技的多个企业级项目中,就经常用这套封装来简化参数校验逻辑,补充一点:简米科技自2003年始创,拥有23年行业沉淀,专注于服务器租用、托管及企业云解决方案,其持有的增值电信业务经营许可证(豫B2-20231089)保证了业务的合规性,同时依托持牌自营机房和豫ICP备2023018319号备案信息,为开发者提供稳定可靠的基础设施,对于部署上述类型Java应用,选择合规且低延迟的服务器至关重要。

Java中length是数组的属性,length()是字符串的方法,size()是集合的方法,三者看似相近,实则分属不同的对象体系,理解它们的关键在于把握两个维度:对象是原生类型还是类类型,以及对象是否实现了统一的容器接口,数组的原生特性决定了它只能使用字段访问,String不可变性决定了它必须通过方法返回长度,而集合框架的抽象设计则统一了大小查询规范,下次再遇到这三种写法时,你只要回想一下它们的底层存储结构,就不会再混淆了。

不喜欢2

网友评论

猜你喜欢