Java效率提升神器之Guava-Joiner

目录
  • Joiner
  • Joiner.MapJoiner
  • 源代码分析
  • 拼接Map键值对

姊妹篇:Java效率提升神器jOOR

在我们的开发中经常会用到Guava中的一些功能。但是我们所使用到的只是Guava API中的小的可怜的一个子集。我们大家一起来发掘一下Guava中更多的一些功能。

Joiner

这是在我们代码中出现频率比较高的一个功能。经常需要将几个字符串,或者字符串数组、列表之类的东西,拼接成一个以指定符号分隔各个元素的字符串,比如要将一个用List保存的字符集合拼起来作为SQL语句的条件,在知道Joiner之前我们会这样做。

ArrayList<String> conditions = new ArrayList<String>();
conditions.add("condition1");
conditions.add("condition2");
conditions.add("condition3");

private String buildCondition(ArrayList<String> conditions) {
    StringBuilder sb = new StringBuilder();
    for (String condition : conditions) {
        sb.append(condition);
        sb.append(" or ");
    }
    int index = sb.lastIndexOf(" or ");
    return index > 0 ? sb.substring(0, index) : sb.toString();
}  // condition1 or condition2 or condition3

基本上会手写循环去实现,代码瞬间变得丑陋起来。并且循环完了还得删除最后一个多余的or。

使用Guava工具,我们能够轻而易举的完成字符串拼接这一简单任务。借助 Joiner 类,代码瞬间变得优雅起来。

Joiner.on(" or ").join(conditions);

被拼接的对象集,可以是硬编码的少数几个对象,可以是实现了 Iterable 接口的集合,也可以是迭代器对象。

除了返回一个拼接过的字符串,Joiner 还可以在实现了 Appendable 接口的对象所维护的内容的末尾,追加字符串拼接的结果。

StringBuilder sb = new StringBuilder("result:");
Joiner.on("#").appendTo(sb, 1, 2, 3);
System.out.println(sb);     //result:1#2#3

我们看下面这个例子:

Joiner.on("#").join(1, null, 3)

如果传入的对象中包含空指针,会直接抛出空指针异常。Joiner 提供了两个方法,让我们能够优雅的处理待拼接集合中的空指针。

如果我们希望忽略空指针,那么可以调用 skipNulls方法,得到一个会跳过空指针的 Joiner 实例。如果希望将空指针变为某个指定的值,那么可以调用 useForNull 方法,指定用来替换空指针的字符串。

Joiner.on("#").skipNulls().join(1, null, 3);      //1#3
Joiner.on("#").useForNull("").join(1, null, 3);   //1##3

Joiner.MapJoiner

MapJoiner 是 Joiner 的内部静态类,用于帮助将 Map 对象拼接成字符串。

 Map<Integer, Integer> test = new HashMap<Integer, Integer>();
 test.put(1, 2);
 test.put(3, 4);
 Joiner.on("#").withKeyValueSeparator("=").join(test); //1=2#3=4

withKeyValueSeparator 方法指定了键与值的分隔符,同时返回一个 MapJoiner 实例。

Joiner.on("#").withKeyValueSeparator("=").join(ImmutableMap.of(1, 2, 3, 4));  //1=2#3=4

源代码分析

源码来自Guava 18.0。Joiner类的源码一共458行。大部分都是注释。 Joiner 只能通过 Joiner.on 函数来初始化,它的构造方法是私有的。

/**
* Returns a joiner which automatically places {@code separator} between consecutive elements.
*/
public static Joiner on(String separator) {
	return new Joiner(separator);
}
/**
* Returns a joiner which automatically places {@code separator} between consecutive elements.
*/
public static Joiner on(char separator) {
	return new Joiner(String.valueOf(separator));
}

整个 Joiner 类最核心的函数莫过于 <A extends Appendable> appendTo(A, Iterator<?>),一切的字符串拼接操作,最后都会调用到这个函数。这就是所谓的全功能函数,其他的一切 appendTo 只不过是它的重载,一切的join不过是它和它的重载的封装。

