Java处理JSON编程实用技巧

Java处理JSON编程实用技巧

1. 前言

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。在Java开发中,JSON处理是一项非常常见且重要的任务。本文将详细介绍Java中处理JSON的各种实用技巧,包括主流JSON框架的使用、性能优化以及最佳实践。

本文将重点介绍Gson、Jackson和Fastjson这三个主流Java JSON处理库的使用技巧和性能优化方法。

2. JSON处理框架对比

Java生态中有多个优秀的JSON处理框架,每个框架都有其特点和适用场景。下面是三个主流框架的对比:

3. Gson使用技巧

3.1 基础用法

Gson是Google开发的Java库,用于将Java对象转换为JSON表示,以及将JSON字符串转换回等效的Java对象。

3.1.1 Maven依赖
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> </dependency>
3.1.2 基本序列化与反序列化
// 创建Gson实例 Gson gson = new Gson(); // Java对象转JSON字符串 User user = new User("张三", 25); String json = gson.toJson(user); System.out.println(json); // {"name":"张三","age":25} // JSON字符串转Java对象 String jsonString = "{\"name\":\"李四\",\"age\":30}"; User parsedUser = gson.fromJson(jsonString, User.class); System.out.println(parsedUser.getName()); // 李四
3.1.3 集合类型的序列化与反序列化
// 序列化集合 List<User> userList = Arrays.asList( new User("张三", 25), new User("李四", 30) ); String jsonList = gson.toJson(userList); // 反序列化集合 - 方法1:使用TypeToken Type userListType = new TypeToken<List<User>>(){}.getType(); List<User> parsedList = gson.fromJson(jsonList, userListType); // 反序列化集合 - 方法2:先转为数组再转集合 User[] userArray = gson.fromJson(jsonList, User[].class); List<User> userList2 = Arrays.asList(userArray);

3.2 高级特性

