How to create a list of duplicated list items? - java

I have a list of objects in this format :
class myObj {
private String identifier;
public myObj(String identifier){
this.identifier = identifier;
}
}
List<myObj> allobjects = new ArrayList<myObj>();
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("123"));
allobjects.add(new myObj("1234"));
allobjects.add(new myObj("12345"));
allobjects.add(new myObj("12"));
allobjects.add(new myObj("12"));
What is an elegant method of extracting the duplicate objects into seperate Lists ?
So in above example a new List is returned containing two lists. The first lists
contains :
new myObj("123");
new myObj("123");
new myObj("123");
new myObj("123");
The second list contains :
new myObj("12");
new myObj("12");
A possible solution is to create a new object :
List<List<myObj>> newList = new ArrayList<List<myObj>>
And then for each element in the list 'allobjects' iterate over each element and for each element that is contained more than once add it to the list. Then at the
end of the iteration for the current element add the newly created list to 'newList'
Is this acceptable or is there another solution ?

Add equals and hashCode methods to the myObj class, so that you can use them as Map keys:
class myObj {
private String identifier;
public myObj(String identifier){
this.identifier = identifier;
}
public int hashCode(){
return identifier.hashCode();
}
public boolean equals(Object o){
return identifier.equals(((myObj)o).identifier);
}
}
Then declare a Map:
Map<myObj, List<myObj>> map = new HashMap<myObj, List<MyObj>>()
and iterate through the original list. Use the myObjs as a map key, retrieving each time a list that corresponds to that myObj. If you encounter a certain myObj for the first time, don't forget to create the list:
for(myObj obj : allobjects){
List<myObj> list = map.get(obj);
if(list == null){
list = new ArrayList<myObj>();
map.put(obj, list);
}
list.add(obj);
}

Implement equals as required and then you can just use contains and iterate through checking other collection.
Here's a way to do it with jdk8s' lambdas.
TransformService transformService = (inputs1, inputs2) -> {
Collection<String> results = new ArrayList<>();
for (String str : inputs1) {
if (inputs2.contains(str)) {
results.add(str);
}
}
return results;
};
Collection<String> inputs1 = new ArrayList<String>(2) {{
add("lemon");
add("cheese");
add("orange");
}};
Collection<String> inputs2 = new
ArrayList<String>(2) {{
add("apple");
add("random");
add("cheese");
}};
Collection<String> results = transformService.transform(inputs1, inputs2);
for (String result : results) {
System.out.println(result);
}
}
public interface TransformService {
Collection<String> transform(Collection<String> inputs1, Collection<String> inputs2);
}

Related

Java Streams .How to Join and combine List of several Objects into one Object?

