Java8 Lambda expression to iterate over enum values and initialize final member - java

I have a static enum like this:
private static enum standardAttributes {
id, gender, firstname, lastname, mail, mobile
}
I need all the values as String. Therefore I have a method like this:
public static List<String> getStandardRecipientsAttributes() {
List<String> standardAttributesList = new ArrayList<String>();
for (standardAttributes s : standardAttributes.values())
standardAttributesList.add(s.toString());
return standardAttributesList;
}
There is no need to create the same List everytime this method is called. So I created a static member:
static final List<String> standardAttributesList;
static {
standardAttributesList = getStandardRecipientsAttributes();
}
This is all fine, but I wonder if there is a fancy Lambda expression to replace the method. Something like this:
Arrays.asList(standardAttributes.values()).forEach((attribute) -> standardAttributesList.add(attribute.toString()));
Two questions:
Can I avoid the Arrays.asList wrapper?
How can I handle the compiler error: The blank final field standardAttributesList may not have been initialized?

You can do
static final List<String> standardAttributesList =
Stream.of(values())
.map(Enum::name)
.collect(Collectors.toList());
This will create a Stream from an the array of values, apply the .name() method to each one and finally collect all the results into a List.

Related

Question about the form of returning value of String[] in java

When there is a getter method with a return type of String[],
for example:
private static Map<Integer, String[]> map = new HashMap();
public static void setterMethod(int id, String number, String address) {
String[] arr = new String[2];
arr[0] = "number";
arr[1] = "address";
map.put(id, arr);
}
public String[] getterMethod(int id) {
String[] arr = new String[]{map.get(id)[0], map.get(id)[1]};
return arr;
}
When we do a unit test, I need to do something like:
Assert.assertEquals(getterMethod(1), Arrays.asList("number", "Address"));
but this test will fail since getterMethod(1) will print out something like: String#a1b2c223d
How can we make getterMethod() to return in the form of [number, address] whilest maintaining the return type of the getterMethod?
There are two issues here.
First, gettMethod retuns an array of Strings (String[]) and Arrays.asList returns a List<String>, so they will never be equal. Instead of Arrays.asList, you should create an array.
Second, arrays don't overwrite the equals(Object) method, so you need to use a different assertion method that handles arrays:
Assert.assertArraysEquals(getterMethod(1), new String[]{"number", "Address"});

Convert a List of objects to a String array

I have the below pojo which consists of the below members so below is the pojo with few members in it
public class TnvoicetNotify {
private List<TvNotifyContact> toMap = new ArrayList<TvNotifyContact>();
private List<TvNotifyContact> ccMap = new ArrayList<TvNotifyContact>();
}
now in some other class i am getting the object of above class TnvoicetNotify in a method signature as parameter as shown below .. So i want to write the code of extraction from list and storing them in string array within this method itself
public void InvPostPayNotification(TnvoicetNotify TnvoicetNotify)
{
String[] mailTo = it should contain all the contents of list named toMap
String[] mailCC = it should contain all the contents of list named ccMap
}
now in the above class i need to extract the toMap which is of type list in the above pojo named TnvoicetNotify and i want to store each item if arraylist in a string array as shown in below fashion
for example first item in list is A1 and second is A2 and third is A3
so it should be stored in string array as
String[] mailTo = {"A1","A2","A3"};
similarly i want to achieve the same for cc section also as in above pojo it is in list i want to store in the below fashion
String[] mailCc = {"C1","C2","C3"};
so pls advise how to achieve this within InvPostPayNotification method
Pseudo code, because I don't know details for TnvoicetNotify:
public void invPostPayNotification(final TnvoicetNotify tnvoicetNotify)
{
final List<String> mailToList = new ArrayList<>();
for (final TvNotifyContact tv : tnvoicetNotify.getToMap()) { // To replace: getToMap()
mailToList.add(tv.getEmail()); // To replace: getEmail()
}
final String[] mailTo = mailToList.toArray(new String[mailToList.size()])
// same for mailCc then use both arrays
}
If you are using Java 8, you could simply use a one liner :
String[] mailCC = ccMap.stream().map(TvNotifyContact::getEmail).toArray(String[]::new);

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
}
//....

Multiple values in one ArrayList key

I'm trying to put multiple values in one arraylist key, but instead I get an error:
Class:
public class BestellenWindow extends javax.swing.JFrame {
private ArrayList<String> bestelling = new ArrayList<String>();
public BestellenWindow() {
initComponents();
}
Action performed:
private void BestellenbuttonActionPerformed(java.awt.event.ActionEvent evt)
{
bestelling.add(Barcodetext.getText(), Aantaltext.getText());
System.out.println(bestelling.get(0));
}
error:
no suitable method found for add(java.lang.String,java.lang.String)
method java.util.ArrayList.add(int,java.lang.String) is not applicable
(actual argument java.lang.String cannot be converted to int by method invocation conversion)
method java.util.ArrayList.add(java.lang.String) is not applicable
An ArrayList is just a list. It doesn't have a "key". If you want to store objects by key, use an implementation of interface Map (for example HashMap) instead of a List.
But, a normal Map can store only one value per key. If you want to store multiple values, you can use a Map<K, List<V>> (where K is the key type and V the value type), or you could use for example Multimap from Google Guava.
But there's also another, perhaps better solution. Create a new class to hold the barcode and aantal, and store instances of that class in your ArrayList. For example:
public class Bestelling {
private String barcode;
private int aantal;
public Bestelling(String barcode, int aantal) {
this.barcode = barcode;
this.aantal = aantal;
}
public String getBarcode() {
return barcode;
}
public int getAantal() {
return aantal;
}
}
// Later:
Bestelling b = new Bestelling(Barcodetext.getText(),
Integer.parseInt(Aantaltext.getText()));
bestelling.add(b);
In Java there are two elements.
1. List Interface : This does not hold any key. It has the collection of the values. You can add one by one by add(value) method.
2. Map : This holds one key for a set of value.
List<String> userName = new ArrayList<String>();
userName.add("Jesper");
userName.add("Mafue");
Map<Long,String> userMap = new HashMap<Long,String>();
userMap.put(1l,"Jesper");
userMap.put(2l,"Mafue");
From map you can retrieve the values by providing key.
You are trying to invoke the method
list.add(int index, String value)
that it is used to insert an element at a specific position.
If this is what you actually want to do then I guess that Barcodetext should contain a string that is a number, so you need to convert it to an int with
Integer.parseInt(Barcodetext.getText())
If instead you want to add multiple values just call the method twice:
bestelling.add(Barcodetext.getText());
bestelling.add(Aantaltext.getText());
Use either:
bestelling.add(...);
bestelling.add(...);
...
or
bestelling.addAll(Arrays.asList(..., ...));

Java: Getting an array of ENUM elements

Is there a better way of creating arrays from elements of an enum:
public static enum LOGICAL {
AND ("&", "AND"),
OR ("||", "OR");
public final String symbol;
public final String label;
LOGICAL(String symbol, String label) {
this.symbol=symbol;
this.label=label;
}
}
public static final String[] LOGICAL_NAMES = new String[LOGICAL.values().length];
static{
for(int i=0; i<LOGICAL.values().length; i++)
LOGICAL_NAMES[i]=LOGICAL.values()[i].symbol;
}
public static final String[] LOGICAL_LABELS = new String[LOGICAL.values().length];
static{
for(int i=0; i<LOGICAL.values().length; i++)
LOGICAL_LABELS[i]=LOGICAL.values()[i].label;
}
Personally I wouldn't expose them as an array, whose contents can be changed by anyone. I'd probably use an unmodifiable list instead - and probably expose that via a property rather than as a field. The initialization would be something like this:
private static final List<String> labels;
private static final List<String> values;
static
{
List<String> mutableLabels = new ArrayList<String>();
List<String> mutableValues = new ArrayList<String>();
for (LOGICAL x : LOGICAL.values())
{
mutableLabels.add(x.label);
mutableValues.add(x.value);
}
labels = Collections.unmodifiableList(mutableLabels);
values = Collections.unmodifiableList(mutableValues);
}
(If you're already using Guava you might even want to use ImmutableList instead, and expose the collections that way to make it clear that they are immutable.)
No. That seems the proper way. Even if there was some utility, it would rely on reflection
If you are using it often cache it in the enum
If you use your values very frequently and your enumeration gets bigger use Maps. Declare the following in your class.
private static EnumMap<LOGICAL,String> symbols = new EnumMap<LOGICAL, String>(LOGICAL.class);
and then just below it:
static{
for(LOGICAL i : LOGICAL.values().)
symbols.put(i, i.symbol);
}
then you can use symbols.values() or symbols.get(LOGICAL.AND) etc.

Categories