5

Okay so let's say I have a really simple class, e.g.

public class Baby {
   private String Name = "alf";
   public String getName() {
      return Name;
   }
}

Now what I'd like to know. Given a list of Baby's, is there any niftier/cooler/shorter way in Java to create an array/arraylist of the Baby's names' rather than simple looping through all the babies and adding their names to the new list? The equivalent of this:

ArrayList<Baby> babies = new ArrayList<Baby>();
/* some code adding a bunch of babies to the arraylist */

ArrayList<String> names = new ArrayList<String>();
for (Baby b : babies) {
  names.add(b.getName());
}

...but cooler. Know what I mean?

5
  • 2
    Cooler? Possibly. More readable and understandable: hardly. Until we finally have lambdas (and method handles) we won't be able to write that much more concise than that. Commented Nov 14, 2012 at 6:32
  • @JoachimSauer When in java will be introduced lambdas as part of the official documentation? Commented Nov 14, 2012 at 6:34
  • @ZagorulkinDmitry java 8 should include it. But then again, it was originally intended for java 7 :| Commented Nov 14, 2012 at 6:35
  • Readable and understandable isn't what I'm going for here. I've been writing code like that for years...I just want to learn some cool tricks. Commented Nov 14, 2012 at 6:40
  • @user891876: slightly offtopic, but, switch to scala. Things like this are naturally elegant there. Commented Nov 14, 2012 at 6:47

5 Answers 5

4

You could use Guava's Lists.transform:

Function<Baby, String> getBabyNameFunction = new Function<Baby, String>() {
    @Override
    public String apply(Baby baby) {
        return baby.getName();
    }
};

List<String> babyNames = Lists.transform(babies, getBabyNameFunction);

The key difference here being that babyNames is a view of the original list on which the transformations are lazily performed. From the documentation:

The function is applied lazily, invoked when needed. This is necessary for the returned list to be a view, but it means that the function will be applied many times for bulk operations like List.contains(java.lang.Object) and List.hashCode(). For this to perform well, function should be fast. To avoid lazy evaluation when the returned list doesn't need to be a view, copy the returned list into a new list of your choosing.

Obviously the syntax for implementing the Function is rather verbose - that's Java for you until lambdas. I typically keep commonly used functions as constants to avoid clutter and re-instantiation at the call site:

public class Baby {

    ...

    public static class Functions {

        private static final Function<Baby, String> GET_NAME =
                new Function<Baby, String>() {
                    @Override
                    public String apply(Baby baby) {
                        return baby.getName();
                    }
                };

        private Functions() { }

        public static Function<Baby, String> getName() {
            return GET_NAME;
        }
    }
}

Yes, it's even more code but it's hidden away and more maintainable. Then at the call site:

List<String> babyNames = Lists.transform(babies, Baby.Functions.getName());
Sign up to request clarification or add additional context in comments.

2 Comments

Lol yeah that's quite a bit more verbose than I was originally looking for, but damn cool nonetheless! I've never heard of these Guava lists before, and that's exactlythe kind of nifty code I was looking for. Thanks!
And if you think it's cool now, once we do have Lambdas & Co it will be as short as List<String> babyNames = Lists.transform(babies, Baby#getName); (the syntax might be slightly different, but something like this).
1

With GS Collections you have a couple ways to do this. You can use the drop-in replacement for ArrayList called FastList.

MutableList<Baby> babies = FastList.newList();
/* some code adding a bunch of babies to the fastlist */

MutableList<String> names = babies.collect(new Function<Baby, String>() {
    public String valueOf(Baby baby) {
        return baby.getName();
    }
});

Or you can use the ArrayList with the ListIterate utility class.

ArrayList<Baby> babies = new ArrayList<Baby>();
/* some code adding a bunch of babies to the arraylist */

List<String> names = ListIterate.collect(babies, new Function<Baby, String>() {
    public String valueOf(Baby baby) {
        return baby.getName();
    }
});

If you don't care about result order, but want to allow duplicates you could use a Bag instead.

MutableList<Baby> babies = FastList.newList();
/* some code adding a bunch of babies to the fastlist */

MutableBag<String> names = babies.collect(new Function<Baby, String>() {
    public String valueOf(Baby baby) {
        return baby.getName();
    }
}, HashBag.<String>newBag());

int numberOfBabiesNamedXYZ = names.occurrencesOf("XYZ");

If you are using this function over and over, you might want to put it as a constant on the Baby class.

Note: I am a developer on GS Collections.

Comments

0

Well... simply put, no.

That's assuming you're looking for something like

ArrayList<String> names = babies.getName();

There's nothing like that in Java.

However, you could make a new class to do it for you:

public class BabyNameifier {

    public BabyNameifier(){}

    public ArrayList<String> Nameify(ArrayList<Baby> babies) {
        ArrayList<String> names = new ArrayList<String>();
        for (Baby b : babies) {
            names.add(b.getName());
        }
        return names;
    }
}

That way, you can later say:

ArrayList<Baby> babies = new ArrayList<Baby>();
/* some code adding a bunch of babies to the arraylist */

BabyNameifier babyNameifier = new BabyNameifier();
ArrayList<String> names = babyNameifier.Nameify(babies);

Which would create just about the coolest way to systematically retrieve Baby names that the world has ever known.

1 Comment

Ahah! Actually, I was picturing something similar to your first statement...babies.getNames()'s; :P Ahh I can dream. edit: still getting used to this "enter means send!" feature. I do like your idea as an alternative for anything super nifty though - the later code does look pretty cool!
0

You want cooler way to do it? If you know Spring framework, you are in luck!

In applicationContext.xml, write something like this:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="babyList" class="com.babies.BabyList">

    <property name="babyLists">
        <list>
            <bean class="com.babies.Baby">
                <property name="name" value="alice" />
            </bean>
            <bean class="com.babies.Baby">
                <property name="name" value="bob" />
            </bean>
            <bean class="com.babies.Baby">
                <property name="name" value="trudy" />
            </bean>
        </list>
    </property>
</bean>

 </beans>

Here is the code in normal Java

package com.babies;

 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;

  public class Main  {
      public static void main( String[] args ) {
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

          BabyList babyList = (BabyList)context.getBean("babyList");
          System.out.println(babyList);

    }
  }

Now you can edit the list in the applicationContext.xml whenever you want to add/delete any name from the list. No need to hard code everything.

You can refer more about Spring framework collection from this link: http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/

Or if you simply want to learn more about Spring, here is a good starting point: http://www.mkyong.com/tutorials/spring-tutorials/

Comments

0

The cooler way after the release of Java 8 back in March 2014 introducing Stream API with .map and .collect operations is like this:

ArrayList<Baby> babies = new ArrayList<Baby>();
/* some code adding a bunch of babies to the arraylist */

List<String> names = babies
    .stream() // Stream<Baby>
    .map(Baby::getName) // Stream<String>; use reference to the getter method
    .collect(Collectors.toList()); // build resulting list of names

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.