3.2.1 GsonBuilder配置
Gson gson = new GsonBuilder() .setPrettyPrinting() // 格式化输出 .serializeNulls() // 序列化null值 .setDateFormat("yyyy-MM-dd HH:mm:ss") // 设置日期格式 .excludeFieldsWithoutExposeAnnotation() // 只序列化有@Expose注解的字段 .registerTypeAdapter(Date.class, new DateSerializer()) // 注册自定义类型适配器 .disableHtmlEscaping() // 禁用HTML转义 .create();
3.2.2 自定义序列化和反序列化
// 自定义序列化器 public class UserSerializer implements JsonSerializer<User> { @Override public JsonElement serialize(User user, Type type, JsonSerializationContext context) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("userName", user.getName()); // 字段名映射 jsonObject.addProperty("userAge", user.getAge()); return jsonObject; } } // 自定义反序列化器 public class UserDeserializer implements JsonDeserializer<User> { @Override public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); String name = jsonObject.get("userName").getAsString(); int age = jsonObject.get("userAge").getAsInt(); return new User(name, age); } } // 注册使用 Gson gson = new GsonBuilder() .registerTypeAdapter(User.class, new UserSerializer()) .registerTypeAdapter(User.class, new UserDeserializer()) .create();
3.2.3 使用注解控制序列化
public class User { @SerializedName("user_name") // 字段重命名 private String name; @SerializedName(value = "user_age", alternate = {"age", "userAge"}) // 多字段名映射 private int age; @Expose(serialize = true, deserialize = false) // 控制序列化和反序列化 private String password; @Expose(serialize = false, deserialize = true) private String email; // getter和setter方法... } // 注意:使用@Expose需要配合excludeFieldsWithoutExposeAnnotation() Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
3.2.4 处理复杂嵌套对象
// 使用JsonElement处理未知结构的JSON String complexJson = "{\"name\":\"张三\",\"address\":{\"city\":\"北京\",\"district\":\"朝阳区\"}}"; JsonElement jsonElement = JsonParser.parseString(complexJson); JsonObject jsonObject = jsonElement.getAsJsonObject(); // 获取简单字段 String name = jsonObject.get("name").getAsString(); // 获取嵌套对象的字段 JsonObject addressObject = jsonObject.getAsJsonObject("address"); String city = addressObject.get("city").getAsString(); // 安全地获取可能不存在的字段 if (jsonObject.has("phone")) { String phone = jsonObject.get("phone").getAsString(); }

技巧:Gson 2.8.6+版本中,JsonParser的静态方法已被弃用,建议使用以下方式:

Gson gson = new Gson(); JsonReader reader = new JsonReader(new StringReader(jsonString)); reader.setLenient(true); JsonElement jsonElement = gson.fromJson(reader, JsonElement.class);

4. Jackson使用技巧

4.1 基础用法

Jackson是一个功能强大的Java库,用于处理JSON数据格式。它被广泛应用于Spring框架中,性能优异且功能丰富。

4.1.1 Maven依赖
<!-- 核心依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> </dependency> <!-- 可选:处理XML --> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.13.0</version> </dependency>
4.1.2 基本序列化与反序列化
// 创建ObjectMapper实例 ObjectMapper objectMapper = new ObjectMapper(); // Java对象转JSON字符串 User user = new User("张三", 25); String json = objectMapper.writeValueAsString(user); System.out.println(json); // {"name":"张三","age":25} // JSON字符串转Java对象 String jsonString = "{\"name\":\"李四\",\"age\":30}"; User parsedUser = objectMapper.readValue(jsonString, User.class); System.out.println(parsedUser.getName()); // 李四 // 格式化输出JSON String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
4.1.3 集合类型的序列化与反序列化
// 序列化集合 List<User> userList = Arrays.asList( new User("张三", 25), new User("李四", 30) ); String jsonList = objectMapper.writeValueAsString(userList); // 反序列化集合 List<User> parsedList = objectMapper.readValue( jsonList, new TypeReference<List<User>>() {} ); // 反序列化为Map Map<String, Object> map = objectMapper.readValue( jsonString, new TypeReference<Map<String, Object>>() {} );

4.2 高级特性

4.2.1 ObjectMapper配置
ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 忽略未知属性 .configure(SerializationFeature.INDENT_OUTPUT, true) // 格式化输出 .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) // 不序列化null值 .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) // 日期不使用时间戳 .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")) // 设置日期格式 .registerModule(new JavaTimeModule()); // 支持Java 8日期时间API
4.2.2 自定义序列化和反序列化
// 自定义序列化器 public class UserSerializer extends JsonSerializer<User> { @Override public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("userName", user.getName()); jsonGenerator.writeNumberField("userAge", user.getAge()); jsonGenerator.writeEndObject(); } } // 自定义反序列化器 public class UserDeserializer extends JsonDeserializer<User> { @Override public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); String name = node.get("userName").asText(); int age = node.get("userAge").asInt(); return new User(name, age); } } // 注册使用 SimpleModule module = new SimpleModule(); module.addSerializer(User.class, new UserSerializer()); module.addDeserializer(User.class, new UserDeserializer()); objectMapper.registerModule(module);
4.2.3 使用注解控制序列化
public class User { @JsonProperty("user_name") // 字段重命名 private String name; @JsonIgnore // 忽略此字段 private String password; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // 日期格式化 private Date createTime; @JsonInclude(JsonInclude.Include.NON_NULL) // 只序列化非null值 private String email; @JsonInclude(JsonInclude.Include.NON_EMPTY) // 只序列化非空值 private List<String> hobbies; // getter和setter方法... }
4.2.4 处理复杂JSON结构
// 使用JsonNode处理未知结构的JSON String complexJson = "{\"name\":\"张三\",\"address\":{\"city\":\"北京\",\"district\":\"朝阳区\"}}"; JsonNode rootNode = objectMapper.readTree(complexJson); // 获取字段 String name = rootNode.get("name").asText(); String city = rootNode.get("address").get("city").asText(); // 安全地获取可能不存在的字段 if (rootNode.has("phone")) { String phone = rootNode.get("phone").asText(); } // 遍历数组 JsonNode arrayNode = rootNode.get("contacts"); if (arrayNode != null && arrayNode.isArray()) { for (JsonNode node : arrayNode) { String contactName = node.get("name").asText(); } }
4.2.5 使用树模型
// 创建树模型 ObjectMapper mapper = new ObjectMapper(); ObjectNode rootNode = mapper.createObjectNode(); rootNode.put("name", "张三"); rootNode.put("age", 25); // 创建嵌套对象 ObjectNode addressNode = mapper.createObjectNode(); addressNode.put("city", "北京"); addressNode.put("district", "朝阳区"); rootNode.set("address", addressNode); // 创建数组 ArrayNode hobbiesNode = mapper.createArrayNode(); hobbiesNode.add("读书"); hobbiesNode.add("游泳"); rootNode.set("hobbies", hobbiesNode); // 转换为JSON字符串 String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);

5. Fastjson使用技巧

5.1 基础用法

Fastjson是阿里巴巴开发的JSON库,以其极高的性能著称,API简洁易用。

安全提示:Fastjson历史上存在一些安全漏洞,使用时请确保更新到最新版本,并根据官方建议进行安全配置。

5.1.1 Maven依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.79</version> </dependency>
5.1.2 基本序列化与反序列化
// Java对象转JSON字符串 User user = new User("张三", 25); String json = JSON.toJSONString(user); System.out.println(json); // {"age":25,"name":"张三"} // JSON字符串转Java对象 String jsonString = "{\"name\":\"李四\",\"age\":30}"; User parsedUser = JSON.parseObject(jsonString, User.class); System.out.println(parsedUser.getName()); // 李四 // 格式化输出 String prettyJson = JSON.toJSONString(user, SerializerFeature.PrettyFormat);
5.1.3 集合类型的序列化与反序列化
// 序列化集合 List<User> userList = Arrays.asList( new User("张三", 25), new User("李四", 30) ); String jsonList = JSON.toJSONString(userList); // 反序列化集合 List<User> parsedList = JSON.parseArray(jsonList, User.class); // 反序列化为JSONObject JSONObject jsonObject = JSON.parseObject(jsonString); String name = jsonObject.getString("name"); int age = jsonObject.getInteger("age");

5.2 高级特性

5.2.1 SerializerFeature配置
// 配置序列化特性 String json = JSON.toJSONString(user, SerializerFeature.PrettyFormat, // 格式化输出 SerializerFeature.WriteNullStringAsEmpty, // null字符串输出为"" SerializerFeature.WriteNullNumberAsZero, // null数字输出为0 SerializerFeature.WriteNullBooleanAsFalse, // null布尔值输出为false SerializerFeature.WriteNullListAsEmpty, // null列表输出为[] SerializerFeature.DisableCircularReferenceDetect, // 禁用循环引用检测 SerializerFeature.WriteDateUseDateFormat, // 日期格式化 SerializerFeature.WriteMapNullValue // 输出null值 );
5.2.2 自定义序列化和反序列化
// 自定义序列化器 public class UserSerializer implements ObjectSerializer { @Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { User user = (User) object; SerializeWriter out = serializer.out; out.write('{'); out.writeFieldName("userName"); serializer.write(user.getName()); out.write(','); out.writeFieldName("userAge"); serializer.write(user.getAge()); out.write('}'); } } // 自定义反序列化器 public class UserDeserializer implements ObjectDeserializer { @Override public User deserialze(DefaultJSONParser parser, Type type, Object fieldName) { JSONObject jsonObject = parser.parseObject(); String name = jsonObject.getString("userName"); int age = jsonObject.getInteger("userAge"); return new User(name, age); } @Override public int getFastMatchToken() { return 0; } } // 注册使用 ParserConfig.getGlobalInstance().putDeserializer(User.class, new UserDeserializer()); SerializeConfig.getGlobalInstance().put(User.class, new UserSerializer());
5.2.3 使用注解控制序列化
public class User { @JSONField(name = "user_name") // 字段重命名 private String name; @JSONField(serialize = false) // 不序列化此字段 private String password; @JSONField(deserialize = false) // 不反序列化此字段 private String token; @JSONField(format = "yyyy-MM-dd HH:mm:ss") // 日期格式化 private Date createTime; @JSONField(ordinal = 1) // 字段序列化顺序 private String email; // getter和setter方法... }
5.2.4 使用JSONObject和JSONArray
// 创建JSONObject JSONObject jsonObject = new JSONObject(); jsonObject.put("name", "张三"); jsonObject.put("age", 25); // 创建嵌套JSONObject JSONObject addressObject = new JSONObject(); addressObject.put("city", "北京"); addressObject.put("district", "朝阳区"); jsonObject.put("address", addressObject); // 创建JSONArray JSONArray hobbiesArray = new JSONArray(); hobbiesArray.add("读书"); hobbiesArray.add("游泳"); jsonObject.put("hobbies", hobbiesArray); // 转换为JSON字符串 String json = jsonObject.toJSONString(); // 解析复杂JSON JSONObject parsedObject = JSON.parseObject(complexJson); String city = parsedObject.getJSONObject("address").getString("city"); JSONArray contacts = parsedObject.getJSONArray("contacts"); for (int i = 0; i < contacts.size(); i++) { String contactName = contacts.getJSONObject(i).getString("name"); }
5.2.5 安全配置
// 禁用AutoType,防止反序列化漏洞 ParserConfig.getGlobalInstance().setAutoTypeSupport(false); // 如果需要使用AutoType,建议使用白名单机制 ParserConfig parserConfig = ParserConfig.getGlobalInstance(); parserConfig.addAccept("com.example.model."); // 只允许反序列化指定包下的类 // 或者使用安全的JSONReader JSONReader jsonReader = new JSONReader(new StringReader(jsonString)); jsonReader.config(Feature.SupportAutoType, false);

6. 性能优化技巧

6.1 通用优化原则

