Guava: Joiner & Splitter

本文介绍Guava库中Joiner和Splitter类的功能及使用方法,包括处理空值、分割字符串和构建键值对等常见场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Joiner

    Comparasion between traditional approach and Guava approach:

    @Test
    public void buildStringTest() {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	String str = buildString(data, ",");
	assertEquals("A,B,C,D,E,F", str);
    }

    public String buildString(List<String> data, String delimeter) {
	StringBuilder sb = new StringBuilder();
	for (String s : data) {
	    if (null != s) {
		sb.append(s).append(delimeter);
	    }
	}
	sb.setLength(sb.length() - delimeter.length());
	return sb.toString();
    }
    @Test
    public void joinTest() throws IOException {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	String str = Joiner.on(",").skipNulls().join(data);
	assertEquals("A,B,C,D,E,F", str);
    } 

   Note the need to remove the last delimeter that was appended to the very end of the string.

   Not very complicated, but it's still some boilerplate code that can be more easily handled by using the Joiner class.

 

2. Joiner usage

   1> Simple Usage

    @Test
    public void joinTest() throws IOException {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	String str = Joiner.on(",").skipNulls().join(data);
	logger.info(str);
	assertEquals("A,B,C,D,E,F", str);

	str = Joiner.on("|").useForNull("NULL").join(data);
	logger.info(str);
	assertEquals("A|B|C|NULL|D|E|F", str);

	StringBuilder sb = new StringBuilder("X|M|U|");
	Joiner.on("|").skipNulls().appendTo(sb, data);
	logger.info(sb.toString());
	assertEquals("X|M|U|A|B|C|D|E|F", sb.toString());

	FileWriter fw = new FileWriter(new File("src/test/resources/test.csv"));
	Joiner.on(",").useForNull(" ").appendTo(fw, data);
	fw.close();
    }
public <A extends Appendable> A appendTo(A appendable, Iterable<?> parts);
appendTo can be used for any class that implements Appendable interface.

    2> Attention:

        1) Joiner instances are always immutable. The joiner configuration methods will always return a new Joiner, which you must use to get the desired semantics.

             This makes any Joiner thread safe, and usable as a static final constant.

    @Test
    public void joinTest() throws IOException {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	Joiner joiner = Joiner.on(",");
	joiner.skipNulls();
	String str = joiner.join(data);
	logger.info(str);
    }
    // NullPointerException will be thrown.

    @Test
    public void joinTest() throws IOException {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	Joiner joiner = Joiner.on(",");
	joiner = joiner.skipNulls();
	String str = joiner.join(data);
	logger.info(str);
    }
    // A,B,C,D,E,F
  public static Joiner on(String separator) {
    return new Joiner(separator);
  }

  public Joiner useForNull(final String nullText) {
    checkNotNull(nullText);
    return new Joiner(this) {...}
  }

  public Joiner skipNulls() {
    return new Joiner(this) {...}
  }

        2) The returned Joiner is slightly different from traditional Joiner.

    @Test
    public void joinTest() throws IOException {
	List<String> data = Lists.newArrayList("A", "B", "C", null, "D", "E",
		"F");
	Joiner joiner = Joiner.on(",");
	joiner = joiner.skipNulls().useForNull("NULL");
	String str = joiner.join(data);
	logger.info(str);
    }

    // UnsupportedOperationException will be thrown indicates that "already specified skipNulls"

          How to achieve that?

  public Joiner skipNulls() {
    return new Joiner(this) {
      @Override public Joiner useForNull(String nullText) {
        throw new UnsupportedOperationException("already specified skipNulls");
      }
    };
  }

  public Joiner useForNull(final String nullText) {
    return new Joiner(this) {
      @Override public Joiner useForNull(String nullText) {
        throw new UnsupportedOperationException("already specified useForNull");
      }

      @Override public Joiner skipNulls() {
        throw new UnsupportedOperationException("already specified useForNull");
      }
    };
  }

 

3. MapJoiner usage

    @Test
    public void joinTest() throws IOException {
	Map<String, String> data = Maps.newHashMap();
	data.put("Name", "Davy");
	data.put("Gender", "Male");
	data.put("Age", "24");
	Joiner joiner = Joiner.on(",");
	MapJoiner mapJoiner = joiner.withKeyValueSeparator("=");
	String str = mapJoiner.join(data);
	logger.info(str);
	assertEquals("Name=Davy,Age=24,Gender=Male", str);
        // Use "," as separator for entry, and  "=" as separator for key and value
    }

    Source code: 

    public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
        throws IOException {
      checkNotNull(appendable);
      if (parts.hasNext()) {
        Entry<?, ?> entry = parts.next();
        appendable.append(joiner.toString(entry.getKey()));
        appendable.append(keyValueSeparator);
        appendable.append(joiner.toString(entry.getValue()));
        while (parts.hasNext()) {
          appendable.append(joiner.separator);
          Entry<?, ?> e = parts.next();
          appendable.append(joiner.toString(e.getKey()));
          appendable.append(keyValueSeparator);
          appendable.append(joiner.toString(e.getValue()));
        }
      }
      return appendable;
    }

 

