Searching on an object with different keys in Java - java

I'm a developer transitioning from C++ to Java.So I still dont have all the expertise to getting stuff done the Java Way.
I have the following class
class Site
{
String siteName;
Integer siteId;
Integer views;
Integer searches;
}
I maintain 2 maps to search over the objects of this class(using sitename or siteid)
HashMap<String, Site> siteNameToSiteMap;
HashMap<Integer, Site> siteIdToSiteMap;
However going forward, I have to add a one more field called parentBrand to the class Site. This will force me to create another Map to be able to search over it.
HashMap<String, Site> parentBrandToSiteMap;
Such "indexing" variables might increase going ahead and thus also increase the number of maps I maintain.
I remember using Boost Multi-indexed container while solving a similar issue while developing in C++. Is there an equivalent well supported, well documented library in Java that I can use. If no, is there a way I can refactor my code to solve my problem.

I'm surprised that there isn't a version of something like the boost multi-index containers available. (Maybe there is somewhere...) But its not too hard to hook up your own version in Java.
A rough, but working, version might look like this:
The basic site object
I've used a slightly different Site object, just to keep things simple (and because I didn't have access to this post on the bus...)
public class Site {
Integer id;
String name;
String rating;
// .. Constructor and toString removed for brevity
}
A wrapped version
I'm going to introduce some workhorse classes later, but they're a little ugly. This is just to show what the final interface would look like once you've wrapped it up a little:
class SiteRepository {
private final MultiMap<Site> sites = new MultiMap<>();
public final AbstractMap<String, Site> byName = sites.addIndex((site)->site.name);
public final AbstractMap<Integer,Site> byId = sites.addIndex((site)->site.id);
public final AbstractMap<String,List<Site>> byRating = sites.addMultiIndex((Site site)->site.rating);
public void add(Site s) { sites.add(s); }
}
SiteRepository repo = new SiteRepository();
repo.add(...);
Site site = repo.byId.get(1234);
repo.byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
The MultiMap core
Probably should be called MultiIndex since MultiMap means something else...
public static class MultiMap<V> {
public static class MultiMapIndex<K,V> extends AbstractMap<K,V> {
#Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
HashMap<K,V> map = new HashMap<>();
}
public <K> MultiMapIndex<K,V> addIndex(Function<V, K> f) {
MultiMapIndex<K,V> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> result.map.put(f.apply(v), v);
mappers.add(e);
values.forEach(e);
return result;
}
public <K> MultiMapIndex<K,List<V>> addMultiIndex(Function<V, K> f) {
MultiMapIndex<K,List<V>> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> {
K key = f.apply(v);
List<V> list = result.map.get(key);
if (list == null) {
list = new ArrayList<>();
result.map.put(key, list);
}
list.add(v);
};
mappers.add(e);
values.forEach(e);
return result;
}
public void add(V v) {
values.add(v);
mappers.forEach( e -> e.accept(v));
}
private List<Consumer<V>> mappers = new ArrayList<>();
private List<V> values = new ArrayList<>();
}
More low level examples
public static void main(String[] args) {
// Create a multi-map
MultiMap<Site> multiMap = new MultiMap<>();
// Add an index by Site.id
MultiMapIndex<Integer, Site> byId = multiMap.addIndex((site)->site.id);
// Add some entries to the map
multiMap.add(new Site(1234,"A Site","A"));
multiMap.add(new Site(4321,"Another Site","B"));
multiMap.add(new Site(7777,"My Site","A"));
// We can add a new index after the entries have been
// added - this time by name.
MultiMapIndex<String, Site> byName = multiMap.addIndex((site)->site.name);
// Get a value by Id.
System.err.printf("Get by id=7777 = %s\n", byId.get(7777));
// Get a value by Name
System.err.printf("Get by name='A Site' = %s\n", byName.get("A Site"));
// We can do usual mappy things with the indexes,
// such as list the keys, or iterate over all entries
System.err.printf("byId.keys() = %s\n", byId.keySet());
byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
// In some cases the map is not unique, so I provide a
// way to get all entries with the same value as a list.
// in this case by their rating value.
MultiMapIndex<String, List<Site>> byRating = multiMap.addMultiIndex((Site site)->site.rating);
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
// Adding stuff after creating the indices is fine.
multiMap.add(new Site(3333,"Last Site","B"));
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
}
}

I think you can search your objects over List :
List<Site> sites;
for (Site s : sites) {
if (s.getSiteName().equal(siteName)) {
// do something
}
if (s.getSiteId().equal(siteId)) {
// do something
}
}

You should create a Bean (Container) as Java is not requiring code optimisation as it will be optimised by the JIT compiler anyway.
public class SiteMap {
String siteName;
Integer siteId;
String parentBrand;
.... Getters and setters ...
}
List<SiteMap> myList = new ArrayList<>();
If you need to compare or sort then you can implement Comparable interface on the SiteMap class allowing you to sort the details if needed.
you can, if using Java 8 then also use Streams to filter or fetch the one you want. as there is a fetchFirst
SiteMap mysite = myList.stream()
.filter(e -> e.siteName.equals("Amazon.com"))
.findFirst()
.get();

Related

Java Methods Container

