Hiển thị các bài đăng có nhãn Guava. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Guava. Hiển thị tất cả bài đăng

Thứ Ba, 1 tháng 5, 2012

Recent Interesting Software Development Posts - 1 May 2012

This post references and very briefly summarizes several recent posts on subjects of interest to software developers such as NetBeans 7.1.2, progress on Java 8's Lambda Expressions, Guava Release 12, TestNG, Scala, and the expense of ineffective meetings.

NetBeans 7.1.2

The release of NetBeans 7.1.2 includes JDK 7 Update 4 (first version of Java SE 7 for Mac OS X).

State of the Lambda: Libraries Edition

After attending JavaOne 2011, I believe that lambda expressions are going to dramatically change how we write Java. Therefore, it is always interesting to read about progress related to Lambda expressions. In the April 2012 post State of the Lambda: Libraries Edition, Brian Goetz "describes the design approach taken in the rough prototype that has been implemented in the Lambda Project repository." He points out that this description is "intended as a working straw-man proposal" and that "the final version may look different." Another Goetz April 2012 post (Translation of Lambda Expressions) "outlines the strategy for translating lambda expressions and method references from Java source code into bytecode."

Guava Release 12

Guava Release 12 was released this week. According to the Guava Release 12 Release Notes, this is the first release of Guava that requires Java SE 6: "Guava 12.0 is the first release to require JDK6. Users requiring JDK5 compatibility may continue to use Guava 11.0.2"

TestNG Rather than JUnit

Tomek Kaczanowski has posted the slides he prepared "to persuade my colleagues to migrate from JUnit to TestNG" in his post Why TestNG and Not JUnit?

Scala or Java? Exploring Myths and Facts

The post Scala or Java? Exploring myths and facts addresses some of the alleged pros and cons of Scala articulated online. The author addresses questions about productivity, complexity, concurrency support, tooling, extensibility, interoperability, performance, and backwards compatibility.

The Expense of Ineffective Meetings

Jeffrey Scott Klubeck's post The Expense of Ineffective Meetings is not necessarily new, but it is new to me. This short post is well worth the couple of minutes to read and articulates well what many of us have felt after having a particularly useless meeting foisted upon us. I love the quote, "Patrick Lencioni, author of Death by Meeting, says that bad meetings not only exact a toll on the attendees as they suffer through them, but also cause real human anguish in the form of anger, lethargy, cynicism, and even in the form of lower self-esteem."

10 Hard Truths Developer Must Accept

In the post 10 hard truths developers must learn to accept, Peter Wayner offers ten things that are reminders that "programming offers an array of bitter pills to swallow."

SSH Tunneling Explained

Buddhika Chamith's post SSH Tunneling Explained is a detailed and illustrated overview of SSH tunneling.

Conclusion

This post has referenced a small set of other blog posts, articles, and announcements that I have found particularly interesting in recent weeks.

Thứ Hai, 26 tháng 12, 2011

Guava Release 11's IntMath

As I stated earlier in the post Sneaking a Peek at Guava Release 11, Guava Release 11 provides numerous new classes including several classes specifically related to mathematical operations. In this post, I look at one of these that is targeted at integer math: Guava's IntMath.

As stated in my previous post, com.google.common.math.IntMath is largely based on Henry S. Warren, Jr.'s Hacker's Delight. As the class name indicates, the operations of this class are focused on "arithmetic on values of type int."

The next screen snapshot shows the methods and static attributes supported by IntMath as listed by javap:

For convenience, I have listed a text version of the above javap output here.


Compiled from "IntMath.java"
public final class com.google.common.math.IntMath {
static final int MAX_POWER_OF_SQRT2_UNSIGNED;
static final int[] POWERS_OF_10;
static final int[] HALF_POWERS_OF_10;
static final int FLOOR_SQRT_MAX_INT;
static final int[] FACTORIALS;
static int[] BIGGEST_BINOMIALS;
public static boolean isPowerOfTwo(int);
public static int log2(int, java.math.RoundingMode);
public static int log10(int, java.math.RoundingMode);
public static int pow(int, int);
public static int sqrt(int, java.math.RoundingMode);
public static int divide(int, int, java.math.RoundingMode);
public static int mod(int, int);
public static int gcd(int, int);
public static int checkedAdd(int, int);
public static int checkedSubtract(int, int);
public static int checkedMultiply(int, int);
public static int checkedPow(int, int);
public static int factorial(int);
public static int binomial(int, int);
static {};
}

As the image and text output above indicate, the IntMath class has much to offer in terms of functionality related to integer arithmetic. The remainder of this post will demonstrate this.

Factorial calculation is commonly implemented in software development examples, especially in academic contexts such as illustrating recursion. The blog post Implementing the Factorial Function Using Java and Guava is devoted to different implementations of factorial implemented in Java and Guava. Guava Release 11 now provides a method for calculating factorial in IntMath.factorial(int).

Guava's IntMath.factorial(int) Demonstrated

/**
* Demonstrate factorial calculation.
*/
public static void demoFactorial()
{
final int x = 5;
final int factorial = IntMath.factorial(x);
out.println("Factorial of x=" + x + " is " + factorial);
}

Another often mathematical operation supported by Guava Release 11 is the calculation of the binomial coefficient via IntMath.binomial(int,int).

Guava's IntMath.binomial(int,int) Demonstrated

/**
* Demonstrate binomial Coefficient calculation
* (http://en.wikipedia.org/wiki/Binomial_coefficient).
*/
public static void demoBinomialCoefficient()
{
final int n = 5;
final int k = 3;
final int binomialCoefficient = IntMath.binomial(n, k);
out.println(
"Binomial Coefficient of n=" + n + " and k=" + k + " is "
+ binomialCoefficient);
}

Guava Release 11 also adds a method for calculating the greatest common divisor of two provided integers.

Guava's IntMath.gcd(int,int) Demonstrated

/**
* Demonstrate calculation of greatest common factor (GCF) [called greatest
* common divisor here].
*/
public static void demoGreatestCommonFactor()
{
final int x = 30;
final int y = 45;
final int gcf = IntMath.gcd(x, y);
out.println("GCF of " + x + " and " + y + " is " + gcf);
}

Guava Release 11 provides a method for calculating an integer square root of a provided integer, rounding to the integer result when needed based on the provided RoundingMode. The fact that the result of the square root function is an integer distinguishes this method from Math.sqrt(double).

Guava's IntMath.sqrt(int) Demonstrated

/**
* Demonstrate calculation of square roots.
*/
public static void demoSquareRoot()
{
final int x = 16;
final int sqrtX = IntMath.sqrt(x, RoundingMode.HALF_EVEN);
out.println("Square root of " + x + " is " + sqrtX);
final int y = 25;
final int sqrtY = IntMath.sqrt(y, RoundingMode.HALF_EVEN);
out.println("Square root of " + y + " is " + sqrtY);
}

Guava Release 11's IntMath provides the method pow(int,int) for the first integer multiplied by itself the number of times expressed by the second integer (similar to Math.pow(double,double), but accepting and returning integers rather than doubles.). The IntMath.pow(int,int) method provides handling for situations in which the operation would normally result in an overflow. In such a situation, according to the method's Javadoc, the method will return a result that "will be equal to BigInteger.valueOf(b).pow(k).intValue()."