  • 重用对象:避免重复创建JSON解析器实例(Gson、ObjectMapper或JSON),这些实例是线程安全的
  • 避免不必要的转换:直接在流上进行操作,减少中间字符串的创建
  • 选择合适的解析模式:根据需求选择树模型或流式API
  • 优化数据结构:减少不必要的嵌套和冗余字段
  • 合理使用缓存:缓存序列化/反序列化的结果

6.2 Gson性能优化

// 1. 重用Gson实例 public class GsonHolder { private static final Gson GSON = new GsonBuilder() .setPrettyPrinting() .create(); public static Gson getGson() { return GSON; } } // 2. 使用TypeAdapter进行高性能序列化/反序列化 TypeAdapter<User> userAdapter = GsonHolder.getGson().getAdapter(User.class); // 序列化到流 userAdapter.write(new JsonWriter(new FileWriter("user.json")), user); // 从流反序列化 User user = userAdapter.read(new JsonReader(new FileReader("user.json"))); // 3. 避免使用toJsonTree再toJson,直接使用toJson String json = GsonHolder.getGson().toJson(user);

6.3 Jackson性能优化

// 1. 重用ObjectMapper实例 public class ObjectMapperHolder { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); public static ObjectMapper getObjectMapper() { return OBJECT_MAPPER; } } // 2. 使用流式API处理大型JSON // 序列化大型对象到文件 ObjectMapperHolder.getObjectMapper().writeValue(new File("large.json"), largeObject); // 反序列化大型JSON LargeObject largeObject = ObjectMapperHolder.getObjectMapper() .readValue(new File("large.json"), LargeObject.class); // 3. 使用JsonNode高效处理部分字段 JsonNode rootNode = ObjectMapperHolder.getObjectMapper().readTree(jsonString); String neededField = rootNode.get("neededField").asText(); // 4. 使用TypeFactory预构建复杂类型 JavaType listType = ObjectMapperHolder.getObjectMapper().getTypeFactory() .constructCollectionType(List.class, User.class);

6.4 Fastjson性能优化

// 1. 使用TypeReference提高泛型反序列化性能 List<User> userList = JSON.parseObject(jsonList, new TypeReference<List<User>>() {}); // 2. 使用JSONB格式(Fastjson 2.0+) byte[] bytes = JSONB.toJSONBBytes(user); User parsedUser = JSONB.parseObject(bytes, User.class); // 3. 自定义序列化过滤器提高性能 PropertyFilter propertyFilter = new PropertyFilter() { @Override public boolean apply(Object object, String name, Object value) { // 只序列化需要的字段 return !"password".equals(name); } }; String json = JSON.toJSONString(user, propertyFilter); // 4. 使用JSONReader处理大型JSON JSONReader reader = new JSONReader(new StringReader(largeJson)); reader.startObject(); while (reader.hasNext()) { String key = reader.readString(); if ("neededField".equals(key)) { String value = reader.readString(); // 处理需要的字段 } else { reader.skipValue(); } } reader.endObject();

注意:性能优化需要根据具体场景进行测试和验证,不同的框架在不同场景下可能有不同的表现。建议使用JMH等基准测试工具进行性能对比。

7. 最佳实践

7.1 选择合适的框架