2. Splitter: 

    1> Comparasion between traditional approach and Guava approach:

    @Test
    public void splitTest() {
	String str = "Monday,Tuesday,,Thursday,Friday,,";
	String[] strs = str.split(",");
	String[] exp = new String[] { "Monday", "Tuesday", "", "Thursday",
		"Friday" };
	assertArrayEquals(exp, strs);
    }
    @Test
    public void splitTest() {
	String str = "Monday,Tuesday,,Thursday,Friday,,";
	List<String> strs = Splitter.on(',').splitToList(str);
	List<String> exp = Lists.newArrayList("Monday", "Tuesday", "",
		"Thursday", "Friday", "", "");
	assertEquals(exp, strs);
    }
    @Test
    public void splitTest() {
	String str = "Monday,Tuesday,,Thursday,Friday,,";
	List<String> strs = Splitter.on(',').omitEmptyStrings()
		.splitToList(str);
	List<String> exp = Lists.newArrayList("Monday", "Tuesday", "Thursday",
		"Friday");
	assertEquals(exp, strs);
    }
    @Test
    public void splitTest() {
	String str = "Monday, Tuesday, , Thursday, Friday, , ";
	List<String> strs = Splitter.on(',').omitEmptyStrings().trimResults()
		.splitToList(str);
	List<String> exp = Lists.newArrayList("Monday", "Tuesday", "Thursday",
		"Friday");
	assertEquals(exp, strs);
    }
    @Test
    public void splitTest() {
	String str = "Monday, Tuesday, , Thursday, Friday, , ";
	List<String> strs = Splitter.on(',').trimResults().splitToList(str);
	List<String> exp = Lists.newArrayList("Monday", "Tuesday", "",
		"Thursday", "Friday", "", "");
	assertEquals(exp, strs);
    }

    We can see that using Guava Splitter, the result is more reasonable and comprehensive.

    2> MapSplitter

    @Test
    public void splitTest() {
	String str = "Name=Davy;Age=24;Gender=Male";
	Map<String, String> expectedMap = Maps.newLinkedHashMap();
	expectedMap.put("Name", "Davy");
	expectedMap.put("Age", "24");
	expectedMap.put("Gender", "Male");
	Map<String, String> generatedMap = Splitter.on(';')
		.withKeyValueSeparator("=").split(str);
	assertEquals(expectedMap, generatedMap);
    }
    public Map<String, String> split(CharSequence sequence) {
      Map<String, String> map = new LinkedHashMap<String, String>();
      for (String entry : outerSplitter.split(sequence)) {
        Iterator<String> entryFields = entrySplitter.splittingIterator(entry);

        checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
        String key = entryFields.next();
        checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key);

        checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
        String value = entryFields.next();
        map.put(key, value);

        checkArgument(!entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry);
      }
      return Collections.unmodifiableMap(map);
    }
  }

    We can see from the source code that we are using ";" for outterSplitter and "=" for entrySplitter.

    @Test
    public void splitTest() {
	String str = "Name = Davy; Age = 24; Gender = Male";
	Map<String, String> expectedMap = Maps.newLinkedHashMap();
	expectedMap.put("Name", "Davy");
	expectedMap.put("Age", "24");
	expectedMap.put("Gender", "Male");
	Map<String, String> generatedMap = Splitter.on(';').trimResults()
		.withKeyValueSeparator(Splitter.on("=").trimResults())
		.split(str);
	assertEquals(expectedMap, generatedMap);
    }

    In the example above, we are using ";" as outterSplitter and thus trimResults to "Name = Davy", "Age = 24", "Gender = Male"

    And we are using "=" as entrySplitter and thus trimResults to "Name", "Davy"...

 

3. Preconditions

    The Preconditions class is a collection of static methods used to verify the state of our code.

    public void setName(String name) {
	if (null == name) {
	    throw new IllegalArgumentException("name cannot be null");
	}
    }
    public void setName(String name) {
        Preconditions.checkNotNull(name);
    }
    public void setName(String name) {
        Preconditions.checkArgument(null != name && !"".equals(name.trim()));
    }
    @Test
    public void conditionsTest() {
	String[] data = new String[] { "A", "B", "C", "D" };
	getValue(data, 4);
    }
    private void getValue(String[] data, int i) {
        Preconditions.checkElementIndex(data.length, i);
    }

 

Reference Links:

1> "Getting Started with Google Guava" -Bill Bejeck

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值