Assume I have two Classes
public class TestA{
TestB testB;
String text;
SecondTest secondTest;
.
.
.
}
public class TestB{
int a;
int b;
}
Now I have a List with TestB List<TestB> list1
If I want to sort the list I can do something like this:
list1.sort(Comparator.comparing(TestB::getA)
But what if I have a List with TestA List<TestA> list2
How I sort to a or b (TestB)?
That's an interesting question.
I don't know of any Java "native" solution for this kind of deep comparison.
But one idea is to use your own specific Comparator:
List<TestA> list2 = Arrays.asList( new TestA(new TestB(2, 20)), new TestA(new TestB(1, 10)), new TestA(new TestB(3, 30)) );
list2.sort( getComparator() );
public Comparator<TestA> getComparator() {
return new Comparator<TestA>() {
#Override
public int compare(TestA obj1, TestA obj2) {
int obj1A = obj1.getTestB().getA();
int obj2A = obj2.getTestB().getA();
return Integer.compare(obj1A, obj2A);
}
};
}
Of couse null values should be handled accordingly.
Related
I'm quite new to using Java and was trying to flatten collections within collections using map to try and get a single List. However I don't seem to be able to get this working. In order to reproduce this I've created quite a simple example of what I'm trying to do. Here is what I have so far:
ClassA
import java.util.List;
public class ClassA {
private List<ClassB> listOfClassB;
public ClassA(List<ClassB> listOfClassB) {
this.listOfClassB = listOfClassB;
}
public List<ClassB> getListOfClassB() {
return this.listOfClassB;
}
}
ClassB
public class ClassB {
private String item;
public ClassB(String item) {
this.item = item;
}
public String getItem() {
return item;
}
}
Main
public class Main {
public static void main(String[] args) {
ClassB firstClass = new ClassB("A");
ClassB secondClass = new ClassB("B");
ClassB thirdClass = new ClassB("C");
ClassB fourthClass = new ClassB("D");
ArrayList<ClassB> firstClassList = new ArrayList<>();
ArrayList<ClassB> secondClassList = new ArrayList<>();
firstClassList.add(firstClass);
firstClassList.add(secondClass);
secondClassList.add(thirdClass);
secondClassList.add(fourthClass);
ArrayList<ClassA> classes = new ArrayList<>();
classes.add(new ClassA(firstClassList));
classes.add(new ClassA(secondClassList));
List<List<String>> collect = classes.stream().map(c -> c.getListOfClassB().stream().map(ClassB::getItem).collect(Collectors.toList())).collect(Collectors.toList());
}
}
As you can see on the bottom I am able to get List<List<String>> but what I'm looking to get is a List<String> of the items within ClassB. I've tried using a flatmap for this but I couldn't get it working and was looking for some guidance.
Thanks in advance.
Here is the flatmap example which works fine:
classes.stream().flatMap(aclass -> aclass.getListOfClassB().stream())
.forEach(b -> System.out.println("Class B Item Name : "+b.getItem()));
It gives the following output:
Class B Item Name : A
Class B Item Name : B
Class B Item Name : C
Class B Item Name : D
and to get the exact answer:
List<String> collect2 = classes.stream().flatMap(aclass -> aclass.getListOfClassB().stream())
.map(b -> b.getItem())
.collect(Collectors.toList());
it gives me a list as follows:
collect2 : [A, B, C, D]
This will return a list of Strings of ClassB :
List<String> listB = classes.stream().flatMap(a-> a.getListOfClassB().stream())
.map(ClassB::getItem)
.collect(Collectors.toList());
Here, we use a flatMap and then map the getItem of ClassB and finally use .collect(...) to return the list.
I trying to implement functionally similar to CollectionUtils transform (Apache Commons Collections)
class CollectionUtils {
public static void transformerModifier(Collection<MyClass> myCollection) {
// How should I implement this method in order that
// output from the line 1 and line 2 will be the same ?
}
public static List<String> transform(Collection<MyClass> myCollection) {
List<String> strCollection = new LinkedList<>();
for (MyClass item : myCollection) {
strCollection.add(item.getName());
}
return strCollection;
}
}
class myClass {
private String name;
private int value;
myClass( String name, int value) {
this.name = name ;
this.value = value;
}
public String toString(){
return new String(name+ ":" + value ) ;
}
}
class MyClassCollection{
private List<myClass> list ;
myClassCollection(List<myClass> list){
this.list = list;
}
List<myClass> collection(){
return list.clone();
}
}
public class TestClass{
public static void main (String[] args) {
List<MyClass> list = new ArrayList<>();
list.add(new myClass("John", 12);
list.add(new myClass("Mike", 16);
list.add(new myClass("Eric", 13);
list.add(new myClass("Mark", 142);
list.add(new myClass("Alex", 112);
MyClassCollection myOjb = new MyClassCollection(list );
CollectionUtils.transformerModifier(myObj.collection() );
List<MyClass> myList = CollectionUtils.transform(myObj.collection());
System.out.println(Arrays.toString(myObj.collection().toArray)); // line 1
System.out.println(Arrays.toString(myList.toArray)); // line 2
}
}
output: [John,Mike,Eric,Mark,Alex] // output after line 1
output: [John,Mike,Eric,Mark,Alex] // should be output after line 2
My question is it possible to implement method transformerModifier in the way that it will change collection of the object myObj so that myObj.collection() return not the List<myClass> but the List of List<String> ( where string is the data from private String name data member of myClass ) ?
My guess is that the solution should be through anonymous class. However, I didn't understand yet how should I implement it.
If you are using Java 8, you could make use of streams and map() to do something like this:
List<MyClass> myClassList = new ArrayList<>();
//add your items to myClassList here
List<String> names = myClassList.stream().map(MyClass::getName).collect(Collectors.toList());
//names will now consist of a List of all the names associated with
//each of the MyClass objects within myClassList in the same order
This solution makes use of Method Reference as well MyClass::getName. This calls the getName method on each object in the stream mapping it to its respective spot in the transformed stream using .map().
Next it uses .collect() to bring it back from a stream to a list using Collectors.toList().
If you are working with a lot of objects within myClassList, this process can be sped up using .parallelStream() instead of .stream(), but if you are not working with a large amount of data, you may see a reduction in performance with .parallelStream(). It all depends on how many objects you expect to be present within the List.
public interface Converter<I, O> {
void tranformer(List list);
O retriever(I obj);
}
_
public static <I, O> void transform(Converter<I, O> converter, List inputList) {
Iterator<I> it = inputList.iterator();
List list = new LinkedList<>();
while (it.hasNext()) {
list.add(converter.retriever(it.next()));
}
converter.tranformer(list);
}
_
public static void main(String[] args) {
List<MyClass> list = new ArrayList<>();
list.add(new myClass("John", 12);
list.add(new myClass("Mike", 16);
list.add(new myClass("Eric", 13);
list.add(new myClass("Mark", 142);
list.add(new myClass("Alex", 112);
MyClassCollection myclasscollection = new MyClassCollection(list);
final List collectionList = myclasscollection.collection();
CollectionUtils.transform(new Converter<myClass, String>() {
#Override
public void tranformer(List list) {
employeeList.clear();
employeeList.addAll(list);
}
#Override
public String retriever(myClass obj) {
return obj.name; // make the data member public or add getter
}
}, collectionList);
collectionList.get(0).toString.toLowerCase();
}
This isn't fully what you need but I bet this isn't bad alternative. Please, notice that could output collection collectionList will be collection of objects ( not String ), however, you can access to methods of the String data type just to right like this collectionList.get(0).toString.toLowerCase(); Hope this help.
I have to write a generic heap priority queue, and three separate comparator classes that compare different things. How do I assign the different comparators to each instance of the heap. So in my main I'll have something like
GenericHeap D= new GenericHeap <String> ();
GenericHeap S= new GenericHeap <Integer> ();
and then the methods of D, such as add, and remove would have to use of the comparators I've made, and S would have to use a different one. How do I assign a comparator to each instance of the heap class. All of the heap have to be written with the same code, so I don't understand how this works?
You could take it as a constructor parameter
GenericHeap D = new GenericHeap<String>(new MyStringComparator());
or via a mutator function
D.setComparator(new MyStringComparator());
Either way you should make these functions take something like an IMyComparatorInterface which has a method such as Compare(T item1, T item2)
public class MyStringComparator implements IMyComparatorInterface
Rather than implementing Comparator, you could create different ones for each type and call .compare on those. Replace List with GenericHeap:
Comparator<List<String>> stringListComparator = new Comparator<List<String>>() {
#Override
public int compare(List<String> arg0, List<String> arg1) {
// TODO Add comparison code
return 0;
}
};
Comparator<List<Integer>> integerListComparator = new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> arg0, List<Integer> arg1) {
// TODO Add comparison code
return 0;
}
};
stringListComparator.compare(Collections.emptyList(), Collections.emptyList());
integerListComparator.compare(Collections.emptyList(), Collections.emptyList());
Or with Java 8 lamdas:
Comparator<List<String>> stringListComparator = (p1, p2) -> {
// TODO Add comparison code
return 0;
};
Comparator<List<Integer>> integerListComparator = (p1, p2) -> {
// TODO Add comparison code
return 0;
};
stringListComparator.compare(Collections.emptyList(), Collections.emptyList());
integerListComparator.compare(Collections.emptyList(), Collections.emptyList());
Alternatively, implement Comparable, pass in a Comparator in the constructor and call that from .compare as follows:
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class StackOverflowExample implements Comparable<StackOverflowExample> {
private final Comparator<StackOverflowExample> comparator;
public StackOverflowExample(Comparator<StackOverflowExample> comparator) {
this.comparator = comparator;
}
#Override
public int compareTo(StackOverflowExample arg0) {
return comparator.compare(this, arg0);
}
public static void main(String[] args) {
Comparator<StackOverflowExample> comparator = (p1, p2) -> {
// TODO Add comparison code
};
StackOverflowExample stackOverflowExample = new StackOverflowExample(comparator);
}
}
Imagine that I have this class:
public class Test
{
private String[] arr = new String[]{"1","2"};
public String[] getArr()
{
return arr;
}
}
Now, I have another class that uses the above class:
Test test = new Test();
test.getArr()[0] ="some value!"; //!!!
So this is the problem: I have accessed a private field of a class from outside!
How can I prevent this? I mean how can I make this array immutable? Does this mean that with every getter method you can work your way up to access the private field? (I don't want any libraries such as Guava. I just need to know the right way to do this).
If you can use a List instead of an array, Collections provides an unmodifiable list:
public List<String> getList() {
return Collections.unmodifiableList(list);
}
You must return a copy of your array.
public String[] getArr() {
return arr == null ? null : Arrays.copyOf(arr, arr.length);
}
Modifier private protects only field itself from being accessed from other classes, but not the object references by this field. If you need to protect referenced object, just do not give it out. Change
public String [] getArr ()
{
return arr;
}
to:
public String [] getArr ()
{
return arr.clone ();
}
or to
public int getArrLength ()
{
return arr.length;
}
public String getArrElementAt (int index)
{
return arr [index];
}
The Collections.unmodifiableList has already been mentioned - the Arrays.asList() strangely not! My solution would also be to use the list from the outside and wrap the array as follows:
String[] arr = new String[]{"1", "2"};
public List<String> getList() {
return Collections.unmodifiableList(Arrays.asList(arr));
}
The problem with copying the array is: if you're doing it every time you access the code and the array is big, you'll create a lot of work for the garbage collector for sure. So the copy is a simple but really bad approach - I'd say "cheap", but memory-expensive! Especially when you're having more than just 2 elements.
If you look at the source code of Arrays.asList and Collections.unmodifiableList there is actually not much created. The first just wraps the array without copying it, the second just wraps the list, making changes to it unavailable.
You can also use ImmutableList which should be better than the standard unmodifiableList. The class is part of Guava libraries that was create by Google.
Here is the description:
Unlike Collections.unmodifiableList(java.util.List), which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change
Here is a simple example of how to use it:
public class Test
{
private String[] arr = new String[]{"1","2"};
public ImmutableList<String> getArr()
{
return ImmutableList.copyOf(arr);
}
}
at this point of view you should use system array copy:
public String[] getArr() {
if (arr != null) {
String[] arrcpy = new String[arr.length];
System.arraycopy(arr, 0, arrcpy, 0, arr.length);
return arrcpy;
} else
return null;
}
}
You could return a copy of the data. The caller who chooses to change the data will only be changing the copy
public class Test {
private static String[] arr = new String[] { "1", "2" };
public String[] getArr() {
String[] b = new String[arr.length];
System.arraycopy(arr, 0, b, 0, arr.length);
return b;
}
}
The nub of the problem is that you are returning a pointer to a mutable object. Oops. Either you render the object immutable (the unmodifiable list solution) or you return a copy of the object.
As a general matter, finality of objects does not protect objects from being changed if they are mutable. These two problems are "kissing cousins."
Returning an unmodifiable list is a good idea. But a list that is made unmodifiable during the call to the getter method can still be changed by the class, or classes that are derived from the class.
Instead you should make it clear to anybody that extends the class that the list should not be modified.
So in your example it could lead to the following code:
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
public static final List<String> STRINGS =
Collections.unmodifiableList(
Arrays.asList("1", "2"));
public final List<String> getStrings() {
return STRINGS;
}
}
In the above example I've made the STRINGS field public, in principle you could do away with the method call, as the values are already known.
You could also assign the strings to a private final List<String> field made unmodifiable during construction of the class instance. Using a constant or instantiation arguments (of the constructor) depends on the design of the class.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
private final List<String> strings;
public Test(final String ... strings) {
this.strings = Collections.unmodifiableList(Arrays
.asList(strings));
}
public final List<String> getStrings() {
return strings;
}
}
Since Java 9, an immutable list can also be constructed from a static factory method List.of() which results in just a bit fewer imports and code:
With an alias being returned from getUsers() when the original users fields can be modified:
class Computer {
private String[] users = new String[] {"user1", "user2", "user3"};
public String[] getUsers;
String[] getUsers() {
return this.users;
}
}
Computer c = new Computer();
c.getUsers()[0] = "me";
for (String user: c.getUsers()) {
System.out.println(user);
}
Output:
me
user2
user3
Using the immutable list:
import java.util.List;
class Computer {
private String[] users = new String[] {"user1", "user2", "user3"};
public List<String> getUsers;
List<String> getUsers() {
return List.of(this.users);
}
}
Computer c = new Computer();
c.getUsers().set(0, "me");
for (String user: c.getUsers()) {
System.out.println(user);
}
Output:
user1
user2
user3
Yes, you should return a copy of the array:
public String[] getArr()
{
return Arrays.copyOf(arr);
}
I have an ArrayList of objects, which I need to sort using two attributes (using Comparators). I need to save the sorted output to a text file with a different name, depending on the attribute used to sort. For example, if the list is sorted by attribute1 then file will be attribute1.txt, if attribute2 the file will be attribute2.txt.
How I want it to work (pseudocode):
if(sortedByAtr1){
FileWriter fwstream = new FileWriter(sortedByAtribute1.getName()+".txt");
}
else(sortedByAtr2){
FileWriter fwstream = new FileWriter(sortedByAtribute2.getName()+".txt");
}
Is this possible?
I appreciate any advice.
Thanks.
Servo
Here's an object-oriented approach to solving this requirement.
Use a wrapper for the List and its sorting attribute:
public class ListSorter<V> {
private final List<V> values;
private String sortingAttribute;
public ListSorter(List<V> values) {
this.values = values;
}
public void sort(AttributeComparator<V> comparator) {
Collections.sort(values, comparator);
sortingAttribute = comparator.getSortingAttribute();
}
public String getSortingAttribute() {
return sortingAttribute;
}
}
Extend the Comparator interface so you can get your attribute name:
public interface AttributeComparator<T> extends Comparator<T> {
public String getSortingAttribute();
}
Create custom AttributeComparators like this:
public class FooBarComparator implements AttributeComparator<Foo> {
public int compare(Foo foo1, Foo foo2) {
// skipped nullchecks for brevity
return foo1.getBar().compare(foo2.getBar());
}
public String getSortingAttribute() {
return "bar";
}
}
Use:
List<Foo> yourList = new ArrayList<Foo>();
ListSorter<Foo> example = new ListSorter<Foo>(yourList);
AttributeComparator comparator1 = new FooBarComparator();
example.sort(comparator1);
FileWriter fwstream = new FileWriter(example.getSortingAttribute() +".txt");