讲师中心 微信公众号
AI工具推荐 视频效率加速

Java中Collections.frequency方法解析

轻瑶姑娘_8614

轻瑶姑娘_8614

发布时间:2025-09-17 19:26:01

|

392人浏览过

|

来源于php中文网

原创

Collections.frequency用于统计集合中某元素出现次数,其依赖equals方法判断相等,处理自定义对象需重写equals和hashCode,性能为O(n),适用于小到中型集合;对于大数据量或频繁查询场景,使用HashMap或Stream API构建频率映射更高效,可将查询时间降为O(1)。

java中collections.frequency方法解析

Collections.frequency
方法在Java中,说白了,就是用来统计某个特定对象在集合(
Collection
)中出现了多少次的工具。它提供了一个简洁的API,让我们不用自己写循环就能完成这个常见的计数任务。

解决方案

Java.util.Collections
类中的
frequency(Collection<?> c, Object o)
方法,其核心功能就是遍历给定的集合
c
,然后使用
o.equals(element)
来判断集合中的每个元素
element
是否与目标对象
o
相等。每当找到一个相等的元素,计数器就会加一。最终,它返回这个计数器的值。

这个方法设计得非常直观。比如你有一个字符串列表,想知道“Apple”出现了几次,直接调用

Collections.frequency(myList, "Apple")
就行了。它会老老实实地从头到尾检查一遍列表里的每一个元素。

值得注意的是,它依赖于对象的

equals()
方法。如果你的集合里存的是自定义对象,而你又没有正确地重写
equals()
方法,那么
frequency
的结果可能就不是你想要的了,它可能会因为默认的引用比较而误判。另外,这个方法也能很好地处理
null
值,如果你的集合里有
null
,而你传入
null
作为目标对象,它也能准确地告诉你集合里有多少个
null
。

立即学习“Java免费学习笔记(深入)”;

从性能角度看,

Collections.frequency
的时间复杂度是 O(n),其中 n 是集合的大小。因为它需要遍历整个集合。对于小型或中型集合,这通常不是问题,但如果集合非常大,并且你需要频繁地查询不同元素的频率,那么可能需要考虑更高效的数据结构。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class FrequencyExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Apple");
        fruits.add(null);
        fruits.add("Banana");
        fruits.add(null);

        // 统计 "Apple" 出现的次数
        int appleCount = Collections.frequency(fruits, "Apple");
        System.out.println("Apple 出现的次数: " + appleCount); // 输出 3

        // 统计 "Banana" 出现的次数
        int bananaCount = Collections.frequency(fruits, "Banana");
        System.out.println("Banana 出现的次数: " + bananaCount); // 输出 2

        // 统计 "Grape" 出现的次数 (不存在)
        int grapeCount = Collections.frequency(fruits, "Grape");
        System.out.println("Grape 出现的次数: " + grapeCount); // 输出 0

        // 统计 null 出现的次数
        int nullCount = Collections.frequency(fruits, null);
        System.out.println("null 出现的次数: " + nullCount); // 输出 2

        // 尝试统计一个不存在的自定义对象
        class MyObject {
            String name;
            MyObject(String name) { this.name = name; }
            // 没有重写 equals() 和 hashCode()
        }
        List<MyObject> myObjects = new ArrayList<>();
        myObjects.add(new MyObject("A"));
        myObjects.add(new MyObject("B"));
        myObjects.add(new MyObject("A"));
        int myObjectACount = Collections.frequency(myObjects, new MyObject("A"));
        System.out.println("MyObject(\"A\") 出现的次数 (未重写 equals): " + myObjectACount); // 输出 0,因为是不同对象引用
    }
}

Collections.frequency
与手动遍历计数有何区别?性能如何?

在我看来,

Collections.frequency
和我们自己写一个
for
循环去遍历集合然后计数,在本质上是做同一件事。主要的区别在于代码的简洁性和可读性。

手动遍历计数通常是这样的:

public static int countManually(List<String> list, String target) {
    int count = 0;
    for (String item : list) {
        if (target == null ? item == null : target.equals(item)) {
            count++;
        }
    }
    return count;
}

而

