java 8 lambda - use stream multiple times - java

Is it possible to avoid creating stream inside current stream from the same collection like in below example to collect some data (listOfA is used two times to create stream) ?
List<A> listOfA = Arrays.asList(new A(1L, "A1", "V1"), new A(2L, "A2", "V1"), new A(1L, "A1", "V2"));
List<B> listOfB = listOfA.stream().map(r -> new B(r.getId(), r.getName(),
listOfA.stream().filter(r2 -> r.getId().equals(r2.getId())).map(A::getVal).collect(toSet())
)).distinct().collect(toList());
class A {
private final Long id;
private final String name;
private final String val;
A(Long id, String name, String val) //constructor
//getters
}
class B {
private final Long id;
private final String name;
private final Set<String> values;
B(Long id, String name, Set<String> values) //constructor
//getters
#Override
public boolean equals(Object o) {
...
return id.equals(a.id);
}
//hashCode
}
The final result should be a list of 2 objects:
B{id=1, name='A1', values=[V1, V2]}
B{id=2, name='A2', values=[V1]
Thanks in advance!

I'm not sure what you question aims at. If the question is what the minimal changes are that are necessary in order to avoid re-creating the stream, then I have to answer: I don't know. However, it seems like your approach is overly complicated. The chain of map, collect, filter, distinct and collect that is built there is really hard to understand.
After a short rant...
Maybe my fear that future Java programs will all look like this, and thus become completely unmaintainable is not justified. Maybe one just has "to get used to" this programming style. Maybe there's a short period when people are too eager with using the new language features, and it will come back to a "normal" style (and a "healthy" level of functional elements) sooner or later. But I, personally, think that a method like createBsByMergingTheValuesOfAs() would be sufficient and appropriate here.
... I'd like to suggest using a dedicated Collector, which already offers much of the infrastructure for mutable reduction that you seem to be emulating with this chain of operations:
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class StreamCollectTest
{
public static void main(String[] args)
{
List<A> listOfA = Arrays.asList(
new A(1L, "A1", "V1"),
new A(2L, "A2", "V1"),
new A(1L, "A1", "V2"));
Map<Long, B> result = listOfA.stream().collect(
Collectors.toConcurrentMap(
// The "id" will be the key of the map
a -> a.getId(),
// The initial value stored for each key will be a "B"
// whose set of values contains only the element of
// the corresponding "A"
a -> new B(a.getId(), a.getName(),
new LinkedHashSet<String>(Collections.singleton(a.getVal()))),
// Two "B"s with the same key will be merged by adding
// all values from the second "B" to that of the first
(b0,b1) -> { b0.values.addAll(b1.values); return b0; }));
System.out.println(result);
}
static class A
{
private final Long id;
private final String name;
private final String val;
A(Long id, String name, String val)
{
this.id = id;
this.name = name;
this.val = val;
}
public Long getId()
{
return id;
}
public String getName()
{
return name;
}
public String getVal()
{
return val;
}
}
static class B
{
private final Long id;
private final String name;
private final Set<String> values;
B(Long id, String name, Set<String> values)
{
this.id = id;
this.name = name;
this.values = values;
}
#Override
public String toString()
{
return id+","+name+","+values;
}
}
}
It prints
{1=1,A1,[V1, V2], 2=2,A2,[V1]}
So the values() of the resulting map should be exactly what you are looking for.

Related

How to iterate list of list on a condition and to return value in Map?

public class ClassOne {
private Long id;
private List<ClassTwo> secondClass;
}
public class ClassTwo {
private Long link;
private ClassThree thirdClass;
}
public class ClassThree {
private Long number;
}
Datas available :
List<classOne> classOneList
List<Long> referencedNumbers
Requirements :
To iterate the classOneList and store the values in Map<Long, <ListLong>> by having id from ClassOne as 'key' and link from ClassTwo as 'values' only if referencedNumbers contains a number from ClassThree.
Note : It should be implement only using streams rather than loops and restricted to Java 8
Return type should be: Map<Long, List<Long>>
For more Clarity: Map<id, List<link>>
Please let me know for any more clarifications
I added extra code to classes ClassOne, ClassTwo and ClassThree in order to write a SSCCE.
The implementation of your requirement (as stated in your question) is in method main of class Main.
More explanations after the code.
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
ClassThree c3 = new ClassThree(3L); // create 'ClassThree' instance
ClassTwo c2 = new ClassTwo(5L, c3); // create 'ClassTwo' instance
List<ClassTwo> c2s = List.of(c2); // create 'List<ClassTwo>'
ClassOne c1 = new ClassOne(0L, c2s); // create 'ClassOne' object
List<ClassOne> classOneList = List.of(c1);
List<Long> referencedNumbers = List.of(3L);
// Obtains map value (see below), i.e. list of links.
Function<ClassOne, List<Long>> valMapper = cOne -> cOne.getSecondClass()
.stream() // Stream<ClassTwo>
.map(cTwo -> cTwo.getLink()) // Stream<Long>
.collect(Collectors.toList()); // List<Long>
Map<Long, List<Long>> map = classOneList.stream()
.filter(cOne -> cOne.getSecondClass().stream()
.filter(cTwo -> referencedNumbers.contains(cTwo.getThirdClass().getNumber()))
.findFirst()
.isPresent())
.collect(Collectors.toMap(cOne -> cOne.getId(), // obtains map key.
valMapper));
System.out.println(map);
}
}
class ClassOne {
private Long id;
private List<ClassTwo> secondClass;
public ClassOne(Long id, List<ClassTwo> c2s) {
this.id = id;
secondClass = c2s;
}
public Long getId() {
return id;
}
public List<ClassTwo> getSecondClass() {
return secondClass;
}
}
class ClassTwo {
private Long link;
private ClassThree thirdClass;
public ClassTwo(Long ln, ClassThree c3) {
link = ln;
thirdClass = c3;
}
public Long getLink() {
return link;
}
public ClassThree getThirdClass() {
return thirdClass;
}
}
class ClassThree {
private Long number;
public ClassThree(Long num) {
number = num;
}
public Long getNumber() {
return number;
}
}
For each element in classOneList, the "filter" searches its secondClass list for a number that appears in referencedNumbers.
From the filtered list, I create the map.
When I run the above code, it outputs the following:
{0=[5]}
In other words, the id from ClassOne is 0 (zero) – which is the map key and the map value is a list containing a single element which is the value of link in ClassTwo.

