首页 Java java教程 提升您的 Java 后端性能:基本的优化技巧!

提升您的 Java 后端性能:基本的优化技巧!

Nov 11, 2024 pm 09:56 PM

Boost Your Java Back-End Performance: Essential Optimization Tips!

性能对于软件项目的成功起着至关重要的作用。 Java 后端开发过程中应用的优化可确保系统资源的有效利用并提高应用程序的可扩展性。

在本文中,我将与您分享一些我认为很重要的优化技巧,以避免常见错误。

选择正确的数据结构以提高性能

选择高效的数据结构可以显着提高应用程序的性能,尤其是在处理大型数据集或时间关键型操作时。使用正确的数据结构可以最大限度地减少访问时间、优化内存使用并减少处理时间。

例如,当您需要在列表中频繁搜索时,使用 HashSet 而不是 ArrayList 可以产生更快的结果:

// Inefficient - O(n) complexity for contains() check
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// Checking if "Alice" exists - time complexity O(n)
if (names.contains("Alice")) {
    System.out.println("Found Alice");
}

// Efficient - O(1) complexity for contains() check
Set<String> namesSet = new HashSet<>();
namesSet.add("Alice");
namesSet.add("Bob");
// Checking if "Alice" exists - time complexity O(1)
if (namesSet.contains("Alice")) {
    System.out.println("Found Alice");
}
登录后复制
登录后复制
登录后复制

在此示例中,HashSet 为 contains() 操作提供的平均时间复杂度为 O(1),而 ArrayList 需要 O(n),因为它必须迭代列表。因此,对于频繁查找,HashSet 比 ArrayList 更高效。

顺便说一下,如果你想知道什么是时间复杂度:时间复杂度是指算法的运行时间如何随输入大小的变化而变化。这有助于我们了解算法的运行速度,并通常显示它在最坏情况下的行为方式。时间复杂度通常用大 O 表示法表示。

在开始时检查异常情况

您可以通过检查方法中要使用的字段(在方法开头不应为空)来避免不必要的处理开销。就性能而言,在开始时检查它们比在方法的后续步骤中检查空检查或非法条件更有效。

public void processOrder(Order order) {
    if (Objects.isNull(order))
        throw new IllegalArgumentException("Order cannot be null");
    if (order.getItems().isEmpty())
        throw new IllegalStateException("Order must contain items");

    ...

    // Process starts here.
    processItems(order.getItems());
}
登录后复制
登录后复制
登录后复制

如本例所示,在进入 processItems 方法之前,该方法可能包含其他进程。在任何情况下,processItems 方法都需要 Order 对象中的 Items 列表才能工作。您可以通过在流程开始时检查条件来避免不必要的处理。

避免创建不必要的对象

在 Java 应用程序中创建不必要的对象可能会增加垃圾收集时间,从而对性能产生负面影响。最重要的例子是字符串的使用。

这是因为Java中的String类是不可变的。这意味着每次新的 String 修改都会在内存中创建一个新对象。这可能会导致严重的性能损失,特别是在循环或执行多个串联时。

解决这个问题的最好方法是使用StringBuilder。 StringBuilder 可以修改它正在处理的 String 并在同一个对象上执行操作,而无需每次都创建新对象,从而获得更高效的结果。

例如,在下面的代码片段中,为每个串联操作创建一个新的 String 对象:

// Inefficient - O(n) complexity for contains() check
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// Checking if "Alice" exists - time complexity O(n)
if (names.contains("Alice")) {
    System.out.println("Found Alice");
}

// Efficient - O(1) complexity for contains() check
Set<String> namesSet = new HashSet<>();
namesSet.add("Alice");
namesSet.add("Bob");
// Checking if "Alice" exists - time complexity O(1)
if (namesSet.contains("Alice")) {
    System.out.println("Found Alice");
}
登录后复制
登录后复制
登录后复制

在上面的循环中,每个 result = 操作都会创建一个新的 String 对象。这会增加内存消耗和处理时间。

我们可以通过对 StringBuilder 执行相同的操作来避免不必要的对象创建。 StringBuilder 通过修改现有对象来提高性能:

public void processOrder(Order order) {
    if (Objects.isNull(order))
        throw new IllegalArgumentException("Order cannot be null");
    if (order.getItems().isEmpty())
        throw new IllegalStateException("Order must contain items");

    ...

    // Process starts here.
    processItems(order.getItems());
}
登录后复制
登录后复制
登录后复制

在这个例子中,使用StringBuilder只创建了一个对象,并在整个循环中对该对象进行操作。这样就完成了字符串操作,而无需在内存中创建新对象。

告别嵌套循环:使用 Flatmap

Java Stream API 中包含的 flatMap 函数是优化集合操作的强大工具。嵌套循环可能会导致性能损失和更复杂的代码。通过使用这种方法,您可以使代码更具可读性并提高性能。

  • flatMap 用法

map: 对每个元素进行操作并返回另一个元素作为结果。
flatMap:对每个元素进行操作,将结果转换为平面结构,提供更简单的数据结构。

在以下示例中,使用嵌套循环对列表执行操作。随着列表的扩大,操作会变得更加低效。

String result = "";
for (int i = 0; i < 1000; i++) 
    result += "number " + i;
登录后复制
登录后复制

通过使用 flatMap,我们可以摆脱嵌套循环并获得更干净、性能更高的结构。

StringBuilder result = new StringBuilder();

for (int i = 0; i < 1000; i++) 
    result.append("number ").append(i);

String finalResult = result.toString();
登录后复制

在这个例子中,我们使用 flatMap 将每个列表转换为扁平流,然后使用 forEach 处理元素。这种方法使代码更短并且性能更高效。