  • 如果项目使用Spring框架,优先考虑Jackson
  • 如果对性能要求极高,可考虑Fastjson(注意安全问题)
  • 如果追求API简洁和易用性,可选择Gson

7.2 代码组织最佳实践

// 示例:创建统一的JSON工具类 public class JsonUtils { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 序列化方法 public static String toJson(Object obj) { try { return OBJECT_MAPPER.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException("JSON序列化失败", e); } } // 反序列化方法 public static <T> T fromJson(String json, Class<T> clazz) { try { return OBJECT_MAPPER.readValue(json, clazz); } catch (JsonProcessingException e) { throw new RuntimeException("JSON反序列化失败", e); } } // 反序列化集合 public static <T> List<T> fromJsonList(String json, Class<T> clazz) { try { return OBJECT_MAPPER.readValue( json, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz) ); } catch (JsonProcessingException e) { throw new RuntimeException("JSON列表反序列化失败", e); } } }

7.3 错误处理和异常管理

// 优雅处理JSON解析异常 try { User user = JsonUtils.fromJson(jsonString, User.class); // 处理用户对象 } catch (RuntimeException e) { // 记录详细错误信息 log.error("JSON解析失败: {}", jsonString, e); // 友好的错误响应 throw new BusinessException("数据格式错误,请检查输入"); } // 验证JSON格式 try { ObjectMapperHolder.getObjectMapper().readTree(jsonString); // JSON格式有效 } catch (Exception e) { // JSON格式无效 throw new IllegalArgumentException("无效的JSON格式"); }

7.4 安全处理

安全提示:处理外部JSON数据时,务必注意安全问题,特别是反序列化操作可能导致远程代码执行漏洞。

// Jackson安全配置 ObjectMapper mapper = new ObjectMapper(); // 禁用外部实体,防止XXE攻击 mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 限制反序列化的类 mapper.registerModule(new SimpleModule() .addDeserializer(Object.class, new SafeObjectDeserializer())); // Fastjson安全配置 ParserConfig.getGlobalInstance().setAutoTypeSupport(false); // 使用白名单 ParserConfig.getGlobalInstance().addAccept("com.example.model."); // Gson安全配置 Gson gson = new GsonBuilder() .registerTypeAdapterFactory(new SafeTypeAdapterFactory()) .create();

7.5 处理日期时间

// Jackson日期时间处理 ObjectMapper mapper = new ObjectMapper(); // 配置日期格式 mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 支持Java 8日期时间API mapper.registerModule(new JavaTimeModule()); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // Gson日期时间处理 Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd HH:mm:ss") .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()) .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer()) .create(); // Fastjson日期时间处理 String json = JSON.toJSONString(dateObject, SerializerFeature.WriteDateUseDateFormat); // 自定义日期格式 JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; String formattedJson = JSON.toJSONString(dateObject, SerializerFeature.WriteDateUseDateFormat);

8. 常见问题与解决方案

8.1 字段名不匹配

问题:JSON中的字段名与Java类中的字段名不一致,导致反序列化失败。

解决方案:

