Java Filtering in collections - java

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

Related

Java 8: How to stream a list into a list of lists?

Given a list of objects that need to be sorted and grouped:
static class Widget {
// ...
public String getCode() { return widgetCode; }
public String getName() { return widgetName; }
}
List<Widget> widgetList = Arrays.asList(
// several widgets with codes and names
);
I want to group the list into a list-of-lists, grouped by widgetCode, with the elements of each sub-list in the order they were encountered in the original list. I know that I can group them into a Map of lists using the groupingBy Collector:
Map<String,List<Widget>> widgetMap = widgetList.stream()
.collect(groupingBy(Widget::getCode));
I do not take for granted that the keys are sorted, so I've taken the extra step of loading the whole thing into a SortedMap type:
SortedMap<String,List<Widget>> sortedWidgetMap = new TreeMap<String,List<Widget>>(
widgetList.stream()
.collect(groupingBy(Widget::getCode))
);
I know I can get a Collection from sortedWidgetMap by using .values(), and I guess it is an ordered collection because it comes from an ordered map type, so that's my current solution:
Collection<List<Widget>> widgetListList = new TreeMap<String,List<Widget>>(
widgetList.stream()
.collect(groupingBy(Widget::getCode))
).values();
widgetListList.forEach(System.out::println); // do something with the data
This works so far, but I'm not confident that the resulting widgetListList is actually guaranteed to be in the right order (i.e. by widgetCode) or that the sub-lists will be built in the order they were found in the original list. Also, I think it must be possible to use the Stream API alone to achieve the output I want. So, how can I do this better?
As mentioned in a comment, referring to a question that is very similar (in fact, I nearly considered it to be a duplicate...), the groupBy call comes in different flavors, and one of them allows passing in a factory for the map that is to be created.
So there is no need to explicitly wrap the result of the "simple" groupBy call into the creation of a new TreeMap, because you can create the TreeMap directly. This map (and its values() collection!) will be ordered by the key. The values of the map are lists, which are created using the downstream collector toList(), which explicitly says that it will collect the results in encounter order.
So the following should indeed be a simple, correct and efficient solution:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class CollectToListOfList {
static class Widget {
String code;
String name;
Widget(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
#Override
public String toString() {
return code + ": " + name;
}
}
public static void main(String[] args) {
List<Widget> widgetList = Arrays.asList(
new Widget("0", "A"),
new Widget("1", "B"),
new Widget("2", "C"),
new Widget("3", "D"),
new Widget("0", "E"),
new Widget("1", "F"),
new Widget("2", "G"),
new Widget("3", "H"),
new Widget("0", "I"),
new Widget("1", "J"));
Collection<List<Widget>> result = widgetList.stream()
.collect(Collectors.groupingBy(Widget::getCode, TreeMap::new, Collectors.toList()))
.values();
for (List<Widget> list : result) {
System.out.println(list);
}
}
}
Edited to correct previous post based on clarification. Only difference between answer(s) by others is that the values() result is fed into an ArrayList constructor to create a List of Lists.
// Create some data
Random r = new Random(29);
String codes = "ABCD";
List<Widget> widgetList = r.ints(10, 0, 4).mapToObj(
n -> new Widget(codes.substring(n, n + 1), "N" + i++)).collect(
Collectors.toList());
// Now create the list of lists.
List<List<Widget>> listofWidgets = new ArrayList<>(
widgetList.stream().collect(Collectors.groupingBy(Widget::getCode,
TreeMap::new,
Collectors.toList())).values());
// Display them
for (List<?> list : listofWidgets) {
System.out.println(list);
}
Widget class
class Widget {
String widgetCode;
String widgetName;
public Widget(String wc, String wn) {
widgetCode = wc;
widgetName = wn;
}
public String getCode() {
return widgetCode;
}
public String getName() {
return widgetName;
}
public String toString() {
return "{" + widgetCode + ":" + widgetName + "}";
}
}

Java iterating through a mapper method using lambdas?

I have a mapper method which accepts a lambda function from the object calling it.
LambdaList<String> strings = new LambdaList<String>("May",
"the", "force", "be", "with", "you");
list = strings.map(x -> x.length());
assert list.equals(new LambdaList<Integer>(3, 3, 5, 2, 4, 3))
: "map() failed. Result is " + list + ".";
The map method will then iterate though the list and return a new list once the lambda has been applied.
/**
* Returns a list consisting of the results of applying the given function
* to the elements of this list.
* #param <R> The type of elements returned.
* #param mapper The function to apply to each element.
* #return The new list.
*/
public <R> LambdaList<R> map(Function<? super T, ? extends R> mapper) {
ArrayList<R> newList = new ArrayList<>();
for(T item: this){
newList.add(mapper.apply(item));
}
return new LambdaList<R>(newList);
}
The lambda list is set up as below:
class LambdaList<T> {
private ArrayList<T> list;
public LambdaList() {
list = new ArrayList<T>();
}
#SafeVarargs
public LambdaList(T... varargs) {
list = new ArrayList<T>();
for (T e: varargs) {
list.add(e);
}
}
private LambdaList(ArrayList<T> list) {
this.list = list;
}
public LambdaList<T> add(T e) {
ArrayList<T> newList = new ArrayList<>();
newList.addAll(list);
newList.add(e);
return new LambdaList<T>(newList);
}
I tried using T item: this to refer to the object itself but that doesn't work. How would i go about implementing this method?
To be able to write an enhanced for loop, the object needs to be iterable.
class LambdaList<T> implements Iterable<T> { ... }
You will then need to implement a public Iterator<T> iterator() method, which will probably look like return internalList.iterator();.

How to give Rank in list of Object and sort on the basis of Rank and any other key?

I have a situation where I need to make a list of object , where Object have (Name, Title) element. for e.g. :
obj1 = ('john', 'colonel')
obj2 = ('Alex', 'major')
obj3 = ('Roy', 'Major general')
obj4 = ('derrick', 'no Rank')
I need to do sorting in two ways:
1. First on the Title basis.
2. For any two names, if the title is same then sorting of object on the
alphabetical name basis(like chronological order of name).
3. And also need to remove duplicate names from the list.
Please help me out as I know how to sort the arraylist but don't know how to give ranking and sort on multiple conditions. If you need further details or do not understand my question then please let me know.
You need to create a Comparator Class, something like this:
class ObjectComparator implements Comparator<SomeClass>{
#Override
public int compare(SomeClass obj1, SomeClass obj2) {
// if two objects' ranks are the same
if(obj1.rank.equals(obj2.rank)){
// then compare their names
return obj1.name.compareTo(obj2.name);
}
else{
return obj1.rank.compareTo(obj2.rank);
}
}
}
I cannot understand how you want to remove the duplicates of the attribute name because that will ruin the sorting, unless you mean to remove the duplicates of the same name and rank (e.g. obj1 = "john" "colonel" and also obj2 ="john" "colonel") then in this case straightforwardly use a Set after overriding the equal and hashCode methods in your class, so your set can know if the two objects are equal...something like this:
#Override
#Override
public boolean equals(Object other){
if(other==null) return false;
if (other == this) return true;
return ((SomeClass)other).name.equalsIgnoreCase(this.name)
&& ((SomeClass)other).rank.equalsIgnoreCase(this.rank);
}
#Override
public int hashCode() {
return Objects.hash(name, rank);
}
But if you mean that you want to remove the duplicates names (i.e. objects have same fields but different ranks), then you may consider using a LinkedHashMap which preserves the order of items, but as I mentioned, this will Not preserve the sorting order for the particular duplicate (TBH I don't think that what you meant by duplicates)... an example for testing:
public class SomeClass{
String name, rank;
public SomeClass(String name, String rank){
this.name = name;
this.rank = rank;
}
public static void main(String[] args) {
SomeClass obj1 = new SomeClass("john", "colonel");
SomeClass obj2 = new SomeClass("XXX", "major");
SomeClass obj3 = new SomeClass("Roy", "general");
SomeClass obj4 = new SomeClass("derrick", "no Rank");
SomeClass obj5 = new SomeClass("john", "something");
SomeClass obj6 = new SomeClass("Alex", "major");
List<SomeClass> list = new ArrayList<>();
list.add(obj1); list.add(obj2); list.add(obj3);
list.add(obj4); list.add(obj5); list.add(obj6);
System.out.println(getSortedObjects(list));
}
public static Map<String, List<String>> getSortedObjects(List<SomeClass> list){
Map<String, List<String>> sortedMap = new LinkedHashMap<>();
// sort the list
Collections.sort(list, new ObjectComparator());
for(SomeClass obj : list){
// for testing sorting before ruining it in a map
System.out.println(obj.name + " " + obj.rank);
List<String> temp;
if(sortedMap.containsKey(obj.name)){
temp = sortedMap.get(obj.name);
temp.add(obj.rank);
sortedMap.put(obj.name, temp);
}
else{
temp = new ArrayList<>();
temp.add(obj.rank);
sortedMap.put(obj.name, temp);
}
}
return sortedMap;
}
}
Test
john colonel
Roy general
Alex major
XXX major
derrick no Rank
john something
{john=[colonel, something], Roy=[general], Alex=[major], XXX=[major], derrick=[no Rank]}
Complete Implementation Example of Set & Comparator:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
public class SomeClass{
// suppose the fields in your class
String name, rank;
// simple constructor
public SomeClass(String name, String rank){
this.name = name;
this.rank = rank;
}
// you need to override both equals and hashCode methods from
// Object superclass in order the set works properly and realizes
// that two given objects of type SomeClass are equal
#Override
public boolean equals(Object other){
if(other==null) return false;
if (other == this) return true;
return ((SomeClass)other).name.equalsIgnoreCase(this.name)
&& ((SomeClass)other).rank.equalsIgnoreCase(this.rank);
}
#Override
public int hashCode() {
return Objects.hash(name, rank);
}
#Override
public String toString(){
return name + " " + rank;
}
/**
* Pass a set then add the items to a temporary ArrayList
* to be sorted according to the Comparator condition
* then clear the set and add the sorted element back
* #param list
*/
public static void sort(Set<SomeClass> list){
List<SomeClass> temp = new ArrayList<>();
temp.addAll(list);
// anonymous class implementation of Comparator
Collections.sort(temp, new Comparator<SomeClass>(){
#Override
public int compare(SomeClass obj1, SomeClass obj2) {
// if two objects' ranks are the same
if(obj1.rank.equals(obj2.rank)){
// then compare their names
return obj1.name.compareTo(obj2.name);
}
else{
return obj1.rank.compareTo(obj2.rank);
}
}});
list.clear();
list.addAll(temp);
}
// testing
public static void main(String[] args) {
// create Objects of the class with duplicate
SomeClass obj1 = new SomeClass("john", "colonel");
SomeClass obj2 = new SomeClass("XXX", "major");
SomeClass obj3 = new SomeClass("Roy", "general");
SomeClass obj4 = new SomeClass("derrick", "no Rank");
SomeClass obj5 = new SomeClass("john", "something");
SomeClass obj6 = new SomeClass("Alex", "major");
SomeClass obj7 = new SomeClass("Alex", "major"); // duplicate object
// LinkedHashSet PRESERVES the order of elements' insertion
// in addition to removing duplicates
Set<SomeClass> list = new LinkedHashSet<>();
// populate the set
list.add(obj1); list.add(obj2); list.add(obj3);
list.add(obj4); list.add(obj5); list.add(obj6);
list.add(obj7);
//before sorting
System.out.println("Before " + list);
// after sorting
sort(list);
System.out.println("After " + list);
}
}
Test:
Before [john colonel, XXX major, Roy general, derrick no Rank, john something, Alex major]
After [john colonel, Roy general, Alex major, XXX major, derrick no Rank, john something]
You need to read up on Comparators, and implement one for your class.
If you are using Java 8, the easiest way is to use Comparators.comparing
https://praveer09.github.io/technology/2016/06/21/writing-comparators-the-java8-way/
In order to prevent duplicate names, I would keep a separate Set (which automatically constrains uniqueness) of names that already exist. Check to see if that set contains the names before you add the new object to the arraylist.

In java, is there a way to search for a specific field of a class stored in a linked list?

I wrote a class that is to be stored in a linkedlist, with 3 fields in the class. One of these fields is a String, which I would like to search for in the linked list.
Example
LinkedList
Obj1
String name = "first";
int age = 2;
int size = 4;
Obj2
String name = "second";
int age = 3;
int size = 6;
Obj3
String name = "third";
int age = 5;
int size = 8;
If this is the linkedlist storing these three objects with the given fields, is there a way to search the linked list for the object with the name "second"?
You can search for an item in the list by iteration
// Iterate over each object within the list
for(YourClass obj : yourLinkedList) {
// Check if the object's name matches the criteria, in this case, the name
// of the object has to match "second"
if (obj.name.equals("second")) {
// If we are within this block, it means that we found the object that has
// its name set as "second".
return obj;
}
}
You could also make a method to make things more elegant
public YourClass findByName(String name) {
for(YourClass obj : yourLinkedList) {
if (obj.name.equals(name)) {
return obj;
}
}
return null;
}
And use it the following way
YourClass object = findByName("second");
The easiest way to do this would be to of course, iterate through each element in the collection, checking if it matched your filter condition, and selecting the matches found. However this gets tedious the more times you need to do it, and the more complex your filter condition is. I would recommend utilizing pre-existing libraries to get the task done efficiently. Here is an example using Google-Collections:
final List<SomeObj> listObjs = Arrays.asList(
new SomeObj("first", 2, 4), new SomeObj("second", 3, 6),
new SomeObj("third", 5, 8));
final Iterable<SomeObj> filtered = Iterables.filter(listObjs,
new Predicate<SomeObj>() {
#Override
public boolean apply(final SomeObj obj) {
return "second".equals(obj.getName());
}
});
for (final SomeObj obj : filtered) {
System.out.println(obj);
}
The code shown would select all objects in the list with a name property of "second". Obviously, the predicate doesn't have to be an anonymous inner class - if you needed to reuse it you would just break it out to a standalone class.
Here's another way to implement a Comparator (just in case it helps).
I find it's easier to understand if you implement the Comparator explicitly:
class PersonAgeComparator implements Comparator<Person> {
#Override
public int compare(Person p1, Person person2) {
return p1.getAge().compareTo(p2.getAge());
}
}
You might use the above like this:
Comparator ageComparator = new PersonAgeComparator();
List<Person> personList = // populate list somehow
Person fourYearOld = new Person();
fourYearOld.setAge(4);
for (Person p : personList) {
if (ageComparator.compare(fourYearOld, p) == 0) {
System.out.println(p.getName() + " is 4 years old");
}
}
This doesn't make much sense for this simple example.
It would be ideal if you had several complicated ways to compare people (by height, by adjusted income, by how many states they've lived in, etc...).
Take a look at the java.util.Comprator interface. You can write a method that iterates over a List and uses a comparator to find the one you are after.
Something like (not compiled):
for(final T value : list)
{
if(comparator.compare(value, desired) == 0)
{
// match
}
}
In your comparator you have it perform whatever comparison you want.
Here is a working example:
public class JavaApplication4
{
public static void main(String[] args)
{
final List<Data> list;
final List<Data> a;
final List<Data> b;
list = new ArrayList<Data>();
list.add(new Data("Foo", 1));
list.add(new Data("Bar", 10));
list.add(new Data("Car", 10));
a = find(list,
new Data("Bar", 0),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.name.compareTo(o2.name));
}
});
b = find(list,
new Data(null, 10),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.count - o2.count);
}
});
System.out.println(a.size());
System.out.println(b.size());
}
private static List<Data> find(final List<Data> list,
final Data desired,
final Comparator<Data> comprator)
{
final List<Data> results;
results = new ArrayList(list.size());
for(final Data data : list)
{
if(comprator.compare(desired, data) == 0)
{
results.add(data);
}
}
return (results);
}
private static class Data
{
private final String name;
private final int count;
Data(final String nm,
final int c)
{
name = nm;
count = c;
}
}
}
And here is a generic version of the find method. Using this method you would never have to write the find method again, using a method that embeds the logic for matching in the iteration code means that you would have to re-write the iteration logic for each new set of matching logic.
private static <T> List<T> find(final List<T> list,
final T desired,
final Comparator<T> comprator)
{
final List<T> results;
results = new ArrayList(list.size());
for(final T value : list)
{
if(comprator.compare(desired, value) == 0)
{
results.add(value);
}
}
return (results);
}
You can go through it and get it done or there's another way.
You need to override the equals method in your class (and the hashcode method as well).
After you override the equals to your desire, in this case to compare the names, create a new object with the same name and call the remove(Object o) method of the LinkedList and get the object.
You should note that with this approach you objects equality will be defined by name and that the entry will be removed from the LinkedList

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