0

I was writing some code that I sometimes need, not often, and was wondering why it required so many lines of code. I needed to create a new collection with the strings returned from a method in the objects of another collection. I might do this three times on three different methods of that class. I thought there might be something in guava to help me. Something like:

collection = Iterators.collectNotNull( myCollection, new Function...{
    public String apply( MyObject input ) {
        String value = input.getStringValue();
        if ( StringUtils.isEmpty( value )
            return null;
        return value; } );

I mean that is even too many lines of code for me. But either way I basically wrote the above for times when I need it.

So the question is, can anyone do this more simply with less code? You can use existing mainstream libraries like Apache commons or Guava. Reflection is okay if you want to remove the need to create the anonymous inner to get the method that will return the value. The above is my best attempt but I had to write the reusable "collectNotNull" method. I'd prefer not to have to.

Mattias Isegran Bergander
  • 11,811
  • 2
  • 41
  • 49
robert_difalco
  • 4,821
  • 4
  • 36
  • 58
  • I do not understanding what you are trying to accomplish. Are you needing to convert empty strings to null? – Aaron Kurtzhals Dec 21 '12 at 17:55
  • Why do you care how many lines of code it is? If it's something you do repeatedly, create a method you can re-use. – jahroy Dec 21 '12 at 18:12
  • No reason. I just notice I do variations on this a lot and that in Java it is more cumbersome than it has been in other languages. Was wondering if there was a simpler idiom as in Ruby, Python, or Smalltalk but Java specific. – robert_difalco Dec 21 '12 at 18:19
  • Of course it is in a method Jahroy. – robert_difalco Dec 21 '12 at 20:23

3 Answers3

3

If your objective is less code, then the obvious approach is the best.

 List<String> list = Lists.newArrayList();
 for (MyObject input : myCollection) {
   String value = input.getStringValue();
   if (!value.isEmpty()) {
     list.add(value);
   }
 }

See the Guava wiki page on functional idioms: they're usually overkill.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • This is essentially what I ended up with but I used an anonymous inner to get the string value. I only did this because I had to call it three times to generate IN CLAUSES I was OR'ing together for three methods of an object. Basically like one IN clause for #getName, another for #getAlternativeName, etc. where #getName is #getStringValue in this example. – robert_difalco Dec 21 '12 at 18:06
  • Another issue with using the above but copied for #getName1, #getName2, and #getName3 is that if I now need to check for #getStringValue == null as well as for "" then I have to remember to update it in three methods. That's why I just put the three iterations in a function that takes an anonymous inner for the value extraction. – robert_difalco Dec 21 '12 at 18:22
  • If you're really that concerned _and_ nulls are a possibility, then just factor out a method or use e.g. [`Strings.emptyToNull`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Strings.html#emptyToNull(java.lang.String)). But more realistically, you shouldn't ever conflate null strings and empty strings: just use empty strings from the beginning. – Louis Wasserman Dec 21 '12 at 18:26
  • Louis, that is just an example and besides, you don't always own all code to make that constraint on the input data. The point is not having to change the for each iteration in three different places. – robert_difalco Dec 21 '12 at 18:32
  • 1
    I still don't follow why an anonymous class is preferable to just factoring out a normal, everyday method. – Louis Wasserman Dec 21 '12 at 18:47
  • 1
    I love that Guava wiki page! ... "When you go to preposterous lengths to make your code "a one-liner," the Guava team weeps." – Jeff Olson Dec 21 '12 at 18:48
  • Thanks! (I wrote the Guava wiki, and I'm glad you are amused.) – Louis Wasserman Dec 21 '12 at 20:01
  • Louis, I'm not sure how else to explain it. I am extracting the non-empty result of methods called on objects in another collection. There is also other processing going on. I totally concur with the Guava wiki page you posted. But I have to do this several times in code and it will be exactly the same each time except for the method that will be called to get the object for the new collection. I can't imagine you think I should write the same iterator and processing code and duplicate it except for varying the method called on the items I am iterating over each time. – robert_difalco Dec 21 '12 at 20:32
  • Ah. Now I understand. You need to vary what's being called inside the method and not any of the other processing. That sounds fine; carry on. – Louis Wasserman Dec 21 '12 at 21:10
0

This:

 myCollection.removeAll(Arrays.asList(null, ""));

Example:

List<String> myCollection = new LinkedList<String>(Arrays.asList("a", "", "b", null, "c"));
System.out.println(myCollection);
myCollection.removeAll(Arrays.asList(null, ""));
System.out.println(myCollection);

Produces:

[a, , b, null, c]
[a, b, c]

Update: That is not very general though, you can retain or remove all elements equal to something that way.

In Java 8 (available for preview) you can do this:

myCollection.filter(s -> s != null && !s.isEmpty())

And do something with that result (add into() to add those to a list or map() further etc).

Mattias Isegran Bergander
  • 11,811
  • 2
  • 41
  • 49
  • Right, there are many better ways to do this in Java 8. Btw, the issue here is getting a new collection from the non-empty call on a method from each object in the input collection. – robert_difalco Dec 21 '12 at 20:25
  • If it's only a new collection that is the remaining problem that's quite easy though. `collection = new ArrayList(myCollection).removeAll(...)` – Mattias Isegran Bergander Dec 21 '12 at 20:30
0

If Java had higher-order function…

Well, let’s not get down that road.

One way of getting closer to this in Java is using the foreach library

for (Collect<Type> each: ForEach.collect(input)) 
    each.yield = each.value.method(); 
for (Reject<String> each: ForEach.reject(ForEach.result())) 
    each.yield = each.value.isEmpty(); 
result = ForEach.result()
akuhn
  • 27,477
  • 2
  • 76
  • 91