Collections.frequency
则将这些细节封装起来,你只需要调用一个方法就行了。从开发者的角度来看,这无疑更优雅,也减少了出错的可能性,毕竟标准库的方法经过了充分的测试。

至于性能,坦白说,对于大多数日常应用场景,两者的差异微乎其微,几乎可以忽略不计。它们都是 O(n) 的时间复杂度,这意味着它们的执行时间会随着集合大小的线性增长。

Collections.frequency
内部也是一个循环,只不过是Java标准库帮你写的。理论上,方法调用会有一些轻微的开销,但现代JVM的优化能力非常强,很多时候这种差异会被即时编译(JIT)优化掉。

我个人更倾向于使用

Collections.frequency
,因为它更符合“使用标准库提供的功能”这一最佳实践。它让代码意图更清晰,也避免了重复造轮子。除非你真的遇到了性能瓶颈,并且通过分析器(profiler)确认
Collections.frequency
是瓶颈所在,否则真的没必要去手动实现。大多数时候,过早的优化反而会牺牲代码的可读性和维护性。

OpenJDK 24
OpenJDK 24

OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。

下载

处理 null 值或自定义对象时,
Collections.frequency
有什么注意事项?

处理

null
值时,
Collections.frequency
的行为非常明确且符合预期。如果你要查找的对象是
null
,它会遍历集合,统计所有
null
元素的数量。这在使用上很方便,比如你可能需要知道某个列表中有多少条数据是缺失的(用
null
表示)。

List<String> items = new ArrayList<>();
items.add("A");
items.add(null);
items.add("B");
items.add(null);
int nullCount = Collections.frequency(items, null); // nullCount 为 2
System.out.println("null 元素的数量: " + nullCount);

但是,当涉及到自定义对象时,事情就有点儿意思了,这里面的“坑”主要集中在

equals()
方法上。
Collections.frequency
内部是使用
o.equals(element)
来判断两个对象是否相等的。

如果你的自定义类没有重写

equals()
方法,它就会继承
Object
类的默认
equals()
方法,而这个方法比较的是对象的内存地址,也就是引用相等性(
obj1 == obj2
)。这意味着,即使两个自定义对象的所有属性值都一样,但如果它们是不同的实例(即内存地址不同),
Collections.frequency
也会认为它们不相等。

class Product {
    String name;
    double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    // 没有重写 equals()
}

List<Product> products = new ArrayList<>();
products.add(new Product("Laptop", 1200.0));
products.add(new Product("Mouse", 25.0));
products.add(new Product("Laptop", 1200.0)); // 另一个实例,但属性相同

// 尝试查找一个属性相同的 Product 对象
int laptopCount = Collections.frequency(products, new Product("Laptop", 1200.0));
System.out.println("Laptop 出现的次数 (未重写 equals): " + laptopCount); // 结果通常是 0

要让

Collections.frequency
正确地统计自定义对象,你必须在你的自定义类中重写
equals()
方法,并确保它实现了你期望的“相等”逻辑(通常是基于对象属性的比较)。同时,根据Java的约定,重写
equals()
方法时,也应该同时重写
hashCode()
方法,尽管
frequency
方法本身不直接使用
hashCode()
,但为了保持与其他基于哈希的集合(如
HashMap
,
HashSet
)的兼容性和正确性,这是非常重要的。

class ProductWithEquals {
    String name;
    double price;