Good way to organize group of constants in enum-like fashion

I'm trying to find a good way to orginize a group of constant values that are used simply for immutable data.
Here is what I'm currently attempting:
public class FishType {
//PredatorFishType extends FishType
public static final PredatorFishType SHARK = new PredatorFishType(5, 20, "Shark");
public static final FishType CAT_FISH = new FishType("Cat Fish");
private String name;
private FishType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
I use reflection to gather the final values into a collection aswell. I used to utilize enum but was forced to think of a new way to do this when different types of fish came into play such as the predator which contains other data such as food and so on. These constants are only used for data displaying purposes and have no reason to be mutated.
If there is some way to have multiple enum types within the same enum (If that makes any sense at all), that'd be great.
Thanks for reading.
You can either use constructor overloading or a combination of overloading and a wrapper class. If you know for certain that this data is immutable and will always be that way, I don't see anything wrong with sticking to enums for it. For the sake of putting it into one class, I've included the enums in the EnumTester class, but you may not want to do that.
Here's an example that prints "Cat Fish 5 20 Shark" and "Cow Fish" when run, using nothing but enums and a wrapper class. You could put accessors wherever you need them, depending on what you actually want to do with the information - I'm trying to demonstrate how to compose the two enums, not how to use them.
package enums;
public class EnumTester
{
public enum MainType {
CAT_FISH("Cat Fish"), DOG_FISH("Dog Fish"), COW_FISH("Cow Fish"); //everything has a name...
private String name;
private MainType(String name){
this.name = name;
}
public String getTypeDetails(){
return name;
}
}
public enum SubType {
PREDATOR(5, 20, "Shark"), PREY(), MANATEE(); //but not everything has any additional information
private boolean isFullSubType;
private int val1;
private int val2;
private String subName;
private SubType(int val1, int val2, String subName){
this.isFullSubType = true;
this.val1 = val1;
this.val2 = val2;
this.subName = subName;
}
private SubType(){
this.isFullSubType = false;
this.val1 = -1;
this.val2 = -1;
this.subName = "none";
}
public String getSubTypeDetails()
{
if( isFullSubType ) {
return val1 + " " + val2 + " " + subName;
}
else {
return "";
}
}
}
private MainType mainType;
private SubType subType;
public EnumTester(MainType mainType, SubType subType)
{
this.mainType = mainType;
this.subType = subType;
}
public static void main(String[] args)
{
EnumTester kittyShark = new EnumTester(MainType.CAT_FISH, SubType.PREDATOR);
System.out.println(kittyShark.printDetails());
EnumTester cowManatee = new EnumTester(MainType.COW_FISH, SubType.MANATEE);
System.out.println(cowManatee.printDetails());
}
public String printDetails(){
return mainType.getTypeDetails()+" "+subType.getSubTypeDetails();
}
}
I typically follow a similar pattern to what you've done above. I might make the class FishTypes to be the collector, just to keep the FishType interface a bit cleaner. You can also invent some syntactic sugar to help you collect registered FishTypes:
public static final Set<FishType> registeredFish = new HashSet<>();
public static final PredatorFishType SHARK = register(new PredatorFishType(5, 20, "Shark"));
public static final FishType CAT_FISH = register(new FishType("Cat Fish"));
public static <T extends FishType> T register(T fishType) {
registeredFish.add(fishType);
return fishType;
}

ArrayList get all values for an object property

Lets say I have an ArrayList of a object User so ArrayList<user>. A User object has a property userID.
Rather than iterating the list myself and adding userIDs to a separate list, is there and API call where I could pass it the property I want on that object and have a list of these properties returned to me? Had a look through the API and nothing stood out.
Looking for a solution in Java 7 or <.
You can do this using lambdas expressions (Java 8) :
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class Test {
public static void main(String args[]){
List<User> users = Arrays.asList(new User(1,"Alice"), new User(2,"Bob"), new User(3,"Charlie"), new User(4,"Dave"));
List<Long> listUsersId = users.stream()
.map(u -> u.id)
.collect(Collectors.toList());
System.out.println(listUsersId);
}
}
class User {
public long id;
public String name;
public User(long id, String name){
this.id = id;
this.name = name;
}
}
Output :
[1, 2, 3, 4]
Snippet here.
Ugliest solution using reflection :
public class Test {
public static void main (String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
List<User> users = Arrays.asList(new User(1,"Alice"), new User(2,"Bob"), new User(3,"Charlie"), new User(4,"Dave"));
List<Object> list = get(users,"id");
System.out.println(list);
}
public static List<Object> get(List<User> l, String fieldName) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
Field field = User.class.getDeclaredField(fieldName);
field.setAccessible(true);
List<Object> list = new ArrayList<>();
for(User u : l){
list.add(field.get(u));
}
field.setAccessible(false);
return list;
}
}
class User {
private long id;
private String name;
public User(long id, String name){
this.id = id;
this.name = name;
}
}
Output :
[1, 2, 3, 4]
You can use Guava's Collections2#transform method to have the same result.
List<Integer> userIDs = Collections2.transform(users, new Function<User, Integer> (){
public Integer apply(User user) {
return user.getUserID();
}
});
Guava supports Java 7 and lower, so if you want to use an external library, the above will work in your case.
Unfortunately, you will have to do similar logic for any other objects and their inner fields you might have. It's not as a generic solution as the reflection one, it's just a more compact one.
It sounds like you want to use a Map.
Maps use a Key, Value pair. You can assign the userID as the key and the actual user object as the value.
You can read more here