  • Gson: 使用@SerializedName注解
  • Jackson: 使用@JsonProperty注解
  • Fastjson: 使用@JSONField注解

8.2 处理null值

问题:JSON中包含null值,如何控制其序列化和反序列化行为。

解决方案:

  • Gson: 使用GsonBuilder.serializeNulls()控制
  • Jackson: 使用@JsonInclude(JsonInclude.Include.NON_NULL)注解或配置
  • Fastjson: 使用SerializerFeature.WriteMapNullValue或@JSONField注解

8.3 循环引用问题

问题:对象之间存在循环引用,导致序列化时出现StackOverflowError。

解决方案:

  • Gson: 默认处理循环引用,但会生成$ref引用标记
  • Jackson: 使用@JsonManagedReference和@JsonBackReference注解
  • Fastjson: 使用SerializerFeature.DisableCircularReferenceDetect禁用循环引用检测

8.4 处理复杂嵌套结构

问题:JSON结构复杂,难以直接映射到Java类。

解决方案:

  • Gson: 使用JsonParser和JsonElement API
  • Jackson: 使用JsonNode树模型API
  • Fastjson: 使用JSONObject和JSONArray API

8.5 性能问题

问题:处理大型JSON数据时性能不佳。

解决方案:

  • 重用解析器实例
  • 使用流式API而不是树模型
  • 避免不必要的字符串转换
  • 使用更高效的JSON库(如需要高性能可考虑Fastjson)

9. 总结与建议

选择建议

  • Spring项目:优先使用Jackson,与Spring框架集成良好
  • 对性能要求极高的场景:考虑Fastjson,但需注意安全问题
  • 追求简洁API:Gson是不错的选择
  • 混合环境:可以根据不同模块的需求选择不同的框架

重要提示

