Guava源码浅析——Joiner

Join类的源码解析

[TOC]

Join类的基本功能
  • 通过Join类,我们可以方便实现将容器中的数据按照自定义的方式拼接成一个字符串,而且这是一种线程安全的方式。

    ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4);
    String join = Joiner.on("---").join(integers);
    System.out.println(join);
    复制代码
实现原理
  • 通过查看Joiner的源码可以发现Join的源码是私有的,也就是不让我们直接去新建对象,而是让我们通过提供的共有静态方法去构建对象:

    public static Joiner on(char separator) {
        return new Joiner(String.valueOf(separator));
      }
    private Joiner(Joiner prototype) {
        this.separator = prototype.separator;
      }
    复制代码

    猜测通过这种方法构造对象的主要目的是让使用者明白这里传入的separator参数是用作容器之间额连接符的。

  • 在对象中保存了分隔符之后又是如何进行划分的呢?

    public final String join(Iterable<?> parts) {
        return join(parts.iterator());
      }
    
    public final String join(Iterator<?> parts) {
        return appendTo(new StringBuilder(), parts).toString();
      }
    
    public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
        try {
          appendTo((Appendable) builder, parts);
        } catch (IOException impossible) {
          throw new AssertionError(impossible);
        }
        return builder;
      }
    复制代码

    上面代码是一种重载的思想,最终是通过appendTo方法进行的字符创拼接,注意到这里传入的第一个参数是一个StringBuilder对象,为了就是保证线程的安全性,防止多线程环境下可能出现的问题。

  • 最后在appendTo函数是如何做字符串拼接的?

    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;
      }
    复制代码

    可以看到这里首先检查了一下这个StringBuilder是否为空,如果为空就会抛出异常。再然后就是迭代器的遍历了,注意到这里有一个toString()方法,很明显,这里重写了Object类的这个方法:

    CharSequence toString(Object part) {
        checkNotNull(part); // checkNotNull for GWT (do not optimize).
        return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
      }
    复制代码

    同样这里做了类型检查,就是为了防止容器中有null对象,其次就是如果part本身就是字符数组了,就不用调用tostring方法了,节约系统资源。

    除了第一个元素外,其余每一个元素都是分隔符与元素的拼接,很简单的逻辑。就这样完成了一次拼接操作。

学习的设计模式
  • 除了基本的字符串拼接外,Joiner类还包含了一些优秀的设计模式,在前面已经说过,如果容器中包含null对象,那么将会抛出空指针异常,我们可以通过如下方法去避免:

    ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4,null);
    String join = Joiner.on("---").skipNulls().join(integers);
    System.out.println(join);
    复制代码

    这里的skipNulls可以避免凭借那些null值,不过拼接的逻辑在之前已经写好了,我们并没有看到有什么避免空值的方法呀,那到底是如何实现的呢?

    public Joiner skipNulls() {
        return new Joiner(this) {
          @Override
          public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
            checkNotNull(appendable, "appendable");
            checkNotNull(parts, "parts");
            while (parts.hasNext()) {
              Object part = parts.next();
              if (part != null) {
                appendable.append(Joiner.this.toString(part));
                break;
              }
            }
            while (parts.hasNext()) {
              Object part = parts.next();
              if (part != null) {
                appendable.append(separator);
                appendable.append(Joiner.this.toString(part));
              }
            }
            return appendable;
          }
    
          @Override
          public Joiner useForNull(String nullText) {
            throw new UnsupportedOperationException("already specified skipNulls");
          }
    
          @Override
          public MapJoiner withKeyValueSeparator(String kvs) {
            throw new UnsupportedOperationException("can't use .skipNulls() with maps");
          }
        };
      }
    复制代码

    这个方法看上去稍微有点长,其实逻辑是很简单的,就是通过重新返回了一个Joiner对象,这个对象的分隔符和之前的是一样的,不过对于appendTouseForNullwithKeyValueSeparator这个几个方法进行了重写,所以最后调用join方法的时候就能够对Null值进行不同的判断了。

  • 同样的,Joiner中还有一个替换Null的方法

    ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4,null);
    String join = Joiner.on("---").useForNull("XXX").join(integers);
    System.out.println(join);
    复制代码

    这里的useForNull方法和上面的skipNulls方法很像,都是通过返回重写了方法的Joiner对象实现不同的判断策略,其实这就是一种策略模式的体现,对于不同的实现策略有不同的实现,针对实际应用中的需求应用不同的策略,避免了在实现方法中加入大量case判断的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值