java: Comparator and Treeset to remove duplicates

i have a java class like this
public class A {
private String field1;
private String field2;
// getters, setters but no equals and hashcode
}
and a list of objects of this class, i want to remove from this list all the duplicates elements that has the same field1 or the same field2, so i have 2 Comparators
public class Comparator1 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField1().compareToIgnoreCase( o2.getField1() );
}
}
public class Comparator2 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField2().compareToIgnoreCase(o2.getField2());
}
}
so to do the task i use treeset like
TreeSet<A> ts1 = new TreeSet<A>(new Comparator1())
ts1.addAll(list)
TreeSet<A> ts2 = new TreeSet<A>(new Comparator2())
ts2.addAll(ts1)
list.clear()
list.addAll(ts2)
but how can i do the same using just one comparator and one treeset ?
Thanks for the help
Update:
Thanks all for the answers, but after reading them i don't know if this is the right approach to the real problem.
In my real case field1 is like a phone number and field2 is like a name.
So i don't want to call the same phone number more than one time (this is the first treeset to removes duplicates) and i don't want to call more than one time the same name (the second treeset to removes duplicates)
You can modify the class but i'd like to know if this approach is ok to resolve the real problem.
If this approach is correct, from your question, i see that without modifying the class is not possible to use just one comparator
Thanks
You can't use one comparator to sort by two criteria at the same time, so there is no real way to go better than two TreeSets in your case. Of course, you can wrap them in one data structure.
(Alternatively you could use two HashMaps, each having one of the strings as key - this will be faster on average, but is more complicated to program.)
You can't, and it's not clear to me that what you're trying to do is well-defined.
Are you aware that your current approach depends both on the order in which elements are added and on whether you check field1 or field2 first for duplicates? Imagine you had these objects of class A:
A ab = new A("a", "b");
A cb = new A("c", "b");
A cd = new A("c", "d");
Checking field1 first gives the result [ab] or [ab, cd], depending on the order added.
Checking field2 first gives the result [cb] or [ab, cd], depending on the order added.
This is pretty strange behavior. Is this what you intended? I don't think it is possible to reproduce this with a single TreeSet and Comparator in the general case.
public static <A extends Comparable<?>> TreeSet<A> getTreeSet(Collection<A> list){
TreeSet<A> result = new TreeSet<A>();
HashSet<A> unique = new HashSet<A>();
unique.addAll(list);
result.addAll(unique);
return result;
}
Generic function that adds items to hashset to make them unique, and then drop them to TreeSet to sort. You can use it with: TreeSet<A> ts1 = getTreeSet(list);.
This approach works well for a fixed list.
#BalusC No, this assumes
public class A implements Comparable<A> {
private String field1;
private String field2;
#Override
public int compareTo(A o) {
// No null checks, because it's illegal anyways.
int tmp = 0;
if ((tmp = field1.compareToIgnoreCase(o.field1)) != 0)
return tmp;
if ((tmp = field2.compareToIgnoreCase(o.field2)) != 0)
return tmp;
return tmp;
}
// getters, setters but no equals and hashcode
}
If your intention is to do two levels of sorting(first: PhoneNumber and second:Name), then you can use the following code, where the duplicate check will be done against both the fields(field1 and field2). As we are already using compareTo for both the fields, it is not required to use equals and hashcode. But it is always good practice to use hashcode and equals.
public class A implements Comparable<A> {
private String field1;
private String field2;
public A(String number, String name) {
this.field1 = number;
this.field2 = name;
}
// First level sorting will be done by field1.
// If field1 is equal then second level sorting will be done on field2
#Override
public int compareTo(A o) {
int compareTo = field1.compareTo(o.getNumber());
if(compareTo==0){
return field2.compareTo(o.getName());
}
return compareTo;
}
public String getNumber() {
return field1;
}
public String getName() {
return field2;
}
}
public class RemoveDuplicate {
public static void main(String[] args) {
final ArrayList<Student> students = new ArrayList<Student>();
Set<Student> set = new TreeSet<Student>();
Student[] starr = new Student[6];
starr[0] = new Student("Student1", "1005");
starr[1] = new Student("Student2", "1004");
starr[2] = new Student("Student3", "1003");
starr[3] = new Student("Student6", "1002");
starr[4] = new Student("Student5", "1001");
starr[5] = new Student("Student6", "1000");
Arrays.sort(starr, Student.StudentIdComparator);
for (Student s : starr) {
students.add(s);
}
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.List;
public class RemoveDuplicate {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
List<Student> students = Arrays.asList(new Student("Student1", "1005"), new Student("Student2", "1004"),
new Student("Student3", "1003"), new Student("Student6", "1002"), new Student("Student5", "1001"),
new Student("Student6", "1000"));
// Sorting Using Lambda
students.sort(new Comparator<Student>() {
#Override
public int compare(Student s1, Student s2) {
return s1.getId().compareTo(s2.getId());
}
});
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}
}
class Student implements Comparable<Student> {
private String name;
private String id;
public Student(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "\n" + "Name=" + name + " Id=" + id;
}
#Override
public int compareTo(Student o1) {
if (o1.getName().equalsIgnoreCase(this.name)) {
return 0;
}
return 1;
}
// public static Comparator<Student> StudentIdComparator = (Student
// s1,Student s2) -> s1.getId().compareTo(s2.getId());
}

Searching an object in ArrayList

I am storing objects in ArrayList, where my pojo is as
public class POJOSortableContacts {
private Long id;
private String displayName;
public POJOSortableContacts(Long id, String displayName) {
super();
this.id = id;
this.displayName = displayName;
}
//Setter and Getters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
//This will be used to sectioned header.
public String getLabel() {
return Character.toString(displayName.charAt(0)).toUpperCase();
}
//Sortable categories
//Sort by Contact name
public static Comparator<POJOSortableContacts> COMPARE_BY_NAME = new Comparator<POJOSortableContacts>() {
public int compare(POJOSortableContacts one, POJOSortableContacts other) {
return one.getDisplayName().compareToIgnoreCase(other.getDisplayName());
//return s1.toLowerCase().compareTo(s2.toLowerCase()); //it returns lower_case word first and then upper_case
}
};
//Sort by id
public static Comparator<POJOSortableContacts> COMPARE_BY_ID = new Comparator<POJOSortableContacts>() {
public int compare(POJOSortableContacts one, POJOSortableContacts other) {
return one.id.compareTo(other.id);
}
};
}
and Arraylist structure is as
ArrayList<POJOSortableContacts> contactArrayList = new ArrayList<POJOSortableContacts>()
, I want to search an object from contactArrayList by id (for example I want an object which id is 20), I want to use binarysearch for this. So how can it will be?
You can use
POJOSortableContacts contact = Collections.binarySearch(contactArrayList,
new POJOSortableContacts(20, ""),
COMPARE_BY_ID);
The new POJOSortableContacts is just a dummy object to act as the key.
Of course, this will only work if your list is sorted by ID to start with - you can't use a binary search on an unsorted list (or on a list which is sorted in a different way).
I will rather suggest that you use a HashMap.
Map<Long,POJOSortableContacts> contactMap = new HashMap<Long,POJOSortableContacts>();
Fill up your contactMap like this:
contactMap.put(myContact.getId(), myContact);
Searching then becomes trivial:
POJOSortableContacts myContact = contactMap.get(myID);
To be able to use binary search, your collection must be sorted. You could sort your ArrayList each time before your search, but that would negate the advantage of using binary search (you could just do a linear search over the unsorted list and still be faster).
ArrayList has a method - BinarySearch, which takes object to search as a parameter.
POJOSortableContacts contactToSearch = new POJOSortableContacts(someId, "Name");
POJOSortableContacts myContact = contactArrayList.BinarySearch(contactToSearch);
Hope this helps.
Sidestepping the question a bit, if you can't have duplicates in the list you'd likely be better served by using a SortedSet to store the contacts. No sorting before using binarySearch anymore...

Categories