Guava's IntMath.pow(int,int) Demonstrated

/**
* Demonstrate exponential power calculation.
*/
public static void demoExponentialPower()
{
final int base = 2;
final int exponent = 5;
final int result = IntMath.pow(base, exponent);
out.println(base + " to power of " + exponent + " is " + result);
}

A particularly interesting method that Guava Release 11 provides that could be especially useful in certain software development and computer contexts is the method IntMath.isPowerOfTwo(int). This method returns a boolean indicating whether the provided integer is evenly divisible (no remainder) by two.

Guava's IntMath.isPowerOfTwo(int) Demonstrated

/**
* Demonstrate determination of whether an integer is a power of two.
*/
public static void demoIsPowerOfTwo()
{
final int x = 16;
out.println(x + (IntMath.isPowerOfTwo(x) ? " IS " : " is NOT " ) + " a power of two.");
final int y = 31;
out.println(y + (IntMath.isPowerOfTwo(y) ? " IS " : " is NOT " ) + " a power of two.");
}

Guava's IntMath class provides two methods for performing logarithmic calculations, specifically focusing on the common logarithm (base 10) and the binary logarithm (base 2). Both of these are demonstrated in the next code listing.

Guava's IntMath.log10(int,RoundingMode) and IntMath.log2(int,RoundingMode) Demonstrated

/**
* Demonstrate IntMath.log10 and IntMath.log2.
*/
public static void demoLogarithmicFunctions()
{
final int x = 10000000;
final int resultX = IntMath.log10(x, RoundingMode.HALF_EVEN);
out.println("Logarithm (base 10) of " + x + " is " + resultX);
final int y = 32;
final int resultY = IntMath.log2(y, RoundingMode.HALF_EVEN);
out.println("Logarithm (base 2) of " + y + " is " + resultY);
}

Guava Release 11's IntMath.divide(int,int,RoundingMode) allows for integer division in which the type of rounding used in the division can be specified as part of the call. This is more flexible than direct Java integer division which always rounds the quotient down to the lower integer (floor).

Guava's IntMath.divide(int,int,RoundingMode) Demonstrated

/**
* Demonstrate division using IntMath.divide.
*/
public static void demoDivision()
{
final int dividend = 30;
final int divisor = 10;
final int quotient = IntMath.divide(dividend, divisor, RoundingMode.HALF_EVEN);
out.println(dividend + " / " + divisor + " = " + quotient);
}

I mentioned previously that Guava Release 11's IntMath.pow(int,int) would handle overflow situations by returning "BigInteger.valueOf(b).pow(k).intValue()" where 'b' is the first integer (base) and 'k' is the second integer (power/exponent). In some cases, it may be preferable to have an exception thrown when the overflow situation occurs rather than "hiding" the issue. In such cases, Guava Release 11's IntMath.checkedPow(int,int) is desirable because it will throw an ArithmeticException if the operation results in an overflow.

IntMath.checkedPow(int,int) Demonstrated

/**
* Demonstrate Guava Release 11's checked power method and compare it to
* other common approaches for determining base multiplied by itself exponent
* number of times.
*/
public static void demoCheckedPower()
{
try
{
final int base = 2;
final int exponent = 4;
final int result = IntMath.checkedPow(base, exponent);
out.println("IntMath.checkedPow: " + base + "^" + exponent + " = " + result);

out.println(
"IntMath.pow: " + Integer.MAX_VALUE + "^2 = " +
+ IntMath.pow(Integer.MAX_VALUE, 2));
out.println(
"Math.pow(int,int): " + Integer.MAX_VALUE + "^2 = "
+ Math.pow(Integer.MAX_VALUE, 2));
out.println("Multiplied: " + Integer.MAX_VALUE*Integer.MAX_VALUE);
out.print("IntMath.checkedPow: " + Integer.MAX_VALUE + "^2 = ");
out.println(IntMath.checkedPow(Integer.MAX_VALUE, 2));
}
catch (Exception ex)
{
err.println("Exception during power: " + ex.toString());
}
}

Guava Release 11's IntMath class provides three more "checked" methods that throw an ArithmeticException when the given mathematical operation results in an overflow condition. These methods are for addition, substraction, and multiplication and are respectively called IntMath.checkedAdd(int,int), IntMath.checkedSubtract(int,int), and IntMath.checkedMultiply(int,int). As discussed in conjunction with IntMath.checkedPow(int,int), the advantage of this occurs in situations where it is better to have an exception and know overflow occurred than to blindly operate on an erroneous value due to an overflow condition.

Guava's checkedAdd(int,int), checkedSubtract(int,int), and checkedMultiply(int,int) Demonstrated

/**
* Demonstrate Guava Release 11's checked addition method.
*/
public static void demoCheckedAddition()
{
try
{
final int augend = 20;
final int addend = 10;
final int sum = IntMath.checkedAdd(augend, addend);
out.println(augend + " + " + addend + " = " + sum);

final int overflowSum = IntMath.checkedAdd(Integer.MAX_VALUE, 1);
out.println(Integer.MAX_VALUE + " + 1 = " + overflowSum);
}
catch (Exception ex)
{
err.println("Exception during addition: " + ex.toString());
}
}

/**
* Demonstrate Guava Release 11's checked subtraction method.
*/
public static void demoCheckedSubtraction()
{
try
{
final int minuend = 30;
final int subtrahend = 20;
final int difference = IntMath.checkedSubtract(minuend, subtrahend);
out.println(minuend + " - " + subtrahend + " = " + difference);

final int overflowDifference = IntMath.checkedSubtract(Integer.MIN_VALUE, 1);
out.println(Integer.MIN_VALUE + " - 1 = " + overflowDifference);
}
catch (Exception ex)
{
err.println("Exception during subtraction: " + ex.toString());
}
}

/**
* Demonstrate Guava Release 11's checked multiplication method.
*/
public static void demoCheckedMultiplication()
{
try
{
final int factor1 = 3;
final int factor2 = 10;
final int product = IntMath.checkedMultiply(factor1, factor2);
out.println(factor1 + " * " + factor2 + " = " + product);

final int overflowProduct = IntMath.checkedMultiply(Integer.MAX_VALUE, 2);
out.println(Integer.MAX_VALUE + " * 2 = " + overflowProduct);
}
catch (Exception ex)
{
err.println("Exception during multiplication: " + ex.toString());
}
}

The blog post Handling Very Large Numbers in Java talks about issues with large numbers in Java and the overflow that can occur. As this post recommends, use of BigInteger and BigDecimal is often recommended for such situations. However, these new Guava IntMath "checked" methods provide another alternative for the Java developer who wants to deal with integers, but know when overflow has occurred.

I have shown simple examples of using most of the methods of the new IntMath class in Guava Release 11 [I did not discuss or show IntMath.mod(int,int)]. The next code listing ties all of the above examples together and is followed by the output from running that code listing.

UsingIntMath.java

package dustin.examples;

import static java.lang.System.err;
import static java.lang.System.out;

import com.google.common.math.IntMath;
import java.math.RoundingMode;