I'm trying to implement an action/reaction system in Java.
For that, I need to have all my methods stock in a container so I can easily call the response I need with the return of the action I want.
Being a C ++ developer and new to Java my first intuition was to create an array of function pointers (or at least reproduce it) so I tried to used anonymous subclasses. But didn't get the result I was looking for.
So I tried with lambdas, here is a sample of what I'm trying to do.
public class Test {
public Map<Integer, Vector<String>> actions = new HashMap<>();
public Map<Integer, Integer> responses = new HashMap<>();
public Test() {
Vector<String> v= new Vector<String>();
actions.put(0, action0());
actions.put(1, action1());
responses.put(0, response0(Vector<String>)); // How can I leave aside this argument which I don't know at this point ?
responses.put(1, response1(Vector<String>));
}
public Vector<String> action0() {...}
public Vector<String> action1() {...}
// This methods takes actions return as argument
public Integer response0 (Vector<String>) {...}
public Integer response1 (Vector<String>) {...}
public void run() {
// When I run, I want to be able to launch any of my responses with any of my actions return
responses.get(0)
}
}
Am I at least trying a good way to solve this problem ?
Thanks a lot
Your code has a few flaws, so I'll assume that getHashtag() and action1() were supposed to be the same, and similar for the other three.
Also, I'll assume that the second responses.put() should have been key 1, not 0. Also, the parameter to your responseX() methods need a name.
Anyway, you need a functional interface, so you can give the responseX() methods as Method References.
In your case, the responseX() methods take a Vector<String> as parameter, and returns an Integer, so the functional interface would be Function<Vector<String>, Integer>.
You can then build a map of those methods, to be executed later.
public Test() {
// Here we can build map of response methods first, if we like, even though Vectors don't exist yet
Map<Integer, Function<Vector<String>, Integer>> responseMethods = new HashMap<>();
responseMethods.put(0, this::response0);
responseMethods.put(1, this::response1);
// Now we build the action map of Vectors
Map<Integer, Vector<String>> actions = new HashMap<>();
actions.put(0, action0());
actions.put(1, action1());
// At this time, we can now execute the referenced methods to get the actual responses
Map<Integer, Integer> responses = new HashMap<>();
for (Integer key : actions.keySet()) {
Vector<String> v = actions.get(key);
Function<Vector<String>, Integer> responseMethod = responseMethods.get(key);
Integer response = responseMethod.apply(v);
responses.put(key, response);
}
}
public Vector<String> action0() {...}
public Vector<String> action1() {...}
public Integer response0(Vector<String> v) {...}
public Integer response1(Vector<String> v) {...}
You can even defer the execution of the action methods if you want:
public Test() {
// Here we can build map of response methods first, if we like, even though Vectors don't exist yet
Map<Integer, Function<Vector<String>, Integer>> responseMethods = new HashMap<>();
responseMethods.put(0, this::response0);
responseMethods.put(1, this::response1);
// Now we build the action map of Vectors
Map<Integer, Supplier<Vector<String>>> actionMethods = new HashMap<>();
actionMethods.put(0, this::action0);
actionMethods.put(1, this::action1);
// At this time, we can now execute the referenced methods to get the actual responses
Map<Integer, Integer> responses = new HashMap<>();
for (Integer key : actionMethods.keySet()) {
Supplier<Vector<String>> actionMethod = actionMethods.get(key);
Function<Vector<String>, Integer> responseMethod = responseMethods.get(key);
Vector<String> v = actionMethod.get();
Integer response = responseMethod.apply(v);
responses.put(key, response);
}
}

Is it possible to group elements without closing the stream?