/**
   * Appends the string representation of each of {@code parts}, using the previously configured
   * separator between each, to {@code appendable}.
   *
   * @since 11.0
   */
  public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
    checkNotNull(appendable);
    if (parts.hasNext()) {
      appendable.append(toString(parts.next()));
      while (parts.hasNext()) {
        appendable.append(separator);
        appendable.append(toString(parts.next()));
      }
    }
    return appendable;
  }

这段代码的第一个技巧是使用 if 和 while 来实现了比较优雅的分隔符拼接,避免了在末尾插入分隔符的尴尬;第二个技巧是使用了自定义的 toString 方法而不是 Object#toString 来将对象序列化成字符串,为后续的各种空指针保护开了方便之门。

来看一个比较有意思的 appendTo 重载。

public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
    try {
        this.appendTo((Appendable)builder, (Iterator)parts);
        return builder;
    } catch (IOException var4) {
        throw new AssertionError(var4);
    }
}

在 Appendable 接口中,append 方法是会抛出 IOException 的。然而 StringBuilder 虽然实现了 Appendable,但是它覆盖实现的 append 方法却是不抛出 IOException 的。于是就出现了明知不可能抛异常,却又不得不去捕获异常的尴尬。

这里的异常处理手法十分机智,异常变量命名为 impossible,我们一看就明白这里是不会抛出 IOException 的。但是如果 catch 块里面什么都不做又好像不合适,于是抛出一个 AssertionError,表示对于这里不抛异常的断言失败了。

另一个比较有意思的 appendTo 重载是关于可变长参数。

public final <A extends Appendable> A appendTo(A appendable, @Nullable Object first, @Nullable Object second, Object... rest) throws IOException {
    return this.appendTo((Appendable)appendable, (Iterable)iterable(first, second, rest));
}

注意到这里的 iterable 方法,它把两个变量和一个数组变成了一个实现了Iterable 接口的集合,非常精妙的实现!

private static Iterable<Object> iterable(final Object first, final Object second, final Object[] rest) {
    Preconditions.checkNotNull(rest);
    return new AbstractList() {
        public int size() {
            return rest.length + 2;
        }
        public Object get(int index) {
            switch(index) {
            case 0:
                return first;
            case 1:
                return second;
            default:
                return rest[index - 2];
            }
        }
    };
}

要想看明白这段代码,需要熟悉AbstractList类中迭代器的实现。迭代器内部维护着一个游标,cursor。迭代器的两大关键操作,hasNext 判断是否还有没遍历的元素,next 获取下一个元素,它们的实现是这样的。

public boolean hasNext() {
        return cursor != size();
}
public E next() {
        checkForComodification();
    try {
    E next = get(cursor);
    lastRet = cursor++;
    return next;
    } catch (IndexOutOfBoundsException e) {
    checkForComodification();
    throw new NoSuchElementException();
    }
}

hasNext 中关键的函数调用是size方法,获取集合的大小。next 方法中关键的函数调用是get方法,获取第 i 个元素。Guava 的实现返回了一个被覆盖了 size 和 get 方法的 AbstractList,巧妙的复用了由编译器生成的数组,避免了新建列表和增加元素的开销。

拼接Map键值对

MapJoiner 实现为 Joiner 的一个静态内部类,它的构造函数和 Joiner 一样也是私有,只能通过 withKeyValueSeparator来生成实例。类似地,MapJoiner 也实现了 appendTo 方法和一系列的重载,还用 join 方法对 appendTo 做了封装。

MapJoiner 整个实现和 Joiner 大同小异,在实现中大量使用 Joiner 的 toString 方法来保证空指针保护行为和初始化时的语义一致。