/**
* Simple examples of using Guava Release 11's {@code IntMath} class.
*
* @author Dustin
*/
public class UsingIntMath
{
/**
* Demonstrate binomial Coefficient calculation
* (http://en.wikipedia.org/wiki/Binomial_coefficient).
*/
public static void demoBinomialCoefficient()
{
final int n = 5;
final int k = 3;
final int binomialCoefficient = IntMath.binomial(n, k);
out.println(
"Binomial Coefficient of n=" + n + " and k=" + k + " is "
+ binomialCoefficient);
}

/**
* Demonstrate factorial calculation.
*/
public static void demoFactorial()
{
final int x = 5;
final int factorial = IntMath.factorial(x);
out.println("Factorial of x=" + x + " is " + factorial);
}

/**
* Demonstrate calculation of greatest common factor (GCF) [called greatest
* common divisor here].
*/
public static void demoGreatestCommonFactor()
{
final int x = 30;
final int y = 45;
final int gcf = IntMath.gcd(x, y);
out.println("GCF of " + x + " and " + y + " is " + gcf);
}

/**
* Demonstrate calculation of square roots.
*/
public static void demoSquareRoot()
{
final int x = 16;
final int sqrtX = IntMath.sqrt(x, RoundingMode.HALF_EVEN);
out.println("Square root of " + x + " is " + sqrtX);
final int y = 25;
final int sqrtY = IntMath.sqrt(y, RoundingMode.HALF_EVEN);
out.println("Square root of " + y + " is " + sqrtY);
}

/**
* Demonstrate determination of whether an integer is a power of two.
*/
public static void demoIsPowerOfTwo()
{
final int x = 16;
out.println(x + (IntMath.isPowerOfTwo(x) ? " IS " : " is NOT " ) + " a power of two.");
final int y = 31;
out.println(y + (IntMath.isPowerOfTwo(y) ? " IS " : " is NOT " ) + " a power of two.");
}

/**
* Demonstrate exponential power calculation.
*/
public static void demoExponentialPower()
{
final int base = 2;
final int exponent = 5;
final int result = IntMath.pow(base, exponent);
out.println(base + " to power of " + exponent + " is " + result);
}

/**
* Demonstrate IntMath.log10 and IntMath.log2.
*/
public static void demoLogarithmicFunctions()
{
final int x = 10000000;
final int resultX = IntMath.log10(x, RoundingMode.HALF_EVEN);
out.println("Logarithm (base 10) of " + x + " is " + resultX);
final int y = 32;
final int resultY = IntMath.log2(y, RoundingMode.HALF_EVEN);
out.println("Logarithm (base 2) of " + y + " is " + resultY);
}

/**
* Demonstrate Guava Release 11's checked addition method.
*/
public static void demoCheckedAddition()
{
try
{
final int augend = 20;
final int addend = 10;
final int sum = IntMath.checkedAdd(augend, addend);
out.println(augend + " + " + addend + " = " + sum);

final int overflowSum = IntMath.checkedAdd(Integer.MAX_VALUE, 1);
out.println(Integer.MAX_VALUE + " + 1 = " + overflowSum);
}
catch (Exception ex)
{
err.println("Exception during addition: " + ex.toString());
}
}

/**
* Demonstrate Guava Release 11's checked subtraction method.
*/
public static void demoCheckedSubtraction()
{
try
{
final int minuend = 30;
final int subtrahend = 20;
final int difference = IntMath.checkedSubtract(minuend, subtrahend);
out.println(minuend + " - " + subtrahend + " = " + difference);

final int overflowDifference = IntMath.checkedSubtract(Integer.MIN_VALUE, 1);
out.println(Integer.MIN_VALUE + " - 1 = " + overflowDifference);
}
catch (Exception ex)
{
err.println("Exception during subtraction: " + ex.toString());
}
}

/**
* Demonstrate Guava Release 11's checked multiplication method.
*/
public static void demoCheckedMultiplication()
{
try
{
final int factor1 = 3;
final int factor2 = 10;
final int product = IntMath.checkedMultiply(factor1, factor2);
out.println(factor1 + " * " + factor2 + " = " + product);

final int overflowProduct = IntMath.checkedMultiply(Integer.MAX_VALUE, 2);
out.println(Integer.MAX_VALUE + " * 2 = " + overflowProduct);
}
catch (Exception ex)
{
err.println("Exception during multiplication: " + ex.toString());
}
}

/**
* Demonstrate Guava Release 11's checked power method and compare it to
* other common approaches for determining base multiplied by itself exponent
* number of times.
*/
public static void demoCheckedPower()
{
try
{
final int base = 2;
final int exponent = 4;
final int result = IntMath.checkedPow(base, exponent);
out.println("IntMath.checkedPow: " + base + "^" + exponent + " = " + result);

out.println(
"IntMath.pow: " + Integer.MAX_VALUE + "^2 = " +
+ IntMath.pow(Integer.MAX_VALUE, 2));
out.println(
"Math.pow(int,int): " + Integer.MAX_VALUE + "^2 = "
+ Math.pow(Integer.MAX_VALUE, 2));
out.println("Multiplied: " + Integer.MAX_VALUE*Integer.MAX_VALUE);
out.print("IntMath.checkedPow: " + Integer.MAX_VALUE + "^2 = ");
out.println(IntMath.checkedPow(Integer.MAX_VALUE, 2));
}
catch (Exception ex)
{
err.println("Exception during power: " + ex.toString());
}
}

/**
* Demonstrate division using IntMath.divide.
*/
public static void demoDivision()
{
final int dividend = 30;
final int divisor = 10;
final int quotient = IntMath.divide(dividend, divisor, RoundingMode.HALF_EVEN);
out.println(dividend + " / " + divisor + " = " + quotient);
}

/**
* Main function for demonstrating Guava Release 11's {@code IntMath} class.
*
* @param arguments Command-line arguments; none expected.
*/
public static void main(final String[] arguments)
{
demoBinomialCoefficient();
demoFactorial();
demoGreatestCommonFactor();
demoSquareRoot();
demoIsPowerOfTwo();
demoExponentialPower();
demoLogarithmicFunctions();
demoCheckedAddition();
demoCheckedSubtraction();
demoCheckedMultiplication();
demoCheckedPower();
demoDivision();
}
}

This blog post has attempted to demonstrate the usefulness and ease-of-use provided by Guava Release 11's IntMath class. This class provides numerous convenient method for simplifying common mathematical operations on integers. Guava Release 11 provides essentially the same methods in the com.google.common.math package for longs (LongMath) and similar methods for double's (DoubleMath) and BigIntegers (BigIntegerMath).

Thứ Ba, 13 tháng 12, 2011

Sneaking a Peek at Guava Release 11

The recent reddit/java thread If you still don't know why Guava is awesome .... references the Guava Explained Wiki page. As I was looking at this, I noticed the page focused on Guava Math Explained. I have blogged several times regarding Guava features, but I was not aware of the referenced math-related classes. That's when I realized that these refer to Guava Release 11. At that point, it seemed interesting to snoop out other enhancements we might expect from the next release of Guava. I briefly cover some of the clues already available as to the enhancements coming with Guava Release 11, but caveat this discussion with the obvious disclaimers that things are subject to change before its final release and that I have no inside knowledge of Guava's inner workings.