  • 始终使用最新稳定版本的JSON库,以获取安全补丁和性能改进
  • 在处理外部输入的JSON数据时,务必注意安全问题
  • 对于复杂的JSON处理需求,考虑封装统一的工具类
  • 在关键性能路径上,进行性能测试以选择最优方案

通过本文介绍的技巧和最佳实践,相信您能够在Java项目中更加高效、安全地处理JSON数据。选择合适的工具,遵循最佳实践,将使您的代码更加健壮和可维护。

Read more

贪心算法(局部最优实现全局最优)第二篇

贪心算法(局部最优实现全局最优)第二篇

目录 1. LeetCode376. 摆动序列 2. LeetCode334. 递增的三元子序列 3. LeetCode674. 最长连续递增序列 4. LeetCode121. 买卖股票的最佳时机 今天我们继续来聊聊贪心算法,因为我在前面也说过贪心算法最重要的就是经验,所以我们今天继续通过刷题的方式来学习贪心算法。 1. LeetCode376. 摆动序列 这道题的意思其实也比较好理解的,就是求一个最长的摆动序列,可以从原数组中删除不符合条件的数。 这道题的话我们先来聊一下思路,因为要求的是最长的子数组。根据题目要求那么是不是说我们每次选的数字都要在有限的分为里面做到尽可能的大或者尽可能的小。为什么要这么做呢?是因为但我们选到最值的时候我们在后面的选择中才可以有更多的选择。 我们看下面这个图,里面有abcdef这几个极值点。我们看,在c和d之间有一个点x1,假设我们在这里选择了这个点的话,那么后面的数都选不了了,因为接下来是要选择比x1小的数。这也是为什么我们每一次都要选择最值的原因。 那么我们代码该怎么设计呢?我们就可以试用一个三指针,通过比较的这三个指针的大

By Ne0inhk
哈希表完全指南:从入门到刷题实战

哈希表完全指南:从入门到刷题实战

文章目录 * 前置知识要求 * 为什么叫Hash? * 和数组有什么关系? * 数组是怎么组织数据的? * 但如果我知道索引呢? * 矛盾点 * 哈希表的做法 * 对比总结 * 哈希表在代码中长什么样?(Java) * 在 Java 中,哈希表的表现形式为**键值对(Key-Value)** * 键值对是什么? * 底层怎么存的? * 哈希表中常用的方法有哪几个? * 实战:刷LeetCode时怎么用哈希表得到更好的时间复杂度? * 简单题:难度1 * 答案 * 通用小技巧 * 简单题:难度2 * 答案 * 中等题:难度4 * 为什么会有不同的哈希表? * 主要的哈希表种类 * **链表法哈希表(最常见)** * **开放寻址法哈希表** * **布谷鸟哈希(Cuckoo Hashing)** * **一致性哈希(Consistent Hashing)** * 题外话:哈希表的前世今生与永远的更优 * 前世 * 今生 *

By Ne0inhk
极致性能的服务器Redis之Hash类型及相关指令介绍

极致性能的服务器Redis之Hash类型及相关指令介绍

目录 1. Hash介绍 2. hset 3. hget 3. hdel 5. hkeys 6. hvals 编辑 7. hgetall  8. hexists 9. hmget 10. hlen 11. hsetnx 12. hincrby 13. hincrbyfloat 1. Hash介绍 Redis 哈希类型是键值对的集合,字段与值均支持字符串、数字等类型,适合建模用户信息、配置项等对象类数据。其支持单字段 / 多字段的增删改查、字段存在性判断、值自增自减等原子操作,且底层通过压缩列表或哈希表优化存储,空间利用率高、查询效率快,是 Redis 中存储结构化数据的核心类型之一。 在Redis中因为本身就是按照哈希的KV结构来进行存储的,所以当我们想要使用Redis里面的哈希的时候,实际上是哈希的哈希,在后者中,

By Ne0inhk
《算法题讲解指南:优选算法-二分查找》--21.山峰数组的的峰顶索引,22.寻找峰值

《算法题讲解指南:优选算法-二分查找》--21.山峰数组的的峰顶索引,22.寻找峰值

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 21. 山峰数组的的峰顶索引 题目链接: 题目描述: 题目示例: 解法(二分查找): 算法思路: C++算法代码: 算法总结及流程解析: 22. 寻找峰值 题目链接: 题目描述: 题目示例: 解法(二分查找): 算法思路: C++算法代码: 算法总结及流程解析: 结束语 21. 山峰数组的的峰顶索引 题目链接: 852. 山脉数组的峰顶索引 - 力扣(LeetCode) 题目描述: 题目示例: 解法(

By Ne0inhk