How to Get attributes list from ArrayList of objects - java

Suppose I have an object that looks like:
public class Obj {
String foo;
String bar;
}
If I create an arraylist of type Obj and populate it, is there a way I can return a list of all the objects in the array's foo attribute from the ArrayList?
EDIT: I should have been more clear, I did not want to do this via iteration

Try this:
Collection<String> names = CollectionUtils.collect(personList, TransformerUtils.invokerTransformer("getName"));
Use apache commons collection api.

You'll have to iterate through your List<Obj> and collate the foo entries into a new List
e.g.
List<String> foos = new ArrayList<String>();
for (Obj obj : objs) {
foos.add(obj.foo)
}
or for Java 8 and beyond, use streams thus:
objs.stream().map(o -> o.foo).collect(toList());

Using Guava you could create a view of the foo property of the objects in the List using Lists.transform like this:
public class Obj {
String foo;
String bar;
public static final Function<Obj, String> FOO = new Function<Obj, String>() {
public String apply(Obj input) {
return input.foo;
}
};
}
public void someMethod(List<Obj> objs) {
List<String> foos = Lists.transform(objs, Obj.FOO);
...
}
Unlike other solutions, this is purely a view of the List<Obj> and as such it doesn't allocate a whole separate ArrayList or some such in memory and can be created in almost no time regardless of the size of your List<Obj>. Additionally, if you change the original List<Obj>, the foos list will reflect that change.
In Java 8 (due in 2012 sometime), this will become a lot easier with lambda expressions and method references. You'll be able to do something like this:
List<Obj> objs = ...
List<String> foos = objs.map(#Obj.getFoo);

The answer of #Surodip uses a compact solution based on Apache Commons Collections.
But that solution is not typesafe, since the Transfomer references the property via string expression: TransformerUtils.invokerTransformer("getName")
Here is a more verbose, but typesafe solution using Apache Commons Collections:
Collection<String> values = CollectionUtils.collect(messages, new Transformer<Obj, String>(){
#Override
public String transform(Obj input) {
return input.getFoo();
}
});
The above solutions uses Apache Commons Collection Version >= 4, which supports generics for type safety.
Below is the less typesafe version for Apache Collections Version < 4, which does not use generics:
Collection values = CollectionUtils.collect(messages, new Transformer(){
#Override
public Object transform(Object input) {
Obj obj = (Obj) input;
return obj.getFoo();
}
});

Iterate through the list and create a Set of all foo properties.

something like this?
List<String> foos = new ArrayList<String>();
for (Obj obj : objList )
{
foos.addElement(obj.foo);
}

Related

Suspicious call to 'LinkedHashMap.get'

Hello I have the following code
public static LinkedHashMap<Object, String[]> dataMap = new LinkedHashMap<>();
public static void parseDataset(int line){
String[] dataArr = dataMap.get(dataMap.keySet().toArray()[line]);
}
Since the Object I use as a Key is dynamically generated I have no knowledge about it so I have to find it before I can use it to get its value.
This code gives me the warning Suspicious call to 'LinkedHashMap.get', is that a problem and how would I get rid of the warning?
You don't need to use get: instead of converting the keys to an array, use values() instead. This works because values() iterates in the same order as the corresponding keys():
String[] dataArr = (String[]) dataMap.values().toArray()[line];
But you don't need to use toArray() either, which wastefully allocates an array containing all values, from which you only want one: you can just iterate through the values to get the thing you want:
static String[] nthItem(int n) {
int i = 0;
for (String[] value : dataMap.values()) {
if (i == n) return value;
++i;
}
throw new ArrayIndexOutOfBoundsException();
}
Or:
String[] dataArr = dataMap.values().stream().skip(line).findFirst().orElseThrow();
(Existing implementations of this sort of thing can be found in commonly-used libraries, e.g. Guava's Iterables.get)

Lazy reinit a list to modifiable after declaring with Collections.emptyList()

Want to declare a list as List<String> info = Collections.emptyList()
but when user calls add(String msg) then re-init to a modifiable list.
Is this the correct way:
private List<String> info = Collections.emptyList();
public void addInfo(String s){
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
}
Or
if(info.equals(e)){
If I have 3 of these can I have this common code :
public void addInfo(String s) {
info = addTo(s, info);
}
public void addWarn(String s) {
warn = addTo(s, warn);
}
public void addErr(String s) {
errs = addTo(s, errs);
}
private List<String> addTo(String s, #org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
return t;
}
I guess the following wont work due to the new list being created?
private void addTo(String s, #org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
}
Note that even if Collections.emptyList() always returns the one instance held in Collections.EMPTY_LIST, a reference comparison does not detect when a caller used JDK 9+ List.of() to initialize the field. On the other hand, being non-empty does not guaranty mutability either.
The entire logic is suitable only for a private method were all callers and their usage are known.
But you should consider the alternative of dropping these special cases altogether. Since Java 8, the default constructor new ArrayList<>() will not create a backing array. It is deferred until the first addition of an element.
So you can initialize all fields with a plain new ArrayList<>() and implement the addInfo, addWarn, and addErr with a plain add call, getting rid of the addTo method, the conditionals, and the repeated assignments. Even declaring the fields final is possible. While still not requiring a significant amount of memory for the unused lists.
Using .equals is the only correct solution -- but equivalent to the much simpler info.isEmpty().
Considering your code:
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
I don't believe there's any guarantee in the Java API that the same reference will always be returned from emptyList() (the javadoc states "Implementations of this method need not create a separate List object for each call").
Given you may modify the list it'd make more sense to initialise with new ArrayList<>() rather than emptyList(). Really doesn't make much sense to use an unmodifiable list that you may want to modify.
However if you really need to use emptyList() for some reason, then perhaps:
if (info.isEmpty())
info = new ArrayList<>();
Given you are about to add an item to it this test will only pass once anyway.

Serialize list of objects, keyed by attribute

Say I have a class:
public class Person {
String name;
Int age;
}
and a list of objects of this class:
List<Person> people = ...
Normally, running this through a serializer such as Jackson or Gson would result in this:
"[{'name':'John','age':42},{'name':'Sam','age':43}]
but I am looking to serialize to a single json object where each property is a list containing the attributes, like this:
"{'name':['John','Sam'],'age':[42,43]}"
Do any of the serialization libraries support this?
I'd create a sort of "wrapper" that takes in any amount of persons and stores the fields in a way that let them be serialized that way. So in this case, you would create a series of persons, create a wrapper containing those persons and then serialize that.
public class PersonWrapper {
private int[] ages;
private String[] names;
public PersonWrapper(Person... persons) {
ages = new int[persons.length];
names = new String[persons.length];
for (int i = 0; i < persons.length; i++) {
ages[i] = persons[i].getAge();
names[i] = persons[i].getName();
}
}
}
Transform your List<Person> to new object.
class NewClass {
List<String> name;
List<Integer> ages;
}
Then pass this object through the Serializer to get:
"{'name':['John','Sam'],'age':[42,43]}"
Serialization libraries are generally not designed for stuff like that.
What you're looking for is JSON tree transformation and you can easily implement it in both Gson and Jackson.
Here is a transformation example for Gson:
final class Transformations {
private Transformations() {
}
static JsonObject transposeShallow(final Iterable<? extends JsonElement> inArray) {
final JsonObject outObject = new JsonObject();
for ( final String name : scanAllNames(inArray) ) {
final JsonArray outSubArray = new JsonArray();
for ( final JsonElement inJsonElement : inArray ) {
if ( !inJsonElement.isJsonObject() ) {
throw new IllegalArgumentException(inJsonElement + " is not a JSON object");
}
outSubArray.add(inJsonElement.getAsJsonObject().get(name));
}
outObject.add(name, outSubArray);
}
return outObject;
}
private static Iterable<String> scanAllNames(final Iterable<? extends JsonElement> jsonArray) {
final ImmutableSet.Builder<String> allNames = new ImmutableSet.Builder<>();
for ( final JsonElement jsonElement : jsonArray ) {
if ( !jsonElement.isJsonObject() ) {
throw new IllegalArgumentException(jsonElement + " is not a JSON object");
}
allNames.addAll(jsonElement.getAsJsonObject().keySet());
}
return allNames.build();
}
}
The transformations above can be incorporated into serialization process, but this would affect the structure for your objects (e.g. how to indicate transposable collections for root objects and their fields? how to introduce a new "transposing" type and incorporate it in your data model type system? how to deserialize it properly if necessary?).
Once you get a JSON tree, you can transpose it on any nesting level.
Transposing the root element is the simplest way as long as you don't need to transform nested elements.
private static final Type peopleType = new TypeToken<Collection<Person>>() {}.getType();
public static void main(final String... args) {
System.out.println(gson.toJson(people, peopleType));
System.out.println(gson.toJson(Transformations.transposeShallow(gson.toJsonTree(people, peopleType).getAsJsonArray())));
}
that gives:
[{"name":"John","age":42},{"name":"Sam","age":43}]
{"name":["John","Sam"],"age":[42,43]}
The solution above can work with almost any types in your code, but is has a small penalty for building a JSON tree.
Something like PersonWrapper as suggested by Schred might be another option but this requires wrappers for all your types and they need to be updated once your wrapped classes change.
Also, you might also be interested in libraries like Jolt that are designed for JSON transformations (not sure if it's doable in Jolt though).

Java - Generics with lists

I wrote a function for my cache to retrieve a specific object. This way I don't need to cast it .
#SuppressWarnings("unchecked")
public static <T> T inCache(Class<T> obj, String token) {
Object cacheObj = Cache.get(token);
if (cacheObj != null) {
if (obj.isAssignableFrom(cacheObj.getClass())) {
return (T) cacheObj;
}
}
return null;
}
I am using it like this
String s = inCache(String.class, title);
But now I have a list of Strings in my cache and I can't use it like this
List<String> ipList = Util.inCache(List<String>.class, title);
The problem is the List<String>.class . I am very new to java, how do I have to write it?
There is a concept in java called type erasure. Due to legacy reasons, something like List is just a list. It doesn't remember that it is a list of string at run time. You should just write List.class.
You can then specify the type of object in the List when iterating through it.
You can't get class of List<String>, in your case the only way is:
List<String> ipList = (List<String>)Util.inCache(List.class, title);
You can try :
List<String> ipList = Util.inCache(List.class, title);
Try this-
List<String> inList = (List<String>)Test.inCache(List.class, title);
And you can do also -
List<String> inList = Test.inCache((Class<? extends List<String>>)List.class, token);
Just to clarify Joe's answer ( I don't have enough reputation to comment), at runtime there is no difference between a List <String> and List<Integer> or any other type of List, generics aren't kept at runtime.
Meaning, List<String>.class is completely identical to List<Integer>.class and is actually List.class. This is a weakness of the Java type system. I'm not familiar with a simple way to implement what you wish for.
A code proof for the heck of it :
// It is true that
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
System.out.println( stringList.getClass() == integerList.getClass() );
// And that ...
List objectList = new ArrayList();
System.out.println( stringList.getClass() == objectList.getClass() );
//However, the following is false because a different implementation is used ( I wanted a false case)
List objectLinkedList = new LinkedList();
System.out.println( objectLinkedList.getClass() == objectList.getClass() );

Is it possible to avoid loop which will cast one object type to another in split() or Arrays.asList()?

I'm trying to simplify my code and I have a question: is it's possible to convert string of IDs separated by coma to specific collection type?
So, my code now is:
String [] condition_list_id_tmp = rule.getContractRuleConditions().split(",");
List<String> condition_list_id = Arrays.asList(condition_list_id_tmp);
List<Long> condition_ids = new ArrayList<Long>();
for (String str_id : condition_list_id){
condition_ids.add(Long.parseLong(str_id));
}
Can I simplify this code by using for example Type collectionType = new TypeToken<List<Long>>() {}.getType(); like in gson?
Have you considered LambdaJ?
class StringToLong implements Converter<String, Long> {
public Long convert(String str) {
return Long.parseLong(str);
}
}
String [] condition_list_id_tmp = rule.getContractRuleConditions().split(",");
List<String> condition_list_id = Arrays.asList(condition_list_id_tmp);
List<Long> condition_ids = convert(condition_list_id, new StringToLong());
or using some libraries, like guava? so that there is no looping in your codes (but in theirs) ?
I saw that your mentioned your goal is "to simplify my code"
e.g.
final List<String> strList = Arrays.asList("1,2,3,4,5".split(","));
final List<Long> l = Lists.transform(strList, new Function<String, Long>() {
#Override
public Long apply(final String input) {
return Long.parseLong(input);
}
});
I don't think so with out looping you can do this. How come your collection's content type be changed with out casting explicitly .
There is no method available without looping. Even if a method is available it will look like to you as a single operation but obviously it has to
loop internally.
For eg: Arrays.fill(arrayname, intval);
This is a single method to fill the array with any integer value. But internally it will also run a loop on the array.
java 8 can do this :
String [] condition_list_id_tmp = rule.getContractRuleConditions().split(",");
List<String> condition_list_id = Arrays.asList(condition_list_id_tmp);
List<Long> condition_ids = condition_list_id.map(c -> Long.parseLong(c))
The solution for your problem until Java 8 pop to the market could be project Guava, with their support for Functional Idioms.
Then you could perform that operation in different way, but as i wrote in the comment. At the end you will have same operation.
public static List<Long> splitToLong(String list, String token) {
StringTokenizer tokenizer= new StringTokenizer(list, token);
List<Long> result = new ArrayList<Long>();
while(tokenizer.hasMoreTokens() {
result.add(Long.parseLong(tokenizer.nextToken()));
}
}
If you put this method in some Util class, then you can enjoy clean code
//....
for(Long mLong : StringHelper.splitToLong(message,",")) {
//Do something with mLong
}
//....

Categories