The previously referenced Guava Explained Wiki page is only one of several of Guava's Wiki pages that have been updated by wasserman.louis in the past 36 hours (from this post's publication).

Most of the updated Guava Wiki pages appear to be updated to associate the "Explained" label with the entry and tie it in to the "Guava Explained" wiki page. However, the Guava Math Explained Wiki page is definitely new.

The classes in the Guava Math utilities appear to belong to the com.google.common.math package with is an all-new package containing four classes: BigIntegerMath, DoubleMath, IntMath, and LongMath. The class-level Javadoc description for each of these four Guava math classes tell us that each class has "implementations of many methods" which "are based on material from Henry S. Warren, Jr.'s Hacker's Delight, (Addison Wesley, 2002)." As is the case with many of Guava's other classes, these classes supplement the methods provided by Java SDK's Math and other mathematical classes.

Although the main Guava Libraries page currently points to Guava Release 10 Javadoc as its "API docs for the most recent release," the "pre-release API docs in git" currently points to documentation that references Guava Release 11. For example, while com.google.common.math is not available in the standard Guava API documentation (Release 10), the package is documented in the pre-release API documentation (Release 11). The newer, "pre-release" API documentation gives us some of our best clues regarding likely future Guava features.

In the blog post Groovy Script for Comparing Javadoc Versions, I used a Groovy script to compare versions of Java by comparing the constructs they added or removed in each new version. I made some adaptations to that script to compare arbitrary listing of Strings and have applied it to compare the "most recent release" (10) of Guava to the "pre-release" (11). Here is the modified script for comparing the two.

diffVersionsJavadocs.groovy

#!/usr/bin/env groovy
// diffVersionsJavadocs.groovy
//
// Examples using Guava:
//
// Guava Release 10:
// http://docs.guava-libraries.googlecode.com/git-history/v10.0.1/javadoc/allclasses-noframe.html
// Guava Release 11:
// http://docs.guava-libraries.googlecode.com/git/javadoc/allclasses-noframe.html

if (args.length < 4)
{
println "\nUSAGE: diffVersionsJavadocs first_label first_url second_label second_url"
println "\n\twhere first_label is name of first version"
println "\t first_url is URL of Javadoc for first version (usually allclasses)"
println "\t second_label is name of second version"
println "\t second_url is URL of Javadoc for second version (usually allclasses)"
System.exit(-1)
}
@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')

def firstXml = new XmlParser(new org.ccil.cowan.tagsoup.Parser()).parse(args[1])
def firstUrls = firstXml.'**'.a.@href
def firstLabel = args[0]

def secondXml = new XmlParser(new org.ccil.cowan.tagsoup.Parser()).parse(args[3])
def secondUrls = secondXml.'**'.a.@href
def secondLabel = args[2]

compareSetsOfStrings(secondUrls, secondLabel, firstUrls, firstLabel)

/**
* Compare first Collection of Strings to second Collection of Strings by
* identifying which Strings are in each Collection that are not in the other.
*
* @param firstStrings First Collection of Strings to be compared.
* @param firstLabel Name of first collection of Strings.
* @param secondStrings Second Collection of Strings to be compared.
* @param secondLabel Name of second collection of Strings.
*/
def void compareSetsOfStrings(
Collection<String> firstStrings, String firstLabel,
Collection<String> secondStrings, String secondLabel)
{
println "Constructs in ${firstLabel} But Not in ${secondLabel}"
def firstButNotSecond = firstStrings - secondStrings
printIndentedStrings(firstButNotSecond)

println "Constructs in ${secondLabel} But Not in ${firstLabel}"
def secondButNotFirst = secondStrings - firstStrings
printIndentedStrings(secondButNotFirst)
}


/**
* Print the provided Strings one per line indented; prints "None" if the
* provided List of Strings is empty or null.
*
* @param strings The Strings to be printed
*/
def void printIndentedStrings(Collection<String> strings)
{
if (!strings?.isEmpty())
{
new TreeSet(strings).each
{
println "\t${it}"
}
}
else
{
println "\tNone"
}
}

For convenience, I placed the two URLs I provided to the script for comparing Guava 10 to Guava 11 in the script comments at the beginning of the script. The next screen snapshot shows running the script and its output.

The Groovy script comparing Javadoc for Guava Release 10 to Guava Release 11 indicates that four constructs have been removed from Guava Release 11 that were in Guava Release 9. These are EvictionListeners (class), MapEvictionListener (interface), AbstractListenableFuture (class), and UninterruptibleFuture (interface). All of these had been clearly marked in the Javadoc for deprecation in the Release 10 documentation, including specific statements that they were scheduled to be removed in Release 11 and what should be used instead of them. (I had posted in Effective Javadoc Documentation Illustrated in Familiar Projects that this advertised version from which a deprecated construct would be removed is a particularly effective documentation tool.)

Guava Release 11 introduces several entirely new constructs. These were shown in the screen snapshot above but are listed here in bullet form for convenience.

Constructs in Release 11 But Not in Release 10 (per Javadoc comparison)
  • com/google/common/cache/AbstractLoadingCache.html
  • com/google/common/cache/CacheLoader.InvalidCacheLoadException.html
  • com/google/common/cache/ForwardingLoadingCache.SimpleForwardingLoadingCache.html
  • com/google/common/cache/ForwardingLoadingCache.html
  • com/google/common/cache/LoadingCache.html
  • com/google/common/cache/Weigher.html
  • com/google/common/collect/ImmutableTable.Builder.html
  • com/google/common/collect/ImmutableTable.html
  • com/google/common/collect/Queues.html
  • com/google/common/collect/RangeSet.html
  • com/google/common/collect/SortedMultiset.html
  • com/google/common/escape/ArrayBasedCharEscaper.html
  • com/google/common/escape/ArrayBasedEscaperMap.html
  • com/google/common/escape/ArrayBasedUnicodeEscaper.html
  • com/google/common/escape/CharEscaper.html
  • com/google/common/escape/CharEscaperBuilder.html
  • com/google/common/escape/Escaper.html
  • com/google/common/escape/Escapers.Builder.html
  • com/google/common/escape/Escapers.html
  • com/google/common/escape/SourceCodeEscapers.html
  • com/google/common/escape/UnicodeEscaper.html
  • com/google/common/html/HtmlEscapers.html
  • com/google/common/math/BigIntegerMath.html
  • com/google/common/math/DoubleMath.html
  • com/google/common/math/IntMath.html
  • com/google/common/math/LongMath.html
  • com/google/common/net/HttpHeaders.html
  • com/google/common/net/PercentEscaper.html
  • com/google/common/net/UriEscapers.html
  • com/google/common/primitives/UnsignedInteger.html
  • com/google/common/primitives/UnsignedInts.html
  • com/google/common/primitives/UnsignedLong.html
  • com/google/common/util/concurrent/AbstractScheduledService.CustomScheduler.html
  • com/google/common/util/concurrent/AbstractScheduledService.Scheduler.html
  • com/google/common/util/concurrent/AbstractScheduledService.html
  • com/google/common/util/concurrent/AsyncFunction.html
  • com/google/common/util/concurrent/AtomicDouble.html
  • com/google/common/util/concurrent/AtomicDoubleArray.html
  • com/google/common/util/concurrent/AtomicLongMap.html
  • com/google/common/xml/XmlEscapers.html