Is it possible to group elements in a Stream, but then continue streaming instead of having to create a new stream from the EntrySet of the returned map?
For example, I can do this:
public static void main(String[] args) {
// map of access date to list of users
// Person is a POJO with first name, last name, etc.
Map<Date, List<Person>> dateMap = new HashMap<>();
// ...
// output, sorted by access date, then person last name
dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {
Date date = e.getKey();
// group persons by last name and sort
// this part seems clunky
e.getValue().stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet()))
.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e2 -> {
// pool agent id is the key
String lastName = e2.getKey();
Set<Person> personSet = e2.getValue();
float avgAge = calculateAverageAge(personSet);
int numPersons = personSet.size();
// write out row with date, lastName, avgAge, numPersons
});
});
}
Which works just fine, but seems a little clunky, especially the streaming into a map, and then immediately streaming on the entry set of that map.
Is there a way to group objects in a stream, but continue streaming?
You can shorten your code by using Map.forEach, downstream collectors, TreeMap, and IntSummaryStatistics.
By grouping into a TreeMap (instead of leaving it up to the groupingBy collector), you get the names sorted automatically. Instead of immediately getting the grouped map, you add a summarizingInt collector that turns the list of persons with the same name into IntSummaryStatistics of their ages.
public static void main(String[] args) {
Map<Date, List<Person>> dateMap = new HashMap<>();
dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {
Date date = e.getKey();
e.getValue().stream()
.collect(Collectors.groupingBy(Person::getLastName,
TreeMap::new,
Collectors.summarizingInt(Person::getAge)))
.forEach((name, stats) -> System.out.println(date +" "+
lastName +" "+
stats.getAverage() +" "+
stats.getCount()));
});
}
If you have control over the type of the initial map, you could use TreeMap there as well, and shorten it further:
public static void main(String[] args) {
Map<Date, List<Person>> dateMap = new TreeMap<>();
dateMap.forEach((date, persons -> { ...
There are several different ways to interpret the question, but if we restate the question as, "Is it possible to group elements within a Stream without using a terminal operation and apply stream operations to the resulting groups within the same stream pipeline," then the answer is "Yes." In this restatement of the question, terminal operation is defined in the way that the Java 8 streams API defines it.
Here is an example that demonstrates this.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
class StreamGrouper {
public static class GroupableObj<K extends Comparable<? super K>, T>
implements Comparable<GroupableObj<K, T>> {
private K key;
private T obj;
private Set<T> setOfObj;
public GroupableObj(K key, T obj) {
if (key == null) {
throw new NullPointerException("Key may not be null");
}
this.key = key;
this.obj = obj;
}
#Override
public int compareTo(GroupableObj<K, T> otherGroupable) {
return key.compareTo(otherGroupable.key);
}
#Override
public boolean equals(Object otherObj) {
if (otherObj == null) {
return false;
}
if (otherObj instanceof GroupableObj) {
GroupableObj<?, ?> otherGroupable =
(GroupableObj<?, ?>)otherObj;
return setOfObj == otherGroupable.setOfObj &&
key.equals(otherGroupable.key);
}
return false;
}
public Set<T> getGroup() {
return setOfObj;
}
public K getKey() {
return key;
}
public T getObject() {
return obj;
}
#Override
public int hashCode() {
return key.hashCode();
}
public void setGroup(Set<T> setOfObj) {
this.setOfObj = setOfObj;
}
}
public static class PeekGrouper<K extends Comparable<? super K>, T>
implements Consumer<GroupableObj<K, T>> {
private Map<K, Set<T>> groupMap;
public PeekGrouper() {
groupMap = new HashMap<>();
}
#Override
public void accept(GroupableObj<K, T> groupable) {
K key = groupable.getKey();
Set<T> group = groupMap.computeIfAbsent(key,
(k) -> new HashSet<T>());
groupable.setGroup(group);
group.add(groupable.getObject());
}
}
public static void main(String[] args) {
Function<Double, Long> myKeyExtractor =
(dblObj) -> Long.valueOf(
(long)(Math.floor(dblObj.doubleValue()*10.0)));
PeekGrouper<Long, Double> myGrouper = new PeekGrouper<>();
Random simpleRand = new Random(20190527L);
simpleRand.doubles(100).boxed().map((dblObj) ->
new GroupableObj<Long, Double>(
myKeyExtractor.apply(dblObj), dblObj)).peek(myGrouper).
distinct().sorted().
map(GroupableObj<Long, Double>::getGroup).
forEachOrdered((grp) -> System.out.println(grp));
}
}
In order to make a program that can be compiled and executed on its own, this example moves away from using the Person objects that are referenced in the question, but the grouping concept is the same, and the code from the question could turn into something like the following.
PeekGrouper<String, Person> myGrouper = new PeekGrouper<>();
e.getValue().stream().map((p) -> new GroupableObj<String, Person>(
p.getLastName(), p)).peek(myGrouper).distinct().sorted().
forEachOrdered(e2 -> {
String lastName = e2.getKey();
Set<Person> personSet = e2.getGroup();
float avgAge = calculateAverageAge(personSet);
int numPersons = personSet.size();
// write out row with date, lastName, avgAge, numPersons
});
Please note that in order for this example to work, it is required that the stream call both the distinct function (which reduces the stream to only a single instance of each group) and the sorted function (which ensures that the entire stream has been processed and the groups have been fully "collected" before processing continues). Also note that as implemented here GroupableObj is not safe to use with parallel streams. If the terminal operation of the stream does not require that the groups be fully "collected" when it processes the objects -- for example, if the terminal operation were something like Collectors.toList() -- then a call to sorted would not be required. The critical point is that any portion of the stream that sees the groups prior to a call to sorted and prior to the end of a terminal operation (including processing during a terminal operation) may see a group that is incomplete.
For the specific example in the question, it may be somewhat less time-efficient to sort the objects before grouping them if many of them are in the same group, but if you are willing to sort the objects before grouping them, you can achieve the same functionality without performing any streaming after doing the grouping. The following is a rewrite of the first example from this answer that demonstrates this.
import java.util.Comparator;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collector;
class PreSortOrderedGrouper {
public static void main(String[] args) {
Function<Double, Long> myKeyExtractor =
(dblObj) -> Long.valueOf(
(long)(Math.floor(dblObj.doubleValue()*10.0)));
Random simpleRand = new Random(20190527L);
Consumer<Set<Double>> groupProcessor =
(grp) -> System.out.println(grp);
simpleRand.doubles(100).boxed().sorted(
Comparator.comparing(myKeyExtractor)).
collect(Collector.of(HashSet<Double>::new,
(set, dblObj) -> {
if (set.isEmpty() || myKeyExtractor.apply(set.iterator().
next()) == myKeyExtractor.apply(dblObj)) {
set.add(dblObj);
} else {
groupProcessor.accept(set);
set.clear();
set.add(dblObj);
}
},
(setOne, setTwo) -> {
throw new UnsupportedOperationException();
},
(finalSet) -> {
groupProcessor.accept(finalSet);
return Integer.valueOf(0);
}));
}
}
I can't be sure that either of these examples will feel less "clunky" to you, but if the example in your question is a pattern you use frequently, you could probably adapt one or both of these examples in ways that will suit your purposes and, aside from a few utility classes, result in no more code than you are currently using.

My method is too specific. How can I make it more generic?

I have a class, the outline of which is basically listed below.
import org.apache.commons.math.stat.Frequency;
public class WebUsageLog {
private Collection<LogLine> logLines;
private Collection<Date> dates;
WebUsageLog() {
this.logLines = new ArrayList<LogLine>();
this.dates = new ArrayList<Date>();
}
SortedMap<Double, String> getFrequencyOfVisitedSites() {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> domains = new HashSet<String>();
Frequency freq = new Frequency();
for (LogLine line : this.logLines) {
freq.addValue(line.getVisitedDomain());
domains.add(line.getVisitedDomain());
}
for (String domain : domains) {
frequencyMap.put(freq.getPct(domain), domain);
}
return frequencyMap;
}
}
The intention of this application is to allow our Human Resources folks to be able to view Web Usage Logs we send to them. However, I'm sure that over time, I'd like to be able to offer the option to view not only the frequency of visited sites, but also other members of LogLine (things like the frequency of assigned categories, accessed types [text/html, img/jpeg, etc...] filter verdicts, and so on). Ideally, I'd like to avoid writing individual methods for compilation of data for each of those types, and they could each end up looking nearly identical to the getFrequencyOfVisitedSites() method.
So, my question is twofold: first, can you see anywhere where this method should be improved, from a mechanical standpoint? And secondly, how would you make this method more generic, so that it might be able to handle an arbitrary set of data?
This is basically the same thing as Eugene's solution, I just left all the frequency calculation stuff in the original method and use the strategy only for getting the field to work on.
If you don't like enums you could certainly do this with an interface instead.
public class WebUsageLog {
private Collection<LogLine> logLines;
private Collection<Date> dates;
WebUsageLog() {
this.logLines = new ArrayList<LogLine>();
this.dates = new ArrayList<Date>();
}
SortedMap<Double, String> getFrequency(LineProperty property) {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> values = new HashSet<String>();
Frequency freq = new Frequency();
for (LogLine line : this.logLines) {
freq.addValue(property.getValue(line));
values.add(property.getValue(line));
}
for (String value : values) {
frequencyMap.put(freq.getPct(value), value);
}
return frequencyMap;
}
public enum LineProperty {
VISITED_DOMAIN {
#Override
public String getValue(LogLine line) {
return line.getVisitedDomain();
}
},
CATEGORY {
#Override
public String getValue(LogLine line) {
return line.getCategory();
}
},
VERDICT {
#Override
public String getValue(LogLine line) {
return line.getVerdict();
}
};
public abstract String getValue(LogLine line);
}
}
Then given an instance of WebUsageLog you could call it like this:
WebUsageLog usageLog = ...
SortedMap<Double, String> visitedSiteFrequency = usageLog.getFrequency(VISITED_DOMAIN);
SortedMap<Double, String> categoryFrequency = usageLog.getFrequency(CATEGORY);
I'd introduce an abstraction like "data processor" for each computation type, so you can just call individual processors for each line:
...
void process(Collection<Processor> processors) {
for (LogLine line : this.logLines) {
for (Processor processor : processors) {
processor.process();
}
}
for (Processor processor : processors) {
processor.complete();
}
}
...
public interface Processor {
public void process(LogLine line);
public void complete();
}
public class FrequencyProcessor implements Processor {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> domains = new HashSet<String>();
Frequency freq = new Frequency();
public void process(LogLine line)
String property = getProperty(line);
freq.addValue(property);
domains.add(property);
}
protected String getProperty(LogLine line) {
return line.getVisitedDomain();
}
public void complete()
for (String domain : domains) {
frequencyMap.put(freq.getPct(domain), domain);
}
}
}
You could also change a LogLine API to be more like a Map, i.e. instead of strongly typed line.getVisitedDomain() could use line.get("VisitedDomain"), then you can write a generic FrequencyProcessor for all properties and just pass a property name in its constructor.

Java - map a list of objects to a list with values of their property attributes

I have the ViewValue class defined as follows:
class ViewValue {
private Long id;
private Integer value;
private String description;
private View view;
private Double defaultFeeRate;
// getters and setters for all properties
}
Somewhere in my code i need to convert a list of ViewValue instances to a list containing values of id fields from corresponding ViewValue.
I do it using foreach loop:
List<Long> toIdsList(List<ViewValue> viewValues) {
List<Long> ids = new ArrayList<Long>();
for (ViewValue viewValue : viewValues) {
ids.add(viewValue.getId());
}
return ids;
}
Is there a better approach to this problem?
We can do it in a single line of code using java 8
List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList());
For more info : Java 8 - Streams
You could do it in a one-liner using Commons BeanUtils and Collections:
(why write your own code when others have done it for you?)
import org.apache.commons.beanutils.BeanToPropertyValueTransformer;
import org.apache.commons.collections.CollectionUtils;
...
List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues,
new BeanToPropertyValueTransformer("id"));
Use google collections. Example:
Function<ViewValue, Long> transform = new Function<ViewValue, Long>() {
#Override
public Long apply(ViewValue from) {
return from.getId();
}
};
List<ViewValue> list = Lists.newArrayList();
List<Long> idsList = Lists.transform(list, transform);
UPDATE:
On Java 8 you don't need Guava. You can:
import com.example.ViewValue;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
Function<ViewValue, Long> transform = ViewValue::getId;
List<ViewValue> source = new ArrayList<>();
List<Long> result = source.stream().map(transform).collect(Collectors.toList());
Or just:
List<ViewValue> source= new ArrayList<>();
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList());
NEXT UPDATE (The last one after Javaslang to Vavr name change):
Currently it's worth to mention about the solution with Javaslang library(http://www.javaslang.io/) Vavr library (http://www.vavr.io/). Let's assume that we have our list with genuine objects:
List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2));
We could make transformation with List class from Javaslang library (on the long run the collect is not convenient):
List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList();
But you will see the power with only the Javaslang lists:
io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3));
io.vavr.collection.List<Long> res = source.map(ViewValue::getId);
I encourage to take a look available collections and new types on that library (I like especially the Try type). You will find the documentation under the following address: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.
PS. Due to the Oracle and the "Java" word within the name they had to change the library name from javaslang to something else. They had decided to Vavr.
EDIT: This answer is based on the idea that you'll need to do similar things for different entities and different properties elsewhere in your code. If you only need to convert the list of ViewValues to a list of Longs by ID, then stick with your original code. If you want a more reusable solution, however, read on...
I would declare an interface for the projection, e.g.
public interface Function<Arg,Result>
{
public Result apply(Arg arg);
}
Then you can write a single generic conversion method:
public <Source, Result> List<Result> convertAll(List<Source> source,
Function<Source, Result> projection)
{
ArrayList<Result> results = new ArrayList<Result>();
for (Source element : source)
{
results.add(projection.apply(element));
}
return results;
}
Then you can define simple projections like this:
private static final Function<ViewValue, Long> ID_PROJECTION =
new Function<ViewValue, Long>()
{
public Long apply(ViewValue x)
{
return x.getId();
}
};
And apply it just like this:
List<Long> ids = convertAll(values, ID_PROJECTION);
(Obviously using K&R bracing and longer lines makes the projection declaration a bit shorter :)
Frankly all of this would be a lot nicer with lambda expressions, but never mind...
I've implemented a small functional library for this usecase. One of the methods has this signature:
<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)
Which takes the string and uses reflection to create a call to the property then it returns a List backed by the objectList where get and iterator implemented using this property call.
The mapToProperty functions is implemented in terms of a general map function that takes a Function as a mapper though, just as another post described. Very usefull.
I suggest you read up on basic functionl programming and in particular take a look at Functors (objects implementing a map function)
Edit: Reflection really doesn't have to be expensive. The JVM has improved a lot in this area. Just make sure to compile the invocation once and reuse it.
Edit2: Sample code
public class MapExample {
public static interface Function<A,R>
{
public R apply(A b);
}
public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType)
{
try {
final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1));
if(!propertyType.isAssignableFrom(m.getReturnType()))
throw new IllegalArgumentException(
"Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName()
);
return new Function<A,R>()
{
#SuppressWarnings("unchecked")
public R apply(A b)
{
try {
return (R)m.invoke(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
};
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper)
{
return new AbstractList<T2>()
{
#Override
public T2 get(int index) {
return mapper.apply(list.get(index));
}
#Override
public int size() {
return list.size();
}
};
}
#SuppressWarnings("unchecked")
public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType)
{
if(list == null)
return null;
else if(list.isEmpty())
return Collections.emptyList();
return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType));
}
}
You could use a wrapper:
public class IdList impements List<Long>
{
private List<ViewValue> underlying;
pubic IdList(List<ViewValue> underying)
{
this.underlying = underying;
}
public Long get(int index)
{
return underlying.get(index).getId()
}
// other List methods
}
Though that's even more tedious work, it could improve performance.
You could also implement your and my solution generic-ly using reflection, but that would be very bad for performance.
There's no short and easy generic solution in Java, I'm afraid. In Groovy, you would simply use collect(), but I believe that involves reflection as well.
That depends on what you then do with the List<Long>, and the List<ViewValue>
For example you might get sufficient functionality from creating your own List implementation that wraps a List<ViewValue>, implementing iterator() with an iterator implementation that iterates over the ViewValues, returning the id.
You can populate a map from the properties of a list of objects (say id as key and some property as value) as below
Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));