更喜欢轻量级 DTO 而不是实体

直接返回从数据库中提取的数据作为实体类可能会导致不必要的数据传输。无论从安全性还是性能角度来看,这都是一种非常错误的方法。相反,使用 DTO 仅返回所需的数据可以提高 API 性能并防止不必要的大数据传输。

List<List<String>> listOfLists = new ArrayList<>();
for (List<String> list : listOfLists) {
    for (String item : list) {
        System.out.println(item);
    }
}
登录后复制

说到数据库;使用 EntityGraph、索引和延迟获取功能

数据库性能直接影响应用程序的速度。在相关表之间检索数据时,性能优化尤其重要。此时,您可以通过使用 EntityGraph 和 Lazy Fetching 来避免不必要的数据加载。同时,数据库查询中正确的索引可以极大地提高查询性能。

  • 使用 EntityGraph 优化数据提取

EntityGraph 允许您控制数据库查询中的关联数据。您只提取所需的数据,避免急切获取的成本。

Eager Fetching 是指相关表中的数据自动随查询而来。

// Inefficient - O(n) complexity for contains() check
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// Checking if "Alice" exists - time complexity O(n)
if (names.contains("Alice")) {
    System.out.println("Found Alice");
}

// Efficient - O(1) complexity for contains() check
Set<String> namesSet = new HashSet<>();
namesSet.add("Alice");
namesSet.add("Bob");
// Checking if "Alice" exists - time complexity O(1)
if (namesSet.contains("Alice")) {
    System.out.println("Found Alice");
}
登录后复制
登录后复制
登录后复制

在此示例中,在同一查询中检索地址相关数据和用户信息。避免不必要的额外查询。

  • 通过延迟获取避免不必要的数据

与 Eager Fetch 不同,Lazy Fetch 仅在需要时才从相关表中获取数据。

public void processOrder(Order order) {
    if (Objects.isNull(order))
        throw new IllegalArgumentException("Order cannot be null");
    if (order.getItems().isEmpty())
        throw new IllegalStateException("Order must contain items");

    ...

    // Process starts here.
    processItems(order.getItems());
}
登录后复制
登录后复制
登录后复制
  • 通过索引提高性能

索引是提高数据库查询性能最有效的方法之一。数据库中的表由行和列组成,在进行查询时,常常需要扫描所有行。索引加快了这一过程,使数据库能够更快地搜索特定字段。

使用缓存减轻查询负载

缓存是将经常访问的数据或计算结果临时存储在快速存储区域(例如内存)中的过程。缓存的目的是当再次需要数据或计算结果时更快地提供这些信息。尤其是在计算成本较高的数据库查询和事务中,使用缓存可以显着提高性能。

Spring Boot 的 @Cacheable 注解让缓存的使用变得非常简单。

String result = "";
for (int i = 0; i < 1000; i++) 
    result += "number " + i;
登录后复制
登录后复制

在此示例中,当第一次调用 findUserById 方法时,将从数据库中检索用户信息并将其存储在缓存中。当再次需要相同的用户信息时,会从缓存中检索,而不去数据库。

您还可以根据项目的需要使用Redis等顶级缓存解决方案。

结论

您可以使用这些优化技术开发更快、更高效且可扩展的应用程序,特别是在使用 Java 开发的后端项目中。

...

感谢您阅读我的文章!如果您有任何问题、反馈或想法想要分享,我很乐意在评论中听到它们。

您可以在 Dev.to 上关注我,以获取有关此主题和我的其他帖子的更多信息。不要忘记喜欢我的帖子,以帮助他们接触到更多人!

谢谢你??

在 LinkedIn 上关注我:tamerardal

资源:

  1. Baeldung,JPA 实体图
  2. Baeldung,Spring 缓存指南
  3. Baeldung,Hibernate 中的急切/延迟加载

以上是提升您的 Java 后端性能:基本的优化技巧!的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1677
14
CakePHP 教程
1431
52
Laravel 教程
1334
25
PHP教程
1279
29
C# 教程
1257
24
公司安全软件导致应用无法运行?如何排查和解决? 公司安全软件导致应用无法运行?如何排查和解决? Apr 19, 2025 pm 04:51 PM

公司安全软件导致部分应用无法正常运行的排查与解决方法许多公司为了保障内部网络安全,会部署安全软件。...

如何将姓名转换为数字以实现排序并保持群组中的一致性? 如何将姓名转换为数字以实现排序并保持群组中的一致性? Apr 19, 2025 pm 11:30 PM

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

如何使用MapStruct简化系统对接中的字段映射问题? 如何使用MapStruct简化系统对接中的字段映射问题? Apr 19, 2025 pm 06:21 PM

系统对接中的字段映射处理在进行系统对接时,常常会遇到一个棘手的问题:如何将A系统的接口字段有效地映�...

如何优雅地获取实体类变量名构建数据库查询条件? 如何优雅地获取实体类变量名构建数据库查询条件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本启动Spring...

Java对象如何安全地转换为数组? Java对象如何安全地转换为数组? Apr 19, 2025 pm 11:33 PM

Java对象与数组的转换:深入探讨强制类型转换的风险与正确方法很多Java初学者会遇到将一个对象转换成数组的�...

电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? 电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? Apr 19, 2025 pm 11:27 PM

电商平台SKU和SPU表设计详解本文将探讨电商平台中SKU和SPU的数据库设计问题,特别是如何处理用户自定义销售属...

如何利用Redis缓存方案高效实现产品排行榜列表的需求? 如何利用Redis缓存方案高效实现产品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis缓存方案如何实现产品排行榜列表的需求?在开发过程中,我们常常需要处理排行榜的需求,例如展示一个�...

See all articles