Besides the new math classes, other Groovy constructs that I look forward to trying out include the new collection classes, the new concurrency classes, the new unsigned primitive classes, and the escapers (com.google.common.escape package, com.google.common.html.HtmlEscapers class, com.google.common.net.PercentEscaper, com.google.common.net.UriEscapers, and com.google.common.xml.XmlEscapers).

The script executed above only showed entirely new constructs to Guava Release 11, but Guava Release 11 will also be getting new methods on existing objects. For example, the highly useful Guava Strings class appears to be getting two new methods [commonPrefix(CharSequence,CharSequence) and commonSuffix(CharSequence,CharSequence).] and Objects.ToStringHelper appears to be getting numerous new convenience methods.

Being an open source project, Guava's new features may be best "peeked" at by looking at the source code itself. Guava's Javadoc documentation is generated such that clicking on a construct's name in its Javadoc page will display that construct's source code. In some cases, this may be quicker than downloading the code, though the advantage of downloading source code is obviously the ability to view it in your favorite IDE.

Thứ Sáu, 25 tháng 11, 2011

File Management in Java with Guava's Files Class

Both Groovy and Java SE 7 provide improvements for file management in Java as I discussed in posts here, here, here, and here. However, when a particular Java application is not able to yet use Java SE 7 or Groovy for its file management, an improved file management experience can still be obtained by using Guava's Files class.

File handling has always seemed a little more difficult in Java than in many other programming languages. Java SE 7 certainly improves Java's file handling capabilities dramatically with NIO.2, but not every project can make use of Java SE 7. For those projects not able to use Java SE 7, Guava's Files class is a nice intermediate solution for easier file handling. It is important to note here that Java SE 7 introduces a Files class of its own, so any use of Guava's Files class in Java SE 7 must be fully scoped if the Java version of Files is used in the same code. My examples have been written and built in Java SE 7, but I avoid using Java SE 7's Files class and so don't need to fully scope Guava's same-named class.

File Creation

Guava's Files class includes a couple overloaded write methods for easily writing content to a file. The next code sample demonstrates using Files.write(byte[],File).

Demonstrating Files.write(byte[],File)

/**
* Demonstrate writing bytes to a specified file.
*
* @param fileName Name of file to be written to.
* @param contents Contents to be written to file.
*/
public void demoFileWrite(final String fileName, final String contents)
{
checkNotNull(fileName, "Provided file name for writing must NOT be null.");
checkNotNull(contents, "Unable to write null contents.");
final File newFile = new File(fileName);
try
{
Files.write(contents.getBytes(), newFile);
}
catch (IOException fileIoEx)
{
err.println( "ERROR trying to write to file '" + fileName + "' - "
+ fileIoEx.toString());
}
}

There are a couple observations that can be from this first code sample that will apply to all other code samples in this post. First, I make use of the statically imported Guava Preconditions class to provide an easy check ensuring that the provided parameters are not null. The second common feature of this code is that it explicitly catches and "handles" the checked exception IOException. All other Files methods demonstrated in this post similarly throw this same checked exception.

File Copying

The next example demonstrates how easy it is to copy files using Guava's Files.copy(File,File) method (one of several overloaded methods with name 'copy').

Demonstrating Files.copy(File,File)

/**
* Demonstrate simple file copying in Guava. This demonstrates one of the
* numerous overloaded copy methods provided by Guava's Files class. The
* version demonstrated here copies one provided File instance to another
* provided File instance.
*
* @param sourceFileName Name of file that is to be copied.
* @param targetFileName Name of file that is result of file copying.
*/
public void demoSimpleFileCopy(
final String sourceFileName, final String targetFileName)
{
checkNotNull(sourceFileName, "Copy source file name must NOT be null.");
checkNotNull(targetFileName, "Copy target file name must NOT be null.");
final File sourceFile = new File(sourceFileName);
final File targetFile = new File(targetFileName);
try
{
Files.copy(sourceFile, targetFile);
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to copy file '" + sourceFileName
+ "' to file '" + targetFileName + "' - " + fileIoEx.toString());
}
}
File Moving

Moving files with Guava is as easy as copying. The Files.move(File,File) method is demonstrated in the next code snippet.

Demonstrating Files.move(File,File)

/**
* Demonstrate moving a file with Guava's Files.move(File,File).
*
* @param sourceFileName Path/name of File to be moved.
* @param targetFileName Path/name of Destination of file.
*/
public void demoMove(final String sourceFileName, final String targetFileName)
{
checkNotNull(sourceFileName, "Move source file name must NOT be null.");
checkNotNull(targetFileName, "Move destination name must NOT be null.");
final File sourceFile = new File(sourceFileName);
final File targetFile = new File(targetFileName);
try
{
Files.move(sourceFile, targetFile);
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to move file '" + sourceFileName
+ "' to '" + targetFileName + "' - " + fileIoEx.toString());
}
}
Comparing Files

Determining if two files are the same is straightforward with the Gauva Files.equal(File,File) method.

Demonstrating Files.equal(File,File)

/**
* Demonstrate using Guava's Files.equal(File,File) to compare contents of
* two files.
*
* @param fileName1 Name of first file to be compared.
* @param fileName2 Name of second file to be compared.
*/
public void demoEqual(final String fileName1, final String fileName2)
{
checkNotNull(fileName1, "First file name for comparison must NOT be null.");
checkNotNull(fileName2, "Second file name for comparison must NOT be null.");
final File file1 = new File(fileName1);
final File file2 = new File(fileName2);
try
{
out.println(
"File '" + fileName1 + "' "
+ (Files.equal(file1, file2) ? "IS" : "is NOT")
+ " the same as file '" + fileName2 + "'.");
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to compare two files '"
+ fileName1 + "' and '" + fileName2 + "' - " + fileIoEx.toString());
}
}
Touching Files

Touching a file to create a new empty file or to update the timestamp on an existing file can be easily accomplished with Guava's Files.touch(File) as shown in the next code sample.

Demonstrating Files.touch(File)

/**
* Demonstrate Guava's Files.touch(File) method.
*
* @param fileNameToBeTouched Name of file to be 'touch'-ed.
*/
public void demoTouch(final String fileNameToBeTouched)
{
checkNotNull(fileNameToBeTouched, "Unable to 'touch' a null filename.");
final File fileToTouch = new File(fileNameToBeTouched);
try
{
Files.touch(fileToTouch);
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to touch file '" + fileNameToBeTouched
+ "' - " + fileIoEx.toString());
}
}
Retrieving File Contents

With a simplicity reminiscent of Groovy's GDK extension File.getText(), Guava's Files.toString(File,Charset) makes it easy to retrieve text contents of a file.

Demonstrating Files.toString(File,Charset)