I have a requirement related to Streams in Java.
I need to iterate over a List of Objects,where each object has Integer property and a List property.
What I need is, if same objects has the same ID, I need to concat the lists.
Let me illustrate the example with a little bit of a simple code:
Here just defining 2 simple classes:
public static class Wrapper {
Integer id ;
List<Element> list;
public Wrapper(Integer id, List<Element> list) {
this.id = id;
this.list = list;
}
}
public static class Element {
String content ;
public Element(String content) {
this.content = content;
}
}
Now in a Main Java method,creating same objects for the porpuse of the example:
List<Wrapper> list=new ArrayList();
ArrayList<Element> listForWrapper1= new ArrayList();
listForWrapper1.add(new Element("Content A"));
listForWrapper1.add(new Element("Content B"));
ArrayList<Element> listForWrapper2= new ArrayList();
listForWrapper2.add(new Element("Content C"));
listForWrapper2.add(new Element("Content D"));
ArrayList<Element> listForWrapper3= new ArrayList();
listForWrapper3.add(new Element("Content E"));
listForWrapper3.add(new Element("Content F"));
Wrapper wrapper1=new Wrapper(1,listForWrapper1);
Wrapper wrapper2=new Wrapper(2,listForWrapper2);
//Here this Wrapper has the same ID than wrapper2
Wrapper wrapper3=new Wrapper(2,listForWrapper3);
//Adding Elements to List
list.add(wrapper1);
list.add(wrapper2);
list.add(wrapper3);
As you can see, I am adding 3 Wrappers to the list, BUT 2 of them have the same ID
What I want is when Wrapper IDs are the same in the array,just merge both list. So in this example the result should be:
A list with 2 Element:
Element 1 : Wrapper Object with ID 1,with 2 Elements inside its list property,Element Content A ,and Element Content B
Element 2: Wrapper Object with ID 2,with 4 Elements inside its list property,Element Content C,Element Content D,Element Content E and Element Content F.
How can I achieve this result using Streams? I cant think any elegant solution!
Thanks in advance!
List<Wrapper> combinedList=list.stream().....
You could use BinaryOperator<U> mergeFunction in Collectors.toMap`.
Collection<Wrapper> wrapperList = wrappers.stream()
.collect(Collectors.toMap(Wrapper::getId, x -> x),
(oldVal, newVal) -> {
oldVal.getElements().addAll(newVal.getElements());
return oldVal;
}))
.values();
In the above code I have written mergeFunction to always return oldVal (oldVal, newVal) -> oldVal but you can change the way you want. Lambda function x -> x can also be written as Function.identity().
You can use Collectors.toMap() to add the values of the map using a merge function.
Map<Integer, Wrapper> collect =
list.stream()
.collect(Collectors.toMap(w -> w.id,
w -> w,
(w1, w2) -> {
w1.list.addAll(w2.list);
return w1;
})
);
Working
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Wrapper> list=new ArrayList();
ArrayList<Element> listForWrapper1= new ArrayList();
listForWrapper1.add(new Element("Content A"));
listForWrapper1.add(new Element("Content B"));
ArrayList<Element> listForWrapper2= new ArrayList();
listForWrapper2.add(new Element("Content C"));
listForWrapper2.add(new Element("Content D"));
ArrayList<Element> listForWrapper3= new ArrayList();
listForWrapper3.add(new Element("Content E"));
listForWrapper3.add(new Element("Content F"));
Wrapper wrapper1=new Wrapper(1,listForWrapper1);
Wrapper wrapper2=new Wrapper(2,listForWrapper2);
//Here this Wrapper has the same ID than wrapper2
Wrapper wrapper3=new Wrapper(2,listForWrapper3);
//Adding Elements to List
list.add(wrapper1);
list.add(wrapper2);
list.add(wrapper3);
Map<Integer, Wrapper> collect =
list.stream()
.collect(Collectors.toMap(w -> w.id,
w -> w,
(w1, w2) -> {
w1.list.addAll(w2.list);
return w1;
})
);
System.out.println( collect.values() );
}
}
class Wrapper {
Integer id ;
List<Element> list;
public Wrapper(Integer id, List<Element> list) {
this.id = id;
this.list = list;
}
#Override
public String toString() {
return id + ":" + list;
}
}
class Element {
String content ;
public Element(String content) {
this.content = content;
}
#Override
public String toString() {
return content;
}
}
Output
[1:[Content A, Content B], 2:[Content C, Content D, Content E, Content F]]
You can try this:
Map<Integer, Wrapper> map = list.stream().collect(Collectors.toMap(wrapper -> wrapper.id /*Use the ids as the keys*/, wrapper -> wrapper /*Return the same wrapper as the value*/, (w1, w2) -> {
w1.list.addAll(w2.list); // If a wrapper with the same id is found, then merge the list of wrapper 2 to the list of wrapper 1 and return wrapper 1.
return w1;
}));
list = new ArrayList<>(map.values()); // Create new ArrayList with the values of the map.
System.out.println(list); // [cci.Test$Wrapper#4eec7777, cci.Test$Wrapper#3b07d329]

passing in two lambda functions

I want to pass two lambda expressions (or something similar, I'm still getting familiar with all the terminology) into a method; the first one will get a list of items, and the second one will retrieve one Integer object from (each one of) those items.
So I want to have a method something like this:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Integer>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get());
}
return idList;
}
and call it something like this:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
Item::getItemId);
So the first function is one called on an instance variable that the called method will have access to, and the second function is an instance method on any one of the objects returned as a list by the first function.
But the (eclipse) compiler doesn't like Item::getItemId, with or without parentheses at the end.
Do I just have a syntax thing wrong, or is there something else wrong with this idea?
Edit after many helpful comments -- thanks to you all!
I have one problem left. I've now got a method that I think does what I want, but I'm not sure how to pass the second expression to call it. Here is the method:
private List<Integer> getRecentList(List<Integer> salesCodeIdList,
Function<List<Integer>, List> listGetter,
Function<Object, Integer> idGetter
) {
List<Object> objectList = listGetter.apply(salesCodeIdList);
List<Integer> idList = new ArrayList<>();
for (Object o : objectList) {
idList.add(idGetter.apply((o)));
}
return idList;
}
AFAIK, I have to leave the second List raw in the getter spec, since different getters will return different types of objects in their lists.
But I still don't know how to invoke it -- I want to pass a method that gets an id from a particular instance of object, i.e., I want to pass a getter on the ID of one of the objects returned by the listGetter. That will be different types of objects on different calls. How would I invoke that?
To go back to examples, if I had a Supplier class with getSupplierId(), and a Vendor class with getVendorId(), and I cannot change those classes, can I pass in the correct method to invoke on the objects in the list depending on which getter retrieved the list?
A likely reason why you get the error is hidden inside the implementation of your method:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Supplier<Integer> idGetter)
{
List<Object> objectList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object o: objectList)
{
idList.add(idGetter.get()); // <<<<<==== Here
}
return idList;
}
Note how o from the for loop is not used in the call, indicating that idGetter would get you an ID out of thin air. That's of course is not true: you need to pass an item to idGetter, which means that the call should be
idList.add(idGetter.apply(o));
(from the comment) I can't cast within the method, I don't know what type of object to cast to there.
which in turn means that idGetter should be Function<Object,Integer>:
for (Object o: objectList)
{
idList.add(idGetter.apply(o));
}
Since you would like to reuse the same function for lists of different types, the call would have to use a lambda that performs the cast at the caller, i.e. at the point where you know the type:
List<Integer> idList = setRecentList(searchParams,
(a) -> serviceOne.getItemList(a),
(o) -> ((Item)o).getItemId());
Item::getItemId is not a Supplier<Integer>. A Supplier<Integer> is a lambda function that takes no arguments and returns an Integer. But Item::getItemId would require an Item to get the ID from.
Here is probably what you want. Note I've changed your method signature to match the idea that you're getting IDs from items.
class Item {
int id;
Item (int i) {
id = i;
}
int getItemId() {
return id;
}
}
private static List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Item>> listGetter,
Function<Item, Integer> idGetter)
{
List<Item> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Item item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Edit: If you want this to handle multiple kinds of Items, and they all have IDs, you can do something like the following. First, define an interface to represent the fact that your different kinds of Items all have IDs:
interface ItemWithId {
abstract int getItemId();
}
class ItemA implements ItemWithId {
int id;
ItemA(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
class ItemB implements ItemWithId {
int id;
ItemB(int i) {
id = i;
}
public int getItemId() {
return id;
}
}
Then, your method should use ItemWithId instead of Item:
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<ItemWithId>> listGetter,
Function<ItemWithId, Integer> idGetter)
{
List<ItemWithId> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (ItemWithId item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
Finally, you can call this by casting an ItemA or ItemB to an ItemWithId:
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemWithId) item).getItemId());
Another edit: Ok, so you can't change ItemA or ItemB. You can still make it work:
class ItemA {
int id;
ItemA(int i) {
id = i;
}
public int getItemIdA() {
return id;
}
}
class ItemB {
int id;
ItemB(int i) {
id = i;
}
public int getItemIdB() {
return id;
}
}
private List<Integer> getRecentList(List<Integer> searchParams,
Function<List<Integer>, List<Object>> listGetter,
Function<Object, Integer> idGetter)
{
List<Object> itemList = listGetter.apply(searchParams);
List<Integer> idList = new ArrayList<>();
for (Object item: itemList)
{
idList.add(idGetter.apply(item));
}
return idList;
}
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemA) item).getItemIdA());
// or
List<Integer> idList = getRecentList(searchParams,
(a) -> getItemList(a),
(item) -> ((ItemB) item).getItemIdB());

Java Filtering in collections

I have a list of objects as below,
Emp e1 = new Emp(10,"Anitha",1000,"AE");
Emp e2 = new Emp(20,"Chaitanya",2000,"SE");
Emp e3 = new Emp(30,"Chaitanya",3000,"SE");
Emp e4 = new Emp(40,"Deepthi",2100,"AE");
Emp e5 = new Emp(50,"Deepthi",2200,"CE");
Emp e6 = new Emp(60,"Deepthi",2300,"BE");
Emp e7 = new Emp(70,"Anitha",2300,"BE");
Emp e8 = new Emp(80,"Anitha",2400,"ME");
Emp e9 = new Emp(90,"Sita",2200,"CE");
Emp e10 = new Emp(100,"Hari",2200,"CE");
Emp e11 = new Emp(110,"Krishna",2200,"CE");
I would like to filter the values on unique name and also filter on same name like
1.on unique name : output should be
(50,"Deepthi",2200,"CE")
(100,"Hari",2200,"CE")
(110,"Krishna",2200,"CE")
and sharing the same name :
like output
(10,"Anitha",1000,"AE")
(70,"Anitha",2300,"BE")
(80,"Anitha",2400,"ME")
(20,"Chaitanya",2000,"SE");
(30,"Chaitanya",3000,"SE");
(40,"Deepthi",2100,"AE");
(50,"Deepthi",2200,"CE");
(60,"Deepthi",2300,"BE");
using collections....
Will some one be able to help me?
Thanks in Advance.
Nithya
If you're using java 8 please skip to the end!
I'd probably create a map to do this, but it seems like you're new to Java so I'll describe the more basic way.
You should first create a list(arraylist) like so:
// create an arraylist (list based on an array)
List<Emp> emps = new ArrayList<Emp>();
Then you can add objects to the list:
emps.add(new Emp(10,"Anitha",1000,"AE"));
emps.add(new Emp(20,"Chaitanya",2000,"SE"));
.
.
Now you can start filtering!
So, suppose you have a getName() method in class Emp, you can write a function such as this:
// this function takes a list and a name, and filters the list by the name
public static List<Emp> filterEmpsByName(List<Emp> emps, String name){
// a place to store the result
List<Emp> result = new ArrayList<Emp>();
// iterate over the list we got
for (Emp emp: emps){
// save only the elements we want
if (emp.getName().equals(name)){
result.add(emp);
}
}
return result;
}
Now, filtering would be a simple matter of calling that function:
// print to standard output the result of our function on the "main" list `emp` with name "Anitha"
for (Emp emp : filterEmpsByName(emps, "Anitha")){
System.out.println(emp.toString()); // make sure toString() is overridden in Emp class
}
Now for the second part which is a bit more tricky:
// this function takes a list and a name, and filters the list by the name
public static List<Emp> getDistinctlyNamedEmps(List<Emp> emps, String name) {
// this time we use a map which is A LOT faster for this kind of operation
Map<String, Emp> result = new HashMap<String, Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (result.get(emp.getName()) == null ) {
result.put(emp.getName(), emp);
}
}
// convert map to list - not mandatory if you can use the map as is...
return new ArrayList<Emp>(result.values());
}
Notice that you can also write a comparator that compares objects using the name/any other property, but that's beyond the scope of this comment :-)
Putting the whole thing together:
Main class:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// create an [arraylist][4] (list based on an array)
List<Emp> emps = new ArrayList<Emp>();
emps.add(new Emp(10, "Anitha", 1000, "AE"));
emps.add(new Emp(20, "Chaitanya", 2000, "SE"));
// print to standard output the result of our function on the "main"
// list `emp` with name "Anitha"
System.out.println("filterEmpsByName(emps, \"Anitha\") output:");
for (Emp emp : filterEmpsByName(emps, "Anitha")) {
System.out.println(emp.toString()); // make sure toString() is
// overridden in Emp class
}
// print to standard output the result of our second function on the "main"
// list `emp`
System.out.println("getDistinctlyNamedEmps(emps) output:");
for (Emp emp : getDistinctlyNamedEmps(emps)) {
System.out.println(emp.toString()); // make sure toString() is
// overridden in Emp class
}
}
// this function takes a list and a name, and filters the list by the name
public static List<Emp> filterEmpsByName(List<Emp> emps, String name) {
// a place to store the result
List<Emp> result = new ArrayList<Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (emp.getName().equals(name)) {
result.add(emp);
}
}
return result;
}
// this function takes a list and a name, and filters the list by the name
public static List<Emp> getDistinctlyNamedEmps(List<Emp> emps) {
// this time we use a map which is A LOT faster for this kind of
// operation
Map<String, Emp> result = new HashMap<String, Emp>();
// iterate over the list we got
for (Emp emp : emps) {
// save only the elements we want
if (result.get(emp.getName()) == null) {
result.put(emp.getName(), emp);
}
}
// convert map to list - not necessary
return new ArrayList<Emp>(result.values());
}
}
And partial Emp class:
public class Emp {
private String name;
public Emp(int stubi, String name, int j, String stubs) {
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
return "[" + this.name + "]";
}
}
Java 8:
Java 8 has lambda expressions (anonymous functions), which are neat tools used in many other languages for filtering as well as other operations.
You can read more about using them here.
As far as I can see, the answers until now assumed that the task was to search for a particular name, or to find elements with unique names - and I think this was not what was asked for.
In order to filter a list in the way that is described in the original question, one could create a map from the "key" (that is, the 'name' in this case) to a list of elements that share this key. Using this map, one can easily find
One element for each key that occurs only once in all elements
The list of all elements that have a key that occurs at least twice in all elements
These tasks are rather similar, and further generalizations may be possible, but here is one way of how this may be implemented:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class CriterionFilter
{
public static void main(String[] args)
{
List<Emp> list = new ArrayList<Emp>();
list.add(new Emp(10,"Anitha",1000,"AE"));
list.add(new Emp(20,"Chaitanya",2000,"SE"));
list.add(new Emp(30,"Chaitanya",3000,"SE"));
list.add(new Emp(40,"Deepthi",2100,"AE"));
list.add(new Emp(50,"Deepthi",2200,"CE"));
list.add(new Emp(60,"Deepthi",2300,"BE"));
list.add(new Emp(70,"Anitha",2300,"BE"));
list.add(new Emp(80,"Anitha",2400,"ME"));
list.add(new Emp(90,"Sita",2200,"CE"));
list.add(new Emp(100,"Hari",2200,"CE"));
list.add(new Emp(110,"Krishna",2200,"CE"));
Function<Emp, String> keyFunction = new Function<Emp, String>()
{
#Override
public String apply(Emp s)
{
return s.getName();
}
};
List<Emp> fiteredOnUnique = filterOnUnique(list, keyFunction);
System.out.println("Filtered on unique:");
print(fiteredOnUnique);
List<Emp> filteredOnSame = filterOnSame(list, keyFunction);
System.out.println("Filtered on same:");
print(filteredOnSame);
}
private static void print(Iterable<?> elements)
{
for (Object element : elements)
{
System.out.println(element);
}
}
/**
* Create a map that maps the keys that are provided for the given
* elements to the list of elements that have this key
*
* #param elements The input elements
* #param keyFunction The key function
* #return The map
*/
private static <T, K> Map<K, List<T>> map(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
Map<K, List<T>> map = new HashMap<K, List<T>>();
for (T t : elements)
{
K key = keyFunction.apply(t);
List<T> list = map.get(key);
if (list == null)
{
list = new ArrayList<T>();
map.put(key, list);
}
list.add(t);
}
return map;
}
/**
* Uses the given key function to compute the keys associated with the
* given elements, and returns a list containing the element of
* the given sequence that have unique keys
*
* #param elements The input elements
* #param keyFunction The key function
* #return The filtered list
*/
private static <T, K> List<T> filterOnUnique(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
List<T> result = new ArrayList<T>();
Map<K, List<T>> map = map(elements, keyFunction);
for (Entry<K, List<T>> entry : map.entrySet())
{
List<T> list = entry.getValue();
if (list.size() == 1)
{
result.add(list.get(0));
}
}
return result;
}
/**
* Uses the given key function to compute the keys associated with the
* given elements, and returns a list containing all elements of
* the given sequence that have a key that occurs multiple times.
*
* #param elements The input elements
* #param keyFunction The key function
* #return The filtered list
*/
private static <T, K> List<T> filterOnSame(
Iterable<? extends T> elements, Function<? super T, K> keyFunction)
{
List<T> result = new ArrayList<T>();
Map<K, List<T>> map = map(elements, keyFunction);
for (Entry<K, List<T>> entry : map.entrySet())
{
List<T> list = entry.getValue();
if (list.size() > 1)
{
result.addAll(list);
}
}
return result;
}
/**
* Interface for a generic function
*/
static interface Function<S, T>
{
T apply(S s);
}
}
class Emp
{
private int i;
private String name;
private int j;
private String whatever;
public Emp(int i, String name, int j, String whatever)
{
this.i = i;
this.name = name;
this.j = j;
this.whatever = whatever;
}
#Override
public String toString()
{
return "Emp [i=" + i + ", name=" + name + ", j=" + j + ", whatever=" + whatever + "]";
}
String getName()
{
return name;
}
}
(EDIT: Adjusted the first case, see comment in original question)
Thats not a collections, while it is a 'list' in the traditional sense, it is not a java.util.ArrayList.
To do this, you might try:
ArrayList<Emp> employeeList = new ArrayList<Emp>();
employeeList.add(new Emp(10,"Anitha",1000,"AE"));
// repeat

split an array list into multiple lists in java

I have an array list which when populated has a key and a value I want to know if there is a way of splitting it on repeating keys for example my current data is like this:
[RoleID_123.0, UserHandel_tom, Password_12345.0, prevPassword_null, userCaption_thomas, Email_tom#tom.tom, RoleID_124.0, UserHandel_dave, Password_ghadf, prevPassword_sdfsd, userCaption_david, Email_dave#dave.dave, RoleID_125.0, UserHandel_trevor, Password_tre, prevPassword_null, userCaption_trev, Email_trev#trev.trev]
I want it to come out more like this:
[RoleID_123.0, UserHandel_tom, Password_12345.0, prevPassword_null, userCaption_thomas, Email_tom#tom.tom]
[RoleID_124.0, UserHandel_dave, Password_ghadf, prevPassword_sdfsd, userCaption_david, Email_dave#dave.dave]
[RoleID_125.0, UserHandel_trevor, Password_tre, prevPassword_null, userCaption_trev, Email_trev#trev.trev]
Is there a way to split it on say role id or am I going about this the wrong way?
You can try by using HashMap
private static class MyItemHashMap extends HashMap {
public Item add(Item item) {
get(item).add(item);
return item;
}
public List get(Item key) {
List list = (List) get(createItemKey((Item) key));
return list == null ? createItemEntry((Item) key) : list;
}
private List createItemEntry(Item item) {
List list = new ArrayList();
put(createItemKey(item), list);
return list;
}
private Object createItemKey(Item item) {
return item.getSplitterProperty();
}
}
public static void main(String[] args) {
MyItemHashMap itemMapped = new MyItemHashMap();
List items = Arrays.asList(new Object[]{new Item("A"), new Item("B"),
new Item("C")});
for (Iterator iter = items.iterator(); iter.hasNext();) {
Item item = (Item) iter.next();
itemMapped.add(item);
}
}
If it is an ArrayList, there is no built-in function to split data like this; you will have to do it manually. If you know the number of consecutive fields that make a single structure, this shouldn't be too hard; something like this:
// 6 because there are 6 fields
for (int i = 0; i < arrayList.size(); i = i + 6) {
List thisList = arrayList.subList(i, i + 5);
// ... Now do whatever you want with thisList - it contains one structure.
}
If the number of fields can change then you'll have to do something a little more dynamic and loop through looking for a RoleID field, for example.
I'd use a HashMap to seperate the data instead of one long ArrayList ( you shouldn't have stored the data like this in the first instance )
HashMap<String,ArrayList<String>> hm = new HashMap<String,ArrayList<String>>;
// For each list:
ArrayList<String> arr = new ArrayList<String>;
arr.add("each element");
hm.put("RoleID_123.0", arr);
This way you will end up with a three dimensional structure with a key ( "RoleID..." ) pointing to its child elements.
Try this
String[] str=new String[]{"RoleID_123.0", "UserHandel_tom", "Password_12345.0", "prevPassword_null", "userCaption_thomas", "Email_tom#tom.tom", "RoleID_124.0", "UserHandel_dave", "Password_ghadf", "prevPassword_sdfsd", "userCaption_david", "Email_dave#dave.dave", "RoleID_125.0", "UserHandel_trevor", "Password_tre", "prevPassword_null", "userCaption_trev", "Email_trev#trev.trev"};
List<String> list=new ArrayList<>(Arrays.asList(str));
List<String> subList=list.subList(0,5);
You can try something similar to this
If you feel like taking a Linq-ee Libraried approach, this is about as good as it gets, and it requires use of a couple delegate objects:
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;
//...
final List<String> yourList = //...
final int RECORD_LENGTH = 6;
Collection<String> roleIdValues = filter(yourList, new Predicate<String>() {
public boolean apply(#Nullable String input) {
return input != null && input.startsWith("RoleID");
}
});
Collection<Collection<String>> splitRecords = transform(roleIdValues, new Function<String, Collection<String>>() {
#Nullable public Collection<String> apply(#Nullable String input) {
return yourList.subList(yourList.indexOf(input), RECORD_LENGTH);
}
});
If Oracle had delivered Java 8 on time you would be able to do this in a way more slick manor. Ironically the reason you cant was provided by the same people providing the guava library

Join two Lists in Java

I need to join two Lists in Java. I've a list which has a VO that has a name and description. I've another list which has same VO type that has name and Address. The "name" is same. I need to create a List with this VO which has both name, address and description.
The VO structure is
public class PersonDetails{
private String name;
private String description;
private String address;
//getters and setters
}
Can someone please suggest me the best way to implement it?
It depends:
If the lists contain both exactly the same data, you can sort them both by name, iterate over them en set the missing property.
If not, I would put the first list in a Map, with the name as key. Then iterate over the second list, look in the map for the VO and set the value.
After that, just get all the value's out of the map again as a List.
public List<Vo> merge(List<Vo> list1, List<Vo> list2) {
Map<String, Vo> tempMap = new HashMap<String, Vo>();
for (Vo v : list1) {
tempMap.put(v.name, v);
}
for (Vo vv : list2) {
//The if is in case the 2 lists aren't filled with the same objects
if (tempMap.containsKey(vv.name)) {
tempMap.get(vv.name).description = vv.description;
} else {
tempMap.put(vv.name, vv);
}
}
return new ArrayList<Vo>(tempMap.values());
}
If the lists contain both EXACT the same VO (equal by name), you can use this.
public List<Vo> merge(List<Vo> list1, List<Vo> list2) {
Collections.sort(list1, new Comparator<Vo>() {
public int compare(Vo o1, Vo o2) {
return o1.name.compareTo(o2.name);
}
});
Collections.sort(list2, new Comparator<Vo>() {
public int compare(Vo o1, Vo o2) {
return o1.name.compareTo(o2.name);
}
});
for(int i = 0; i < list1.size(); i++){
list1.get(i).description = list2.get(i).description;
}
return list1;
}
Put all elements of the first list in a map, then merge the contents of the second list into it:
final List<PersonDetails> listWithAddress =
new ArrayList<PersonDetails>();
final List<PersonDetails> listWithDescription =
new ArrayList<PersonDetails>();
// fill both lists with data
final Map<String, PersonDetails> map =
// map sorted by name, change to HashMap otherwise
// (or to LinkHashMap if you need to preserve the order)
new TreeMap<String, PersonDetails>();
for(final PersonDetails detailsWithAddress : listWithAddress){
map.put(detailsWithAddress.getName(), detailsWithAddress);
}
for(final PersonDetails detailsWithDescription : listWithDescription){
final PersonDetails retrieved =
map.get(detailsWithDescription.getName());
if(retrieved == null){
map.put(detailsWithDescription.getName(),
detailsWithDescription);
} else{
retrieved.setDescription(detailsWithDescription.getDescription());
}
}
I would put each list of the source VO lists in a map by name, create a set of both keys, iterate it and add a target VO to the result list.
In the example code, VO is the target VO, VOD is the source VO with description only, VOA is the source VO with address only:
List<VOD> descriptions = ...;
List<VOA> addresses = ...;
Map<String,String> description ByName = new HashMap<String,String>();
for (VOD description : descriptions) {
descriptionByName.put(description.name, description.description);
}
Map<String,String> addressByName = new HashMap<String,String>();
for (VOA address: addresses ) {
addressByName.put(address.name, address.address);
}
Set<String> allNames = new HashSet<String>();
allNames.addAll(descriptionByName.keySet());
allNames.addAll(addressByName.keySet());
List<VO> result = new ArrayList<VO>();
for (String name : allNames) {
VO one = new VO();
one.name = name;
one.address = addressByName.get(name)
one.description = descriptionByName.get(name)
result.add(one);
}
Go from list to HashMap, use the name string as key. So you can merge you data quite efficient.
List<VO> list = new ArrayList<VO>();
for (VO vo1 : vo1List) {
for (VO vo2 : vo2List) {
if (vo1.getName().equals(vo2.getName())) {
VO newVo = new VO();
newVO.setName(vo1.getName());
newVO.setDescription(vo1.getDescription());
newVO.setAddress(vo2.getAddress);
list.add(newVO);
break;
}
}
}
It's best that you sort both lists on name beforehand, it makes the double iteration faster.

Categories

Resources