    public ProductWithEquals(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProductWithEquals that = (ProductWithEquals) o;
        return Double.compare(that.price, price) == 0 &&
               name.equals(that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

List<ProductWithEquals> productsWithEquals = new ArrayList<>();
productsWithEquals.add(new ProductWithEquals("Laptop", 1200.0));
productsWithEquals.add(new ProductWithEquals("Mouse", 25.0));
productsWithEquals.add(new ProductWithEquals("Laptop", 1200.0)); // 另一个实例,但属性相同

int laptopCountCorrect = Collections.frequency(productsWithEquals, new ProductWithEquals("Laptop", 1200.0));
System.out.println("Laptop 出现的次数 (重写 equals): " + laptopCountCorrect); // 结果现在是 2

所以,在使用

Collections.frequency
统计自定义对象时,切记检查你的
equals()
和
hashCode()
实现。这是避免意外结果的关键。

除了
Collections.frequency
,Java 还有哪些更高效的计数方法,尤其是在大数据量场景下?

Collections.frequency
对于一次性、小到中等规模的集合计数确实很方便。但正如前面提到的,它的 O(n) 复杂度意味着如果你需要对同一个大集合进行多次不同元素的频率查询,或者集合本身就非常庞大,那么每次都全量遍历一遍效率就会很低。在这种情况下,Java提供了更高效的替代方案。

我个人觉得,最常用的高效计数方法是使用

Map
来存储元素的频率。这种方法的核心思想是:遍历集合一次,将每个元素作为
Map
的键,其出现的次数作为
Map
的值。

import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;

// ... (假设有前面的 List<String> fruits 集合)

public class EfficientCountingExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("Apple", "Banana", "Apple", "Orange", "Apple", null, "Banana", null);

        // 方法一:使用 HashMap 手动计数
        Map<String, Integer> frequencyMap = new HashMap<>();
        for (String fruit : fruits) {
            frequencyMap.put(fruit, frequencyMap.getOrDefault(fruit, 0) + 1);
        }
        System.out.println("HashMap 手动计数结果: " + frequencyMap);
        // 之后查询 "Apple" 的次数就是 O(1)
        System.out.println("Apple 出现次数 (Map查询): " + frequencyMap.get("Apple"));

        // 方法二:使用 Java 8 Stream API
        // 注意:Stream API 默认不会将 null 作为 key,或者需要特殊处理
        Map<String, Long> streamFrequencyMap = fruits.stream()
                                                    .filter(f -> f != null) // 过滤掉 null,或者根据需求处理
                                                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        System.out.println("Stream API 计数结果 (不含null): " + streamFrequencyMap);
        System.out.println("Apple 出现次数 (Stream Map查询): " + streamFrequencyMap.get("Apple"));

        // 如果要包含 null,可以这样处理:
        Map<String, Long> streamFrequencyMapWithNull = fruits.stream()
                .collect(Collectors.groupingBy(f -> f == null ? "NULL_KEY" : f, Collectors.counting()));
        System.out.println("Stream API 计数结果 (含null,用特殊键表示): " + streamFrequencyMapWithNull);
        System.out.println("null 出现次数 (Stream Map查询): " + streamFrequencyMapWithNull.get("NULL_KEY"));
    }
}

HashMap 手动计数:

  • 优点:只需要遍历集合一次(O(n)),构建
    Map
    。之后对任何元素的频率查询都是 O(1) 的平均时间复杂度。对于需要多次查询不同元素频率的场景,这种方式的整体效率远高于多次调用
    Collections.frequency
    。
  • 缺点:需要额外的内存来存储
    Map
    。

Java 8 Stream API:

  • 优点:代码非常简洁和声明式,可读性高。同样只需要一次遍历。特别适合函数式编程风格。
  • 缺点:对于非常小的集合,Stream API 的启动开销可能略大于传统循环。处理
    null
    值时需要稍微注意,比如上面例子中用
    filter
    过滤掉
    null
    ,或者用一个特殊的字符串作为
    null
    的键。

选择建议:

  • Collections.frequency
    :适用于你只需要查询一两个元素的频率,且集合规模不是特别大的情况。它代码最少,最直接。
  • HashMap
    手动计数
    :当你需要对同一个集合进行多次频率查询,或者集合规模较大,对查询效率有较高要求时。这是性能和灵活性的良好平衡。
  • Java 8 Stream API:如果你喜欢函数式编程风格,并且项目已经在使用 Java 8 及以上版本,Stream API 是一个非常优雅的选择,它能用更少的代码实现与
    HashMap
    类似的高效计数。

在我看来,掌握这几种计数方法,能让你在不同的场景下灵活选择最合适的工具,写出更高效、更健壮的Java代码。很多时候,选择不是非黑即白,而是根据实际需求和上下文来做权衡。

热门AI工具

更多
咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1678

2024.03.01

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1718

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2664

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6044

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

5609

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

7274

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

10554

2024.04.29

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

20

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn