How should I instantiate List<List<String>> in Java - java

I have the following code:
List<List<String>> allData= getData()
if (allData== null)
allData= new ArrayList<ArrayList<String>>();
// populate allData below
Now I want to initialize allData but I get Type mismatch: cannot convert from ArrayList<ArrayList<String>> to List<List<String>>. What is the correct way I can initialize this?
It is not possible to return ArrayList<ArrayList<String>> from getData()
Thanks!

You do it very simply:
allData = new ArrayList<>();
Then you can add new lists to allData:
List innerList = new ArrayList<>();
innerList.add("some string");
// .... etc ...
allData.add(innerList);

You cannot redefine the generic type of the reference when you instantiate the concrete implementation. The reference is List<List<String>> so the assigned List must be capable of accepting any List<String> as an element. When you instantiated your instance, you attempted to limit this to ArrayList<String>.
The explicit solution is:
allData = new ArrayList<List<String>>();
or more simply as:
allData = new ArrayList<>();

A simple example of getData might be as below -
public static List<List<String>> getData(String fileName){
List<List<String>> content = null;
try (Stream<String> lines = Files.lines(Paths.get(fileName ))) {
content = lines
.map(l -> l.split(" "))
.map(Arrays::asList)
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
return content;
}

Related

How can I parse List<List> into ArrayList<HashMap> through lambda expression?

Maybe it's quite a stupid question but I cant find any solution for it. I am new to lambda and I am trying to use it to parse a list <List> into an ArrayList <HashMap>.
Initially I parse a data set from csv file, and I turn each row of them into a List, and add them into the List <'List'> I stated above. Since I have to filter them after this, I would like to change the each row of them into a HashMap<columnName, columnValue>, but when I try to do it, it returns nothing.
Here is my code:
// import data from csv file
private static List<List> readWholeFile(BufferedReader br) throws IOException {
List<List> rows = new ArrayList<>();
String line = br.readLine();
try {
if (line != null) {
String[] test = line.split(",");
List<String> row = Arrays.asList((String[]) line.split(","));
rows.add(row);
return readWholeFile(br);
}
} catch (NullPointerException ex) {
ex.printStackTrace();
} finally {
return rows;
}
}
//parse List<List> into ArrayList<HashMap>
private static ArrayList<HashMap> putRowIntoMap(List<List> rows, List<String> columns) {
ArrayList<HashMap> itemMap = new ArrayList<>();
List<HashMap> test = new ArrayList<>();
HashMap<String, String> eleMap = new HashMap<>();
rows.stream().map(row -> (row.stream().map(ele -> eleMap.put(keys.get(row.indexOf(ele)), (String) ele))))
.collect(Collectors.toCollection(ArrayList::new));
itemMap.add(eleMap);
System.out.println(eleMap); //output: {}
return itemMap;
}
Read the input file into List<List<String>> using NIO Files.lines returning lines of the text file as Stream<String>
public static List<List<String>> readFile(String filename) throws IOException {
return Files.lines(Paths.get(filename)) // Stream<String>
.map(line -> Arrays.asList(line.split(","))) // Stream<List<String>>
.collect(Collectors.toList()); // List<List<String>>
}
Convert the list of List<String> into map, assuming that the columnNames have the same indexes as the lists inside rows and all column names are different.
public static List<Map<String, String>> intoMap(List<List<String>> rows, List<String> columnNames) {
return rows.stream() // Stream<List<String>>
.map(row -> IntStream.range(0, row.size()) // IntStream
.boxed() // Stream<Integer>
.collect(Collectors.toMap(
i -> columnNames.get(i), // key: columnName
i -> row.get(i) // value
// ,(r1, r2) -> r1, // placeholder for merge function
// LinkedHashMap::new // supplier of LinkedHashMap
)) // Map<String, String>
) // List<Map<String, String>>
.collect(Collectors.toList());
}
By default, Collectors.toMap returns a HashMap implementation which is not ordered, so it could make sense to use LinkedHashMap maintaining insertion order (if needed, two commented lines in the toMap should be uncommented).

How to filter my List with Strings?

Could someone explain me why I can`t filter my strings in List by .stream().filter ? All I receive is unfiltered strings!
I will appreciate it.
public static void main(String[] args) throws FileNotFoundException {
List<Country> countries = CsvToBeanConverterCountry("country.csv");
countries
.stream().filter(s -> s.getName().contains("xx")).collect(Collectors.toList());
countries.forEach(s -> System.out.println(s.getName()));
}
private static List CsvToBeanConverterCountry(String csvFilename) throws FileNotFoundException {
CsvToBean csv = new CsvToBean();
CSVReader csvReader = new CSVReader(new FileReader(csvFilename), ';', '"', 1);
List list = csv.parse(setColumMappingCountry(), csvReader);
return list;
}
private static ColumnPositionMappingStrategy setColumMappingCountry() {
ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
strategy.setType(Country.class);
String[] columns = new String[]{"country_id", "city_id", "name"};
strategy.setColumnMapping(columns);
return strategy;
}
}
You ignored the result of collect:
countries.stream().filter(s -> s.getName().contains("xx"))
.collect(Collectors.toList());
So you should assign the result to a new list and print that list only
List<Country> result = countries.stream()...
result.forEach(...)
You should assign the result to countries:
countries = countries.stream().filter(s -> s.getName()
.contains("xx")).collect(Collectors.toList());
countries.forEach(s -> System.out.println(s.getName()));
countries.stream().filter(...) does not filter the countries list inline, it runs a stream and the result is ignored.
Also, you can run forEach on the stream directly without collecting into a list first:
countries.stream().filter(s -> s.getName()
.contains("xx"))
.forEach(s -> System.out.println(s.getName()));
It seems like your intent was to modify the original list, rather than to produce a new list with some of the elements.
In that case, you can simply remove the elements you don't want:
countries.removeIf(c -> !c.getName().contains("xx"));
This is more efficient than creating a stream and a new collection, but it alters the list.
Sure, what happens is that: .stream() doesn't change the actual list.
What you can do is:
List<Country> countries = CsvToBeanConverterCountry("country.csv");
countries
.stream()
.filter(s -> s.getName().contains("xx"))
.forEach(s -> System.out.println(s.getName()));

Convert from map to arraylist

I tried to convert a Map<String,ArrayList<Object>> to ArrayList<Object> using this code:
Collection<ArrayList<Object>> coll = map().values();
List list = new ArrayList(coll);
ArrayList<Object> arrayList = new ArrayList<>(list.size());
arrayList.addAll(list);
However, the arraylist I got still groups objects by key still as collection.
How can I convert to ArrayList of separate Objects?
You can use Java 8 Streams:
List<Object> list = map.values().stream()
.flatMap(ArrayList::stream)
.collect(Collectors.toList());
Without Streams, you'll have to iterate over the elements of your first List and call arrayList.addAll() for each of them separately.
A non stream version would be:
List<Object> accumulator = new ArrayList<>();
for(ArrayList<Object> a : map.values())
accumulator.addAll(a);
same result can be achieved by using following traditional approach as well -
Map<String, ArrayList<Object>> map = new HashMap<>();
ArrayList<Object> objects1 = new ArrayList<>();
objects1.add("data1");
objects1.add("data2");
objects1.add("data3");
map.put("key1", objects1);
ArrayList<Object> objects2 = new ArrayList<>();
objects2.add("data1");
objects2.add("data2");
objects2.add("data3");
map.put("key2", objects2);
Collection<ArrayList<Object>> collections = map.values();
List<Object> list = new ArrayList<>();
for(ArrayList<Object> collection : collections) {
for(Object obj : collection) {
list.add(obj);
}
}
System.out.println(list);
It will print the output as :
[data1, data2, data3, data1, data2, data3]

Lambda expressions and nested arrays

I'm trying to come up with some nice lambda expressions to build "desiredResult" from "customers" ArrayList. I implemented it in an old ugly way "for" loop. I know there should be nice one-liners, but I can't think of any method - nested arrays come into my way.
Iterable<List<?>> params;
Customer customer1 = new Customer("John", "Nowhere");
Customer customer2 = new Customer("Alma", "Somewhere");
Customer customer3 = new Customer("Nemo", "Here");
Collection<Customer> customers = new ArrayList<>();
customers.add(customer1);
customers.add(customer2);
customers.add(customer3);
Collection<List<?>> desiredResult = new ArrayList<>();
for (Customer customer : customers) {
List<Object> list = new ArrayList<>();
list.add(customer.getName());
list.add(customer.getAddress());
list.add("VIP");
desiredResult.add(list);
}
params = desiredResult;
I'd just use Arrays.asList for creating the inner lists, which makes the problem much simpler:
Collection<List<?>> desiredResult =
customers.stream()
.map(c -> Arrays.asList(c.getName(), c.getAddress(), "VIP"))
.collect(Collectors.toList());
If you absolutely must have an ArrayList, just wrap the Arrays.asList call with it.
Here is a suggestion:
Collection<List<?>> desiredResult = customers.stream()
.map(MyClass::customerToList)
.collect(toList());
I have extracted the list building into a separate method for better readability - the corresponding method would look like this:
private static List<Object> customerToList(Customer c) {
List<Object> list = new ArrayList<>();
list.add(c.getName());
list.add(c.getAddress());
list.add("VIP");
return list;
}

How can I convert ArrayList<Object> to ArrayList<String>?

ArrayList<Object> list = new ArrayList<Object>();
list.add(1);
list.add("Java");
list.add(3.14);
System.out.println(list.toString());
I tried:
ArrayList<String> list2 = (String)list;
But it gave me a compile error.
Since this is actually not a list of strings, the easiest way is to loop over it and convert each item into a new list of strings yourself:
List<String> strings = list.stream()
.map(object -> Objects.toString(object, null))
.toList();
Or when you're not on Java 16 yet:
List<String> strings = list.stream()
.map(object -> Objects.toString(object, null))
.collect(Collectors.toList());
Or when you're not on Java 8 yet:
List<String> strings = new ArrayList<>(list.size());
for (Object object : list) {
strings.add(Objects.toString(object, null));
}
Or when you're not on Java 7 yet:
List<String> strings = new ArrayList<String>(list.size());
for (Object object : list) {
strings.add(object != null ? object.toString() : null);
}
Note that you should be declaring against the interface (java.util.List in this case), not the implementation.
It's not safe to do that!
Imagine if you had:
ArrayList<Object> list = new ArrayList<Object>();
list.add(new Employee("Jonh"));
list.add(new Car("BMW","M3"));
list.add(new Chocolate("Twix"));
It wouldn't make sense to convert the list of those Objects to any type.
Using Java 8 you can do:
List<Object> list = ...;
List<String> strList = list.stream()
.map( Object::toString )
.collect( Collectors.toList() );
You can use wildcard to do this as following
ArrayList<String> strList = (ArrayList<String>)(ArrayList<?>)(list);
If you want to do it the dirty way, try this.
#SuppressWarnings("unchecked")
public ArrayList<String> convert(ArrayList<Object> a) {
return (ArrayList) a;
}
Advantage: here you save time by not iterating over all objects.
Disadvantage: may produce a hole in your foot.
Using guava:
List<String> stringList=Lists.transform(list,new Function<Object,String>(){
#Override
public String apply(Object arg0) {
if(arg0!=null)
return arg0.toString();
else
return "null";
}
});
Here is another alternative using Guava
List<Object> lst ...
List<String> ls = Lists.transform(lst, Functions.toStringFunction());
Your code ArrayList<String> list2 = (String)list; does not compile because list2 is not of type String. But that is not the only problem.
Using Java 8 lambda:
ArrayList<Object> obj = new ArrayList<>();
obj.add(1);
obj.add("Java");
obj.add(3.14);
ArrayList<String> list = new ArrayList<>();
obj.forEach((xx) -> list.add(String.valueOf(xx)));
With Java Generics Takes a list of X and returns a list of T that extends or implements X, Sweet!
// the cast is is actually checked via the method API
#SuppressWarnings("unchecked")
public static <T extends X, X> ArrayList<T> convertToClazz(ArrayList<X> from, Class<X> inClazz, Class<T> outClazz) {
ArrayList<T> to = new ArrayList<T>();
for (X data : from) {
to.add((T) data);
}
return to;
}
A simple solution:
List<Object> lst =listOfTypeObject;
ArrayList<String> aryLst = new ArrayList<String>();
for (int i = 0; i < lst.size(); i++) {
aryLst.add(lst.get(i).toString());
}
Note: this works when the list contains all the elements of datatype String.

Categories