/**
* Demonstrate retrieving text contents of a specified file with Guava's
* Files.toString(File) method.
*
* @param nameOfFileToGetTextFrom Name of file from which text is to be
* retrieved.
*/
public void demoToString(final String nameOfFileToGetTextFrom)
{
checkNotNull(nameOfFileToGetTextFrom, "Unable to retrieve text from null.");
final File sourceFile = new File(nameOfFileToGetTextFrom);
try
{
final String fileContents = Files.toString(sourceFile, Charset.defaultCharset());
out.println(
"Contents of File '" + nameOfFileToGetTextFrom
+ "' are: " + fileContents);
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to get text contents of file '"
+ nameOfFileToGetTextFrom + "' - " + fileIoEx.toString());
}
}
Temporary Directory Creation

Guava makes it easy to generate a temporary directory with Files.createTempDir().

Demonstrating Files.createTempDir()

/**
* Demonstrate Guava's Files.createTempDir() method for creating a temporary
* directory.
*/
public void demoTemporaryDirectoryCreation()
{
final File newTempDir = Files.createTempDir();
try
{
out.println(
"New temporary directory is '" + newTempDir.getCanonicalPath() + "'.");
}
catch (IOException ioEx)
{
err.println("ERROR: Unable to create temporary directory - " + ioEx.toString());
}
}

I don't provide a code sample here, but it's worth noting that Guava provides a convenience method for creating a new directory will all necessary new parent directories with its Files.createParentDirs(File) method.

Retrieving Contents of File as Lines

There are times when it is most convenient to get the contents of a file as a series of lines so that each line can be processed. This is commonly done in Groovy with overloaded versions of readLines() and eachLine(). Guava provides similar functionality to Groovy's File.readLines() with its Files.readLines(File, Charset) method. This is demonstrated in the following code sample.

Demonstrating Files.readLines(File,Charset)

/**
* Demonstrate extracting lines from file.
*
* @param fileName Name of file from which lines are desired.
*/
public void demoRetrievingLinesFromFile(final String fileName)
{
final File file = new File(fileName);
try
{
final List<String> lines = Files.readLines(file, Charset.defaultCharset());
for (final String line : lines)
{
out.println(">> " + line);
}
}
catch (IOException ioEx)
{
err.println(
"ERROR trying to retrieve lines from file '"
+ fileName + "' - " + ioEx.toString());
}
}

The other overloaded version of readLines is interesting because it allows a LineProcessor callback to be specified to terminate returning of lines earlier than the end of the file.

Reading File's First Line

I have run into numerous situations in which it has been useful to read only the first line of file. This first line might tell my code what type of script is being run, provide XML prolog information, or other interesting overview data of a file's contents. Guava makes it easy to retrieve just the first line with the Files.readFirstLine(File,Charset) method. This is demonstrated in the next code listing.

Demonstrating Files.readFirstLine(File,Charset)

/**
* Demonstrate extracting first line of file.
*
* @param fileName File from which first line is to be extracted.
*/
public void demoRetrievingFirstLineFromFile(final String fileName)
{
final File file = new File(fileName);
try
{
final String line = Files.readFirstLine(file, Charset.defaultCharset());
out.println("First line of '" + fileName + "' is '" + line + "'.");
}
catch (IOException fileIoEx)
{
err.println(
"ERROR trying to retrieve first line of file '"
+ fileName + "' - " + fileIoEx.toString());
}
}
There's Much More

Although I have discussed and demonstrated several useful Files methods in this post, the class has many more to offer. Some of these include the ability to append to an existing file with Files.append(CharSequence,File,Charset), getting a file's checksum with Files.getChecksum(File,Checksum), getting a file's digest with Files.getDigest(File,MessageDigest), accessing a BufferedReader with Files.newReader(File,Charset), accessing a BufferedWriter with Files.newWriter(File,Charset), and accessing a MappedByteBuffer mapped to an underlying file via overloaded Files.map methods.

Conclusion

File handling in Java is much easier and more convenient with Guava's Files class. Guava brings file-handling convenience to Java applications that cannot make use of Groovy's or Java SE 7's file handling conveniences.

Thứ Hai, 21 tháng 11, 2011

Two Generally Useful Guava Annotations

Guava currently (Release 10) includes four annotations in its com.google.common.annotations package: Beta, VisibleForTesting, GwtCompatible, and GwtIncompatible. The last two are specific to use with Google Web Toolkit (GWT), but the former two can be useful in a more general context.

The @Beta annotation is used within Guava's own code base to indicate "that a public API (public class, method or field) is subject to incompatible changes, or even removal, in a future release." Although this annotation is used to indicate at-risk public API constructs in Guava, it can also be used in code that has access to Guava on its classpath. Developers can use this annotation to advertise their own at-risk public API constructs.

The @Beta annotation is defined as a @Documented, which means that it marks something that is part of the public API and should be considered by Javadoc and other source code documentation tools.

The @VisibleForTesting annotation "indicates that the visibility of a type or member has been relaxed to make the code testable." I have never liked having to relax type or member visibility to make something testable. It feels wrong to have to compromise one's design to allow testing to occur. This annotation is better than nothing in such a case because it at least makes it clear to others using the construct that there is a reason for its otherwise surprisingly relaxed visibility.

Conclusion

