ArrayStringBuilder makes StringBuilder acts as an array

String concat is something that we need to do a lot when creating SQLs but there is a problem by using string-plus-string concatenation (“string”+”"string”): Every couple of quotations java will create and alocate one String class, this can be a trigger for memory problems.

The orientation (at Celepar) is to use StringBuffer that is faster than simple concat. Searching the web i found that StringBuilder is a bit faster than StringBuffer.

Now let me go to the point, i needed to make an join and use a separator at a group of strings, but memory was something i was worried, then i tried to simulate an array over StringBuilder,  the result was the following class.

p.s: I really doen’t feel that there aren’t a better and faster way to do this, but i didn’t found anything at this time;

package com.br.webcentro.utils;

import java.util.Arrays;

/**
 * This class works using the java.lang.StringBuilder,
 * but this simulates an array to implement a joinable splitable string
 *
 * may be better extend the class AbstractStringBuilder but for now it will only work using it;
 *
 * feel free to post implementations
 *
 * If you have some advice please contact me at webcentro(at)gmail.com
 * @author alandanielweiss
 * @since 2010-01-22
 */

public class ArrayStringBuilder implements java.io.Serializable{

	private StringBuilder builder;

	private boolean isMultiEnabled = false;
	private boolean isEmptyAllowed = true;

	private int start[];
	private int len[];
	private int counter=0;
	private int totalLen=0;

	private static final long serialVersionUID = -2443226579329551214L;

	/**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */

	public ArrayStringBuilder() {
		this.builder = new StringBuilder();
		this.start   = new int[16];
		this.len     = new int[16];
    }

	/**
	 * Sets the initial StringBuilder capacity
	 * @param capacity
	 */
	public ArrayStringBuilder(int capacity) {
		this.builder = new StringBuilder(capacity);
		this.start   = new int[capacity];
		this.len     = new int[capacity];
    }

	/**
	 * Create class and append string
	 * @param string
	 */
    public ArrayStringBuilder(String string) {
    	this();
    	this.add(string);
    }

	/**
	 * Create class and the the StringBuilder
	 * @param string
	 */
    public ArrayStringBuilder(StringBuilder builder) {
		this.start   = new int[16];
		this.len     = new int[16];

		this.counter = 1;
    	this.start[0] = 0;
    	this.len[0] = builder.toString().length();
    	this.totalLen = this.len[0];

    	this.builder = builder;
    }

    /**
     * Append a string
     * @param str
     * @return
     */
	public ArrayStringBuilder add(String str){
		if (str == null)
			str = "null";
	    int len = str.length();
		if (len == 0 && !isEmptyAllowed)
			return this;

		//multi-line don't increment counter
		if(!this.isMultiEnabled)
			this.counter ++;

		if (this.counter > start.length)
		    expandCapacity(this.counter);

		if(!this.isMultiEnabled)
			this.start[this.counter-1] = this.totalLen;

		if(this.isMultiEnabled)
			len +=this.len[this.counter-1];

		this.len[this.counter-1]   = len;

		this.totalLen += str.length();
		this.builder.append(str);
		return this;
	}

	/**
	 * start multiline array
	 * when done use the method end()
	 * @return
	 */
	public ArrayStringBuilder start(String str){
		this.isMultiEnabled = false;
		this.add(str);
		this.isMultiEnabled = true;
		return this;
	}

	/**
	 * finish multiline array
	 * @return
	 */
	public ArrayStringBuilder finish(String str){
		this.add(str);
		this.isMultiEnabled = false;
		return this;
	}

	void expandCapacity(int minimumCapacity) {
		int newCapacity = (start.length + 1) * 2;
        if (newCapacity < 0) {
        	newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        	newCapacity = minimumCapacity;
		}
        this.start = Arrays.copyOf(this.start, newCapacity);
        this.len = Arrays.copyOf(this.len, newCapacity);
    }

	/**
	 * Joins the StringBuilder using the passed separator
	 * @param separator
	 * @return
	 */
	public String join(String separator){
		StringBuilder str = new StringBuilder();
		for (int idx=0; idx<this.counter;idx++){
			str.append(this.builder.substring(this.start[idx], this.start[idx]+this.len[idx]));
			if(idx+1<this.counter)
				str.append(separator);
	    }
		return str.toString();
	}

	public String[] toArray(){
		String[] str = new String[this.counter];
		for (int idx=0; idx<this.counter;idx++){
			str[idx] = this.builder.substring(this.start[idx], this.start[idx]+this.len[idx]);
	    }
		return str;
	}

	/**
	 * To string...
	 * @return String
	 */
    public String toString() {
    	return this.builder.toString();
    }

    /**
     * Creates a StringBuilder with the actual object values
     * @return builder:StringBuilder
     */
    public StringBuilder getStringBuilder(){
    	return this.builder;
    }

    /**
     * when true every time you use add/start/finish with an empty string it will add a new array
     * if false the command add/start/finish empty has no effect
     */
    public void setEmptyAllowed(boolean isMultiEnabled){
    	this.isMultiEnabled = isMultiEnabled;
    }

    public static void main(String[] args) {
		//Sample 1
    	StringBuilder strb = new StringBuilder();
    	strb.append("to be");
    	ArrayStringBuilder arrb = new ArrayStringBuilder(strb);
    	arrb.start(" or ");
    	arrb.add("not ");
    	arrb.add("to ");
    	arrb.finish("be");
		ArrayStringBuilder arrb2 = new ArrayStringBuilder(arrb.join(","));
		System.out.println(arrb2.add(": that's").add("the").add("question").join(" "));

		//Sample 2
		ArrayStringBuilder fields = new ArrayStringBuilder();
		fields.add("id");
		if(1==1)
			fields.add("name");
		if(1==2)
			fields.add("profession");
		StringBuilder sql = new StringBuilder();
		sql.append("select ").append(fields.join(" , ")).append(" from table ");
		System.out.println(sql.toString());
	}
}

A good comparision between StringBuilder and StringBuffer can be found at: StringBuilder vs StringBuffer vs String.concat – done right

Please feel free to send comments, to improve this class.

  1. Nenhum trackbacks ainda.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.