How to return multiple objects from a Java method?

I want to return two objects from a Java method and was wondering what could be a good way of doing so?
The possible ways I can think of are: return a HashMap (since the two Objects are related) or return an ArrayList of Object objects.
To be more precise, the two objects I want to return are (a) List of objects and (b) comma separated names of the same.
I want to return these two Objects from one method because I dont want to iterate through the list of objects to get the comma separated names (which I can do in the same loop in this method).
Somehow, returning a HashMap does not look a very elegant way of doing so.
If you want to return two objects you usually want to return a single object that encapsulates the two objects instead.
You could return a List of NamedObject objects like this:
public class NamedObject<T> {
public final String name;
public final T object;
public NamedObject(String name, T object) {
this.name = name;
this.object = object;
}
}
Then you can easily return a List<NamedObject<WhateverTypeYouWant>>.
Also: Why would you want to return a comma-separated list of names instead of a List<String>? Or better yet, return a Map<String,TheObjectType> with the keys being the names and the values the objects (unless your objects have specified order, in which case a NavigableMap might be what you want.
If you know you are going to return two objects, you can also use a generic pair:
public class Pair<A,B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
};
Edit A more fully formed implementation of the above:
package util;
public class Pair<A,B> {
public static <P, Q> Pair<P, Q> makePair(P p, Q q) {
return new Pair<P, Q>(p, q);
}
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((a == null) ? 0 : a.hashCode());
result = prime * result + ((b == null) ? 0 : b.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
#SuppressWarnings("rawtypes")
Pair other = (Pair) obj;
if (a == null) {
if (other.a != null) {
return false;
}
} else if (!a.equals(other.a)) {
return false;
}
if (b == null) {
if (other.b != null) {
return false;
}
} else if (!b.equals(other.b)) {
return false;
}
return true;
}
public boolean isInstance(Class<?> classA, Class<?> classB) {
return classA.isInstance(a) && classB.isInstance(b);
}
#SuppressWarnings("unchecked")
public static <P, Q> Pair<P, Q> cast(Pair<?, ?> pair, Class<P> pClass, Class<Q> qClass) {
if (pair.isInstance(pClass, qClass)) {
return (Pair<P, Q>) pair;
}
throw new ClassCastException();
}
}
Notes, mainly around rustiness with Java & generics:
both a and b are immutable.
makePair static method helps you with boiler plate typing, which the diamond operator in Java 7 will make less annoying. There's some work to make this really nice re: generics, but it should be ok-ish now. (c.f. PECS)
hashcode and equals are generated by eclipse.
the compile time casting in the cast method is ok, but doesn't seem quite right.
I'm not sure if the wildcards in isInstance are necessary.
I've just written this in response to comments, for illustration purposes only.
In the event the method you're calling is private, or called from one location, try
return new Object[]{value1, value2};
The caller looks like:
Object[] temp=myMethod(parameters);
Type1 value1=(Type1)temp[0]; //For code clarity: temp[0] is not descriptive
Type2 value2=(Type2)temp[1];
The Pair example by David Hanak has no syntactic benefit, and is limited to two values.
return new Pair<Type1,Type2>(value1, value2);
And the caller looks like:
Pair<Type1, Type2> temp=myMethod(parameters);
Type1 value1=temp.a; //For code clarity: temp.a is not descriptive
Type2 value2=temp.b;
You may use any of following ways:
private static final int RETURN_COUNT = 2;
private static final int VALUE_A = 0;
private static final int VALUE_B = 1;
private static final String A = "a";
private static final String B = "b";
1) Using Array
private static String[] methodWithArrayResult() {
//...
return new String[]{"valueA", "valueB"};
}
private static void usingArrayResultTest() {
String[] result = methodWithArrayResult();
System.out.println();
System.out.println("A = " + result[VALUE_A]);
System.out.println("B = " + result[VALUE_B]);
}
2) Using ArrayList
private static List<String> methodWithListResult() {
//...
return Arrays.asList("valueA", "valueB");
}
private static void usingListResultTest() {
List<String> result = methodWithListResult();
System.out.println();
System.out.println("A = " + result.get(VALUE_A));
System.out.println("B = " + result.get(VALUE_B));
}
3) Using HashMap
private static Map<String, String> methodWithMapResult() {
Map<String, String> result = new HashMap<>(RETURN_COUNT);
result.put(A, "valueA");
result.put(B, "valueB");
//...
return result;
}
private static void usingMapResultTest() {
Map<String, String> result = methodWithMapResult();
System.out.println();
System.out.println("A = " + result.get(A));
System.out.println("B = " + result.get(B));
}
4) Using your custom container class
private static class MyContainer<M,N> {
private final M first;
private final N second;
public MyContainer(M first, N second) {
this.first = first;
this.second = second;
}
public M getFirst() {
return first;
}
public N getSecond() {
return second;
}
// + hashcode, equals, toString if need
}
private static MyContainer<String, String> methodWithContainerResult() {
//...
return new MyContainer("valueA", "valueB");
}
private static void usingContainerResultTest() {
MyContainer<String, String> result = methodWithContainerResult();
System.out.println();
System.out.println("A = " + result.getFirst());
System.out.println("B = " + result.getSecond());
}
5) Using AbstractMap.simpleEntry
private static AbstractMap.SimpleEntry<String, String> methodWithAbstractMapSimpleEntryResult() {
//...
return new AbstractMap.SimpleEntry<>("valueA", "valueB");
}
private static void usingAbstractMapSimpleResultTest() {
AbstractMap.SimpleEntry<String, String> result = methodWithAbstractMapSimpleEntryResult();
System.out.println();
System.out.println("A = " + result.getKey());
System.out.println("B = " + result.getValue());
}
6) Using Pair of Apache Commons
private static Pair<String, String> methodWithPairResult() {
//...
return new ImmutablePair<>("valueA", "valueB");
}
private static void usingPairResultTest() {
Pair<String, String> result = methodWithPairResult();
System.out.println();
System.out.println("A = " + result.getKey());
System.out.println("B = " + result.getValue());
}
I almost always end up defining n-Tuple classes when I code in Java. For instance:
public class Tuple2<T1,T2> {
private T1 f1;
private T2 f2;
public Tuple2(T1 f1, T2 f2) {
this.f1 = f1; this.f2 = f2;
}
public T1 getF1() {return f1;}
public T2 getF2() {return f2;}
}
I know it's a bit ugly, but it works, and you just have to define your tuple types once. Tuples are something Java really lacks.
EDIT: David Hanak's example is more elegant, as it avoids defining getters and still keeps the object immutable.
Before Java 5, I would kind of agree that the Map solution isn't ideal. It wouldn't give you compile time type checking so can cause issues at runtime. However, with Java 5, we have Generic Types.
So your method could look like this:
public Map<String, MyType> doStuff();
MyType of course being the type of object you are returning.
Basically I think that returning a Map is the right solution in this case because that's exactly what you want to return - a mapping of a string to an object.
Apache Commons has tuple and triple for this:
ImmutablePair<L,R> An immutable pair consisting of two Object
elements.
ImmutableTriple<L,M,R> An immutable triple consisting of
three Object elements.
MutablePair<L,R> A mutable pair consisting of
two Object elements.
MutableTriple<L,M,R> A mutable triple
consisting of three Object elements.
Pair<L,R> A pair consisting of
two elements.
Triple<L,M,R> A triple consisting of three elements.
Source: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html
Alternatively, in situations where I want to return a number of things from a method I will sometimes use a callback mechanism instead of a container. This works very well in situations where I cannot specify ahead of time just how many objects will be generated.
With your particular problem, it would look something like this:
public class ResultsConsumer implements ResultsGenerator.ResultsCallback
{
public void handleResult( String name, Object value )
{
...
}
}
public class ResultsGenerator
{
public interface ResultsCallback
{
void handleResult( String aName, Object aValue );
}
public void generateResults( ResultsGenerator.ResultsCallback aCallback )
{
Object value = null;
String name = null;
...
aCallback.handleResult( name, value );
}
}
While in your case, the comment may be a good way to go, in Android, you can use Pair . Simply
return new Pair<>(yourList, yourCommaSeparatedValues);
Use of following Entry object
Example :
public Entry<A,B> methodname(arg)
{
.......
return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
}
Regarding the issue about multiple return values in general I usually use a small helper class that wraps a single return value and is passed as parameter to the method:
public class ReturnParameter<T> {
private T value;
public ReturnParameter() { this.value = null; }
public ReturnParameter(T initialValue) { this.value = initialValue; }
public void set(T value) { this.value = value; }
public T get() { return this.value; }
}
(for primitive datatypes I use minor variations to directly store the value)
A method that wants to return multiple values would then be declared as follows:
public void methodThatReturnsTwoValues(ReturnParameter<ClassA> nameForFirstValueToReturn, ReturnParameter<ClassB> nameForSecondValueToReturn) {
//...
nameForFirstValueToReturn.set("...");
nameForSecondValueToReturn.set("...");
//...
}
Maybe the major drawback is that the caller has to prepare the return objects in advance in case he wants to use them (and the method should check for null pointers)
ReturnParameter<ClassA> nameForFirstValue = new ReturnParameter<ClassA>();
ReturnParameter<ClassB> nameForSecondValue = new ReturnParameter<ClassB>();
methodThatReturnsTwoValues(nameForFirstValue, nameForSecondValue);
Advantages (in comparison to other solutions proposed):
You do not have to create a special class declaration for individual methods and its return types
The parameters get a name and therefore are easier to differentiate when looking at the method signature
Type safety for each parameter
All possible solutions will be a kludge (like container objects, your HashMap idea, “multiple return values” as realized via arrays). I recommend regenerating the comma-separated list from the returned List. The code will end up being a lot cleaner.
Keep it simple and create a class for multiple result situation. This example accepts an ArrayList and a message text from a databasehelper getInfo.
Where you call the routine that returns multiple values you code:
multResult res = mydb.getInfo();
In the routine getInfo you code:
ArrayList<String> list= new ArrayList<String>();
add values to the list...
return new multResult("the message", list);
and define a class multResult with:
public class multResult {
public String message; // or create a getter if you don't like public
public ArrayList<String> list;
multResult(String m, ArrayList<String> l){
message = m;
list= l;
}
}
As I see it there are really three choices here and the solution depends on the context. You can choose to implement the construction of the name in the method that produces the list. This is the choice you've chosen, but I don't think it is the best one. You are creating a coupling in the producer method to the consuming method that doesn't need to exist. Other callers may not need the extra information and you would be calculating extra information for these callers.
Alternatively, you could have the calling method calculate the name. If there is only one caller that needs this information, you can stop there. You have no extra dependencies and while there is a little extra calculation involved, you've avoided making your construction method too specific. This is a good trade-off.
Lastly, you could have the list itself be responsible for creating the name. This is the route I would go if the calculation needs to be done by more than one caller. I think this puts the responsibility for the creation of the names with the class that is most closely related to the objects themselves.
In the latter case, my solution would be to create a specialized List class that returns a comma-separated string of the names of objects that it contains. Make the class smart enough that it constructs the name string on the fly as objects are added and removed from it. Then return an instance of this list and call the name generation method as needed. Although it may be almost as efficient (and simpler) to simply delay calculation of the names until the first time the method is called and store it then (lazy loading). If you add/remove an object, you need only remove the calculated value and have it get recalculated on the next call.
Can do some thing like a tuple in dynamic language (Python)
public class Tuple {
private Object[] multiReturns;
private Tuple(Object... multiReturns) {
this.multiReturns = multiReturns;
}
public static Tuple _t(Object... multiReturns){
return new Tuple(multiReturns);
}
public <T> T at(int index, Class<T> someClass) {
return someClass.cast(multiReturns[index]);
}
}
and use like this
public Tuple returnMultiValues(){
return Tuple._t(new ArrayList(),new HashMap())
}
Tuple t = returnMultiValues();
ArrayList list = t.at(0,ArrayList.class);
I followed a similar approach than the described in the other answers with a few tweaks based on the requirement I had, basically I created the following classes(Just in case, everything is Java):
public class Pair<L, R> {
final L left;
final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public <T> T get(Class<T> param) {
return (T) (param == this.left.getClass() ? this.left : this.right);
}
public static <L, R> Pair<L, R> of(L left, R right) {
return new Pair<L, R>(left, right);
}
}
Then, my requirement was simple, in the repository Class that reaches the DB, for the Get Methods than retrieve data from the DB, I need to check if it failed or succeed, then, if succeed, I needed to play with the returning list, if failed, stop the execution and notify the error.
So, for example, my methods are like this:
public Pair<ResultMessage, List<Customer>> getCustomers() {
List<Customer> list = new ArrayList<Customer>();
try {
/*
* Do some work to get the list of Customers from the DB
* */
} catch (SQLException e) {
return Pair.of(
new ResultMessage(e.getErrorCode(), e.getMessage()), // Left
null); // Right
}
return Pair.of(
new ResultMessage(0, "SUCCESS"), // Left
list); // Right
}
Where ResultMessage is just a class with two fields (code/message) and Customer is any class with a bunch of fields that comes from the DB.
Then, to check the result I just do this:
void doSomething(){
Pair<ResultMessage, List<Customer>> customerResult = _repository.getCustomers();
if (customerResult.get(ResultMessage.class).getCode() == 0) {
List<Customer> listOfCustomers = customerResult.get(List.class);
System.out.println("do SOMETHING with the list ;) ");
}else {
System.out.println("Raised Error... do nothing!");
}
}
In C++ (STL) there is a pair class for bundling two objects. In Java Generics a pair class isn't available, although there is some demand for it. You could easily implement it yourself though.
I agree however with some other answers that if you need to return two or more objects from a method, it would be better to encapsulate them in a class.
Why not create a WhateverFunctionResult object that contains your results, and the logic required to parse these results, iterate over then etc. It seems to me that either:
These results objects are intimately tied together/related and belong together, or:
they are unrelated, in which case your function isn't well defined in terms of what it's trying to do (i.e. doing two different things)
I see this sort of issue crop up again and again. Don't be afraid to create your own container/result classes that contain the data and the associated functionality to handle this. If you simply pass the stuff around in a HashMap or similar, then your clients have to pull this map apart and grok the contents each time they want to use the results.
public class MultipleReturnValues {
public MultipleReturnValues() {
}
public static void functionWithSeveralReturnValues(final String[] returnValues) {
returnValues[0] = "return value 1";
returnValues[1] = "return value 2";
}
public static void main(String[] args) {
String[] returnValues = new String[2];
functionWithSeveralReturnValues(returnValues);
System.out.println("returnValues[0] = " + returnValues[0]);
System.out.println("returnValues[1] = " + returnValues[1]);
}
}
This is not exactly answering the question, but since every of the solution given here has some drawbacks, I suggest to try to refactor your code a little bit so you need to return only one value.
Case one.
You need something inside as well as outside of your method. Why not calculate it outside and pass it to the method?
Instead of:
[thingA, thingB] = createThings(...); // just a conceptual syntax of method returning two values, not valid in Java
Try:
thingA = createThingA(...);
thingB = createThingB(thingA, ...);
This should cover most of your needs, since in most situations one value is created before the other and you can split creating them in two methods. The drawback is that method createThingsB has an extra parameter comparing to createThings, and possibly you are passing exactly the same list of parameters twice to different methods.
Case two.
Most obvious solution ever and a simplified version of case one. It's not always possible, but maybe both of the values can be created independently of each other?
Instead of:
[thingA, thingB] = createThings(...); // see above
Try:
thingA = createThingA(...);
thingB = createThingB(...);
To make it more useful, these two methods can share some common logic:
public ThingA createThingA(...) {
doCommonThings(); // common logic
// create thing A
}
public ThingB createThingB(...) {
doCommonThings(); // common logic
// create thing B
}
Pass a list to your method and populate it, then return the String with the names, like this:
public String buildList(List<?> list) {
list.add(1);
list.add(2);
list.add(3);
return "something,something,something,dark side";
}
Then call it like this:
List<?> values = new ArrayList<?>();
String names = buildList(values);
You can utilize a HashMap<String, Object> as follows
public HashMap<String, Object> yourMethod()
{
.... different logic here
HashMap<String, Object> returnHashMap = new HashMap<String, Object>();
returnHashMap.put("objectA", objectAValue);
returnHashMap.put("myString", myStringValue);
returnHashMap.put("myBoolean", myBooleanValue);
return returnHashMap;
}
Then when calling the method in a different scope, you can cast each object back to its initial type:
// call the method
HashMap<String, Object> resultMap = yourMethod();
// fetch the results and cast them
ObjectA objectA = (ObjectA) resultMap.get("objectA");
String myString = (String) resultMap.get("myString");
Boolean myBoolean = (Boolean) resultMap.get("myBoolean");
I noticed there is no no-custom class, n-length, no-cast, type-safe answers yet to returning multiple values.
Here is my go:
import java.util.Objects;
public final class NTuple<V, T extends NTuple<?, ?>> {
private final V value;
private final T next;
private NTuple(V value, T next) {
this.value = value;
this.next = next;
}
public static <V> NTuple<V, ?> of(V value) {
return new NTuple<>(value, null);
}
public static <V, T extends NTuple<?, ?>> NTuple<V, T> of(V value, T next) {
return new NTuple<>(value, next);
}
public V value() {
return value;
}
public T next() {
return next;
}
public static <V> V unpack0(NTuple<V, ?> tuple) {
return Objects.requireNonNull(tuple, "0").value();
}
public static <V, T extends NTuple<V, ?>> V unpack1(NTuple<?, T> tuple) {
NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
NTuple<V, ?> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
return tuple1.value();
}
public static <V, T extends NTuple<?, NTuple<V, ?>>> V unpack2(NTuple<?, T> tuple) {
NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
NTuple<?, NTuple<V, ?>> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
NTuple<V, ?> tuple2 = Objects.requireNonNull(tuple1.next(), "2");
return tuple2.value();
}
}
Sample use:
public static void main(String[] args) {
// pre-java 10 without lombok - use lombok's var or java 10's var if you can
NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> multiple = wordCount("hello world");
String original = NTuple.unpack0(multiple);
Integer wordCount = NTuple.unpack1(multiple);
Integer characterCount = NTuple.unpack2(multiple);
System.out.println(original + ": " + wordCount + " words " + characterCount + " chars");
}
private static NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> wordCount(String s) {
int nWords = s.split(" ").length;
int nChars = s.length();
return NTuple.of(s, NTuple.of(nWords, NTuple.of(nChars)));
}
Pros:
no-custom container class - no need to write a class just for a return type
n-length - can handle any number of return values
no-cast - no need to cast from Object
type-safe - the types are checked via Java's generics
Cons:
inefficient for large numbers of return values
according to my experience with python's multiple return values, this should not happen in practice
heavy type declarations
can be alleviated by lombok/Java 10 var
In C, you would do it by passing pointers to placeholders for the results as arguments:
void getShoeAndWaistSizes(int *shoeSize, int *waistSize) {
*shoeSize = 36;
*waistSize = 45;
}
...
int shoeSize, waistSize;
getShoeAndWaistSize(&shoeSize, &waistSize);
int i = shoeSize + waistSize;
Let's try something similar, in Java.
void getShoeAndWaistSizes(List<Integer> shoeSize, List<Integer> waistSize) {
shoeSize.add(36);
waistSize.add(45);
}
...
List<Integer> shoeSize = new List<>();
List<Integer> waistSize = new List<>();
getShoeAndWaistSizes(shoeSize, waistSize);
int i = shoeSize.get(0) + waistSize.get(0);
PASS A HASH INTO THE METHOD AND POPULATE IT......
public void buildResponse(String data, Map response);

Categories