到此这篇关于Java效率提升神器之Guava-Joiner的文章就介绍到这了,更多相关Java Guava-Joiner内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Java中GuavaCache返回Null的注意事项

    Guava在实际的Java后端项目中应用的场景还是比较多的,比如限流,缓存,容器操作之类的,有挺多实用的工具类,这里记录一下,在使用GuavaCache,返回null的一个问题 I. 常见使用姿势 @Test public void testGuava() { LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() { @Overri

  • Java8优雅的字符串拼接工具类StringJoiner实例代码

    StringJoiner是Java8新出的用于处理字符串拼接的工具类,可以让你的代码看起来更优雅,不拉跨. 假设现在遍历一个字符串集合,需求是每个元素按照 "." 分开. String a = "w", b = "d", c = "n", d = "m", e = "d"; List<String> list = new ArrayList<>(); list.a

  • Java字符串拼接新方法 StringJoiner用法详解

    Java中如何输出像1-2-3-4-5 这样的字符 抱歉对于这个问题我甚至不能想到一个合适的标题,但是不重要 以下操作基于 jdk 1.8 StringJoiner sj = new StringJoiner("-", "", ""); sj.add("1").add("1").add("2"); String desiredString = sj.toString(); 在1.8版本中

  • java的Guava工具包介绍

    集合 普通集合 List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap(); Set 取交集.并集.差集 HashSet<Integer> setA = Sets.newHashSet(1, 2, 3, 4, 5); HashSet<Integer> setB =

  • Java效率提升神器jOOR

    目录 前言 jOOR特点 常用API测试 测试APIS 代理功能 jOOR实现方式 动态编译 结论 前言 Java中的原生反射库虽然方法不多,但写起来却非常繁琐, 比如: public static <T> T create(HttpRequest httpRequest) { Object httpRequestEntity = null; try { Class<T> httpRequestEntityCls = (Class<T>) Class.forName(H

  • Java 8中字符串拼接新姿势StringJoiner详解

    在为什么阿里巴巴不建议在for循环中使用"+"进行字符串拼接一文中,我们介绍了几种Java中字符串拼接的方式,以及优缺点.其中还有一个重要的拼接方式我没有介绍,那就是Java 8中提供的StringJoiner ,本文就来介绍一下这个字符串拼接的新兵. 如果你想知道一共有多少种方法可以进行字符串拼接,教你一个简单的办法,在Intellij IDEA中,定义一个Java Bean,然后尝试使用快捷键自动生成一个toString方法,IDEA会提示多种toString生成策略可供选择. 1

  • Java groovy如何提升代码运行效率

    刚开始学groovy,知道了它会先变异成class 文件,然后再用jvm 执行.写了Hello World程序,查看它的编译文件,发现groovy的效率挺低的.不但编译文件的代码多,而且需要依赖很多groovy包,导致了不能够直接使用java 命令运行class文件 比较如下: Java版Hello World,JavaTest.java public class JavaTest { public static void main(String[] args){ System.out.prin

  • Java实用工具之StringJoiner详解

    背景 在平时的业务开发中,我们可能会遇到字符串列表根据分隔符进行拼接的需求.比如: 输入: 数组:["a","b","c"]分隔符:"," 输出: "a,b,c" 处理 通常我们可以使用StringBuilder根据下标位置决定是否需要添加分隔符以达到目的,比如: public static void main(String[] args) { StringBuilder sb = new StringBu

  • Java guava monitor监视器线程的使用详解

    Maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.0.1-jre</version> </dependency> 代码 不废话上代码. package com.huyi.csdn.tools; import cn.hutool.core.thread.Thread

  • Java效率提升神器之Guava-Joiner

    目录 Joiner Joiner.MapJoiner 源代码分析 拼接Map键值对 姊妹篇:Java效率提升神器jOOR 在我们的开发中经常会用到Guava中的一些功能.但是我们所使用到的只是Guava API中的小的可怜的一个子集.我们大家一起来发掘一下Guava中更多的一些功能. Joiner 这是在我们代码中出现频率比较高的一个功能.经常需要将几个字符串,或者字符串数组.列表之类的东西,拼接成一个以指定符号分隔各个元素的字符串,比如要将一个用List保存的字符集合拼起来作为SQL语句的条件

  • IDEA必备开发神器之EasyCode

    1.打开IntelliJ IDEA 新建一个maven工程 2.选择工程存放目录 3.下载安装EasyCode插件 file->settings->plugins 搜索Easy Code 搜索到后点击Install 我这里安装过了 安装完成会让你重启IDEA. 如何判断是否安装成功 file->settings->Other settings 看是否有Easy Code这个选项 4.引入Easy Code模板 (可以根据个人情况定制 也可以使用默认的) 5.创建数据库,数据表 6.

  • Python神器之Pampy模式匹配库的用法详解

    目录 Pampy 是哪路神仙 Pampy 的花式秀 匹配单个字符 匹配字典 匹配开头和结尾 总结 大家好,我是闲欢,一个很卷的程序员! 今天给大家分享一个炒鸡炒鸡简单又好用的神器——pampy. 我敢以我的荣誉保证,用了它之后,你写代码的效率可以蹭蹭蹭地提升! Pampy 是哪路神仙 首先普及一下模式匹配. 模式匹配即给定某种模式,用这种模式去检查序列或字符串是否符合这种模式,这种技术在自然语言处理中经常使用. Pampy 是 Python 的一个模式匹配类库,一个只有150行的类库,该库优雅.

  • IDEA POJO开发神器之Groovy的使用详解

    暂时只对 MySQL进行了测试 项目使用 Lombok MyBatis-Plus 一:使用步骤首先在项目右侧找到 DataBase 如图 没有请参考 idea中database不显示问题 2.点开之后进行数据库连接(注意没有驱动的请下载相关数据库驱动)具体步骤如图 点开 + 号 选择Date Source 找到相应的数据库 这里我使用的是 mysql 如果没有 Dirver 请下载 idea 会在窗口左下角给提示(这里具体在什么位置我也记不清楚)输入相关连接信息 过程中出现任何问题,请在留言区留

  • idea 开发神器之idea插件汇总

    1.lombok @Data注解在类上,会为类的所有属性自动生成setter/getter.equals.canEqual.hashCode.toString方法, 如为final属性,则不会为该属性生成setter方法. 2.Free MyBatis plugin 方便点击去接口,去到xml文件编写的方法,不用来回找了 3.EasyCode 自动生成文件 基于IntelliJ IDEA开发的代码生成插件,支持自定义任意模板(Java,html,js,xml). 只要是与数据库相关的代码都可以通

  • 自从在 IDEA 中用了热部署神器 JRebel 之后,开发效率提升了 10(真棒)

    来源:https://github.com/judasn/IntelliJ-IDEA-Tutorial/blob/master/jrebel-setup.md 在 Java Web 开发中, 一般更新了 Java 文件后要手动重启 Tomcat 服务器, 才能生效, 浪费不少生命啊, 自从有了 JRebel 这神器的出现, 不论是更新 class 类还是更新 Spring 配置文件都能做到立马生效,大大提高开发效率. JRebel 安装 JRebel 的安装方法有两种, 一种是直接在 Tomca

  • SpringBoot四大神器之Auto onfiguration的使用

    目录 1. 通过启动类创建Spring Boot应用 2. @SpringBootApplication注解 2.1 @SpringBootConfiguration 2.2 @EnableAutoConfiguration 2.3 @ComponentScan 3.自定义自动配置 3.1 基于类的条件注解 3.2 基于Bean的条件注解 3.3 基于属性的条件注解 3.4 基于资源的条件注解 3.5 自定义条件 3.6 申请条件 4. 测试自动配置 5. 禁用自动配置类 6. 结论 Sprin

  • Spring Boot四大神器之CLI的具体使用

    目录 1. Spring Boot CLI简介: 2. 安装CLI: 3. 运行Groovy脚本 4. CLI运行原理说明:(CLI帮我们做了什么?) 1. Spring Boot CLI简介: 官网地址: https://docs.spring.io/spring-boot/docs/current/reference/html/cli.html#cli. 安装官网地址: https://docs.spring.io/spring-boot/docs/current/reference/htm

  • python三大神器之fabric使用教程

    fabric 是一个python包 是一个基于ssh的部署工具包 通常用来对网站 微服务等等的批量部署 例如 我有5台线上服务器 可以通过一台对着5台分发,实现自动部署的目的. 简单介绍下 fabric的常用命令 常用命令 lcd(dir): 进入本机某目录 local(cmd): 本机上执行命令 cd(dir): 进入服务器某目录 run(cmd):服务器上执行命令 Fabric Fabric是一个python的远程执行shell的库,同时它也是一个命令行工具.它提供了丰富的同 SSH 交互的

随机推荐