Guava provides two annotations that are not part of the standard Java distribution, but cover situations that we often run into during Java development. The @Beta annotation indicates a construct in a public API that may be changed or removed. The @VisibleForTesting annotation advertises to other developers (or reminds the code's author) when a decision was made for relaxed visibility to make testing possible or easier.

Thứ Tư, 2 tháng 11, 2011

Guava's Strings Class

In the post Checking for Null or Empty or White Space Only String in Java, I demonstrated common approaches in the Java ecosystem (standard Java, Guava, Apache Commons Lang, and Groovy) for checking whether a String is null, empty, or white space only similar to what C# supports with the String.IsNullOrWhiteSpace method. One of the approaches I showed was a Guava-based approach that made use of the Guava class Strings and its static isNullOrEmpty(String) method. In this post, I look at other useful functionality for working with Strings that is provided by Guava's six "static utility methods pertaining to String" that are bundled into the Strings class.

Using Guava's Strings class is straightforward because of its well-named methods. The following list enumerates the methods (all static) on the Strings class with a brief description of what each does next to the method name (these descriptions are borrowed or adapted from the Javadoc documentation).

isNullOrEmpty

Guava's Strings.isEmptyOrNull(String) method makes it easy to build simple and highly readable conditional statements that check a String for null or emptiness before acting upon said String. As previously mentioned, I have briefly covered this method before. Another code demonstration of this method is shown next.

Code Sample Using Strings.isNullOrEmpty(String)

/**
* Print to standard output a string message indicating whether the provided
* String is null or empty or not null or empty. This method uses Guava's
* Strings.isNullOrEmpty(String) method.
*
* @param string String to be tested for null or empty.
*/
private static void printStringStatusNullOrEmpty(final String string)
{
out.println( "String '" + string + "' "
+ (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
+ " null or empty.");
}

/**
* Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
* Strings.
*/
public static void demoIsNullOrEmpty()
{
printHeader("Strings.isNullOrEmpty(String)");
printStringStatusNullOrEmpty("Dustin");
printStringStatusNullOrEmpty(null);
printStringStatusNullOrEmpty("");
}

The output from running the above code is contained in the next screen snapshot. It shows that true is returned when either null or empty String ("") is passed to Strings.isNullOrEmpty(String).

nullToEmpty and emptyToNull

There are multiple times when one may want to treat a null String as an empty String or wants present a null when an empty String exists. In cases such as these when transformations between null and empty String are desired, The following code snippets demonstrate use of Strings.nullToEmpty(String) and Strings.emptyToNull(String).

nullToEmpty and emptyToNull

/**
* Print to standard output a simple message indicating the provided original
* String and the provided result/output String.
*
* @param originalString Original String.
* @param resultString Output or result String created by operation.
* @param operation The operation that acted upon the originalString to create
* the resultString.
*/
private static void printOriginalAndResultStrings(
final String originalString, final String resultString, final String operation)
{
out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
}

/** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
public static void demoEmptyToNull()
{
final String operation = "Strings.emptyToNull(String)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
}

/** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
public static void demoNullToEmpty()
{
final String operation = "Strings.nullToEmpty(String)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
}

The output from running the above code (shown in the next screen snapshot) proves that these methods work as we'd expect: converting null to empty String or converting empty String to null.

padStart and padEnd

Another common practice when dealing with Strings in Java (or any other language) is to pad a String to a certain length with a specified character. Guava supports this easily with its Strings.padStart(String,int,char) and Strings.padEnd(String,int,char) methods, which are demonstrated in the following code listing.

padStart and padEnd

/**
* Demonstrate Guava Strings.padStart(String,int,char) method on example
* Strings.
*/
public static void demoPadStart()
{
final String operation = "Strings.padStart(String,int,char)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
/* Do NOT call Strings.padStart(String,int,char) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.padStart(Strings.java:97)
*/
//printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);
}

/**
* Demonstrate Guava Strings.padEnd(String,int,char) method on example
* Strings.
*/
public static void demoPadEnd()
{
final String operation = "Strings.padEnd(String,int,char)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
/*
* Do NOT call Strings.padEnd(String,int,char) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.padEnd(Strings.java:129)
*/
//printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);
}

When executed, the above code pads the provided Strings with underscore characters either before or after the provided String depending on which method was called. In both cases, the length of the String was specified as ten. This output is shown in the next screen snapshot.

repeat

A final manipulation technique that Guava's Strings class supports is the ability to easily repeat a given String a specified number of times. This is demonstrated in the next code listing and the corresponding screen snapshot with that code's output. In this example, the provided String is repeated three times.

repeat

/** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
public static void demoRepeat()
{
final String operation = "Strings.repeat(String,int)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
/*
* Do NOT call Strings.repeat(String,int) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.repeat(Strings.java:153)
*/
//printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
}
Wrapping Up

The above examples are simple because Guava's Strings class is simple to use. The complete class containing the demonstration code shown earlier is now listed.

GuavaStrings.java

package dustin.examples;

import com.google.common.base.Strings;
import static java.lang.System.out;

/**
* Simple demonstration of Guava's Strings class.
*
* @author Dustin
*/
public class GuavaStrings
{
/**
* Print to standard output a string message indicating whether the provided
* String is null or empty or not null or empty. This method uses Guava's
* Strings.isNullOrEmpty(String) method.
*
* @param string String to be tested for null or empty.
*/
private static void printStringStatusNullOrEmpty(final String string)
{
out.println( "String '" + string + "' "
+ (Strings.isNullOrEmpty(string) ? "IS" : "is NOT")
+ " null or empty.");
}

/**
* Demonstrate Guava Strings.isNullOrEmpty(String) method on some example
* Strings.
*/
public static void demoIsNullOrEmpty()
{
printHeader("Strings.isNullOrEmpty(String)");
printStringStatusNullOrEmpty("Dustin");
printStringStatusNullOrEmpty(null);
printStringStatusNullOrEmpty("");
}

/**
* Print to standard output a simple message indicating the provided original
* String and the provided result/output String.
*
* @param originalString Original String.
* @param resultString Output or result String created by operation.
* @param operation The operation that acted upon the originalString to create
* the resultString.
*/
private static void printOriginalAndResultStrings(
final String originalString, final String resultString, final String operation)
{
out.println("Passing '" + originalString + "' to " + operation + " produces '" + resultString + "'");
}

/** Demonstrate Guava Strings.emptyToNull() method on example Strings. */
public static void demoEmptyToNull()
{
final String operation = "Strings.emptyToNull(String)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.emptyToNull("Dustin"), operation);
printOriginalAndResultStrings(null, Strings.emptyToNull(null), operation);
printOriginalAndResultStrings("", Strings.emptyToNull(""), operation);
}

/** Demonstrate Guava Strings.nullToEmpty() method on example Strings. */
public static void demoNullToEmpty()
{
final String operation = "Strings.nullToEmpty(String)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.nullToEmpty("Dustin"), operation);
printOriginalAndResultStrings(null, Strings.nullToEmpty(null), operation);
printOriginalAndResultStrings("", Strings.nullToEmpty(""), operation);
}

/**
* Demonstrate Guava Strings.padStart(String,int,char) method on example
* Strings.
*/
public static void demoPadStart()
{
final String operation = "Strings.padStart(String,int,char)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.padStart("Dustin", 10, '_'), operation);
/* Do NOT call Strings.padStart(String,int,char) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.padStart(Strings.java:97)
*/
//printOriginalAndResultStrings(null, Strings.padStart(null, 10, '_'), operation);
printOriginalAndResultStrings("", Strings.padStart("", 10, '_'), operation);
}

/**
* Demonstrate Guava Strings.padEnd(String,int,char) method on example
* Strings.
*/
public static void demoPadEnd()
{
final String operation = "Strings.padEnd(String,int,char)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.padEnd("Dustin", 10, '_'), operation);
/*
* Do NOT call Strings.padEnd(String,int,char) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.padEnd(Strings.java:129)
*/
//printOriginalAndResultStrings(null, Strings.padEnd(null, 10, '_'), operation);
printOriginalAndResultStrings("", Strings.padEnd("", 10, '_'), operation);
}

/** Demonstrate Guava Strings.repeat(String,int) method on example Strings. */
public static void demoRepeat()
{
final String operation = "Strings.repeat(String,int)";
printHeader(operation);
printOriginalAndResultStrings("Dustin", Strings.repeat("Dustin", 3), operation);
/*
* Do NOT call Strings.repeat(String,int) on a null String:
* Exception in thread "main" java.lang.NullPointerException
* at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
* at com.google.common.base.Strings.repeat(Strings.java:153)
*/
//printOriginalAndResultStrings(null, Strings.repeat(null, 3), operation);
printOriginalAndResultStrings("", Strings.repeat("", 3), operation);
}

/**
* Print a separation header to standard output.
*
* @param headerText Text to be placed in separation header.
*/
public static void printHeader(final String headerText)
{
out.println("\n=========================================================");
out.println("= " + headerText);
out.println("=========================================================");
}

/**
* Main function for demonstrating Guava's Strings class.
*
* @param arguments Command-line arguments: none anticipated.
*/
public static void main(final String[] arguments)
{
demoIsNullOrEmpty();
demoEmptyToNull();
demoNullToEmpty();
demoPadStart();
demoPadEnd();
demoRepeat();
}
}

The methods for padding and for repeating Strings do not take kindly to null Strings being passed to them. Indeed, passing a null to these three methods leads to NullPointerExceptions being thrown. Interestingly, these are more examples of Guava using the Preconditions class in its own code.

Conclusion

Many Java libraries and frameworks provide String manipulation functionality is classes with names like StringUtil. Guava's Strings class is one such example and the methods it supplies can make Java manipulation of Strings easier and more concise. Indeed, as I use Guava's Strings's methods, I feel almost like I'm using some of Groovy's GDK String goodness.

Thứ Hai, 31 tháng 10, 2011

Filtering and Transforming Java Collections with Guava's Collections2

One of the conveniences of Groovy is the ability to easily perform filtering and transformation operations on collections via Groovy's closure support. Guava brings filtering and transformation on collections to standard Java and that is the subject of this post.

Guava's Collections2 class features two public methods, both of which are static. The methods filter(Collection, Predicate) and transform(Collection, Function) do what their names imply: perform filtering and transformation respectively on a given collection. The collection to be filtered or transformed is the first parameter to each static method. The filtering function's second argument is an instance of Guava's Predicate class. The second argument of the transformation function is an instance of Guava's Function class. The remainder of this post demonstrates combining all of this together to filter and transform Java collections.

Filtering Collections with Guava

Filtering collections with Guava is fairly straightforward. The following code snippet demonstrates a simple example of this. A Set of Strings is provided (not shown in the code snippet, but obvious in the output that follows the code) and that provided Set is filtered for only entries beginning with a capital 'J'. This is done via use of Java regular expression support and Guava's Predicates.containsPattern(String), but there are other types of Predicates that can be specified.

Filtering Strings Beginning with 'J'

/**
* Demonstrate Guava's Collections2.filter method. Filter String beginning
* with letter 'J'.
*/
public static void demonstrateFilter()
{
printHeader("Collections2.filter(Collection,Predicate): 'J' Languages");
final Set<String> strings = buildSetStrings();
out.println("\nOriginal Strings (pre-filter):\n\t" + strings);
final Collection<String> filteredStrings =
Collections2.filter(strings, Predicates.containsPattern("^J"));
out.println("\nFiltered Strings:\n\t" + filteredStrings);
out.println("\nOriginal Strings (post-filter):\n\t" + strings);
}

The output from running the above method is shown next. This output shows the lengthy list of programming languages that make up the original Set of Strings returned by buildSetStrings() [source code shown later in the post] and shows the results of the filter call featuring only those programming languages that begin with 'J.'

Transforming Collections with Guava

Transforming collections with Guava works similarly to filtering syntactically, but a Function is used to specify how source collection entries are "transformed" to the output collection rather than using a Predicate to determine which entries to keep. The following code snippet demonstrates transforming entries in a given collection to the uppercase version of themselves.

Transforming Entries to Uppercase

/**
* Demonstrate Guava's Collections2.transform method. Transform input
* collection's entries to uppercase form.
*/
public static void demonstrateTransform()
{
printHeader("Collections2.transform(Collection,Function): Uppercase");
final Set<String> strings = buildSetStrings();
out.println("\nOriginal Strings (pre-transform):\n\t" + strings);
final Collection<String> transformedStrings =
Collections2.transform(strings, new UpperCaseFunction<String, String>());
out.println("\nTransformed Strings:\n\t" + transformedStrings);
out.println("\nOriginal Strings (post-transform):\n\t" + strings);
}

The above transformation code snippet made use of a class called UpperCaseFunction, but you won't find that class in the Guava API documentation. That is a custom class defined as shown in the next code listing.

UpperCaseFunction.java

package dustin.examples;

import com.google.common.base.Function;

/**
* Simple Guava Function that converts provided object's toString() representation
* to upper case.
*
* @author Dustin
*/
public class UpperCaseFunction<F, T> implements Function<F, T>
{
@Override
public Object apply(Object f)
{
return f.toString().toUpperCase();
}
}

The output of running the transformation code snippet that uses the UpperCaseFunction class is shown next.

The above code snippets showed methods devoted to filtering and transforming collections' entries with Guava. The entire code listing for the main class is shown next.

GuavaCollections2.java

package dustin.examples;

import static java.lang.System.out;

import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
* Class whose sole reason for existence is to demonstrate Guava's Collections2
* class.
*
* @author Dustin
*/
public class GuavaCollections2
{
/**
* Provides a Set of Strings.
*
* @return Set of strings representing some programming languages.
*/
private static Set<String> buildSetStrings()
{
final Set<String> strings = new HashSet<String>();
strings.add("Java");
strings.add("Groovy");
strings.add("Jython");
strings.add("JRuby");
strings.add("Python");
strings.add("Ruby");
strings.add("Perl");
strings.add("C");
strings.add("C++");
strings.add("C#");
strings.add("Pascal");
strings.add("Fortran");
strings.add("Cobol");
strings.add("Scala");
strings.add("Clojure");
strings.add("Basic");
strings.add("PHP");
strings.add("Flex/ActionScript");
strings.add("JOVIAL");
return strings;
}

/**
* Demonstrate Guava's Collections2.filter method. Filter String beginning
* with letter 'J'.
*/
public static void demonstrateFilter()
{
printHeader("Collections2.filter(Collection,Predicate): 'J' Languages");
final Set<String> strings = buildSetStrings();
out.println("\nOriginal Strings (pre-filter):\n\t" + strings);
final Collection<String> filteredStrings =
Collections2.filter(strings, Predicates.containsPattern("^J"));
out.println("\nFiltered Strings:\n\t" + filteredStrings);
out.println("\nOriginal Strings (post-filter):\n\t" + strings);
}

/**
* Demonstrate Guava's Collections2.transform method. Transform input
* collection's entries to uppercase form.
*/
public static void demonstrateTransform()
{
printHeader("Collections2.transform(Collection,Function): Uppercase");
final Set<String> strings = buildSetStrings();
out.println("\nOriginal Strings (pre-transform):\n\t" + strings);
final Collection<String> transformedStrings =
Collections2.transform(strings, new UpperCaseFunction<String, String>());
out.println("\nTransformed Strings:\n\t" + transformedStrings);
out.println("\nOriginal Strings (post-transform):\n\t" + strings);
}

/**
* Print a separation header including the provided text.
*
* @param headerText Text to be included in separation header.
*/
private static void printHeader(final String headerText)
{
out.println("\n==========================================================");
out.println("== " + headerText);
out.println("==========================================================");
}

/**
* Main function for demonstrating Guava's Collections2 class.
*
* @param arguments
*/
public static void main(final String[] arguments)
{
demonstrateFilter();
demonstrateTransform();
}
}

Before concluding this post, there is an important caveat to note here. Both methods defined on the Collections2 class contain warnings in their Javadoc documentation about their use. Both methods provide collections that are considered "live views" of the original collections and thus "changes to one affect the other." For example, removing an element from a source collection similarly removes it from the transformed collection. The documentation for each method also warns that neither returns a collection that is Serializable or thread-safe even when the source collection was Serializable and/or thread-safe.

Conclusion

Guava makes it easier to filter collections and transform collections' entries in Java. Although the code may not be as concise as that of Groovy for doing similar things, it beats writing straight Java code without use of Guava's Collections2 class. Java collections can be filtered with Collections2.filter(Collection,Predicate) or transformed with Collections2.transform(Collection,Function).