Map collection elements and keep reference to source collection - java

I'm looking for a way to create a collection, list, set, or map which contains the transformed elements of an original collection and reflects every modification in that collection.
For example if I have a List<Integer> from a third party API and another API is expecting a List<String>. I know I can transform the list like this:
List<Integer> intList = thirdPartyBean.getIntListProperty();
List<String> stringList = intList.stream().map(Integer::toString)
.collect(Collectors.toList());
secondBean.setStringListProperty(stringList);
The problem is, if anything is changed in one of the lists the other one will still reflect the previous state. Let's assume that intList contains [1, 2, 3]:
intList.add(4);
stringList.remove(0);
System.out.println(intList.toString()); // will print: [1, 2, 3, 4]
System.out.println(stringList.toString()); // will print: [2, 3]
// Expected result of both toString(): [2, 3, 4]
So I'm searching for something like List.sublist(from, to) where the result is "backed" by the original list.
I'm thinking of implementing my own list wrapper which is used like this:
List<String> stringList = new MappedList<>(intList, Integer::toString, Integer::valueOf);
The second lambda is for inverting the conversion, to support calls like stringList.add(String).
But before I implement it myself I would like to know if I try to reinvent the wheel - maybe there is already a common solution for this problem?

I would wrap the list in another List with transformers attached.
public class MappedList<S, T> extends AbstractList<T> {
private final List<S> source;
private final Function<S, T> fromTransformer;
private final Function<T, S> toTransformer;
public MappedList(List<S> source, Function<S, T> fromTransformer, Function<T, S> toTransformer) {
this.source = source;
this.fromTransformer = fromTransformer;
this.toTransformer = toTransformer;
}
public T get(int index) {
return fromTransformer.apply(source.get(index));
}
public T set(int index, T element) {
return fromTransformer.apply(source.set(index, toTransformer.apply(element)));
}
public int size() {
return source.size();
}
public void add(int index, T element) {
source.add(index, toTransformer.apply(element));
}
public T remove(int index) {
return fromTransformer.apply(source.remove(index));
}
}
private void test() {
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> stringList = new MappedList<>(intList, String::valueOf, Integer::valueOf);
intList.add(4);
stringList.remove(0);
System.out.println(intList); // Prints [2, 3, 4]
System.out.println(stringList); // Prints [2, 3, 4]
}
Note that the fromTransformer needs null checking for the input value, if source may contain null.
Now you are not transforming the original list into another one and losing contact with the original, you are adding a transformation to the original list.

I don't know what version of the JDK you are using, but if you are okay with using the JavaFX library you can use ObservableList. You do not need to modify an existing list as ObservableList is a wrapper for java.util.List. Look at extractor in FXCollection for complex Objects. This article has an example of it.
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ListChangeListener.Change;
public class ObservableBiList{
//prevent stackoverflow
private static final AtomicBoolean wasChanged = new AtomicBoolean( false);
public static <T, R> void change( Change< ? extends T> c, ObservableList< R> list, Function< T, R> convert) {
if( wasChanged.get()){
wasChanged.set( false);
return;
}
wasChanged.set( true);
while( c.next()){
if( c.wasAdded() && !c.wasReplaced()){
for( T str : c.getRemoved())
list.add( convert.apply( str));
}else if( c.wasReplaced()){
for( int i=c.getFrom();i<c.getTo();i++)
list.set( i,convert.apply( c.getList().get( i)));
}else if( c.wasRemoved()){
for( T str : c.getRemoved())
list.remove( convert.apply( str));
}
}
System.out.printf( "Added: %s, Replaced: %s, Removed: %s, Updated: %s, Permutated: %s%n",
c.wasAdded(), c.wasReplaced(), c.wasRemoved(), c.wasUpdated(), c.wasPermutated());
}
public static void main( String[] args){
ObservableList< Integer> intList = FXCollections.observableArrayList();
intList.addAll( 1, 2, 3, 4, 5, 6, 7);
ObservableList< String> stringList = FXCollections.observableArrayList();
stringList.addAll( "1", "2", "3", "4", "5", "6", "7");
intList.addListener( ( Change< ? extends Integer> c) -> change( c, stringList, num->Integer.toString( num)));
stringList.addListener( ( Change< ? extends String> c) -> change( c, intList, str->Integer.valueOf( str)));
intList.set( 1, 22);
stringList.set( 3, "33");
System.out.println( intList);
System.out.println( stringList);
}
}

This is exactly the kind of problems that the Observer Pattern solves.
You can create two wrappers, around List<String> and List<Integer> and let first wrapper observe the state of the other one.

public static void main(String... args) {
List<Integer> intList = ObservableList.createBase(new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)));
List<String> stringList = ObservableList.createBase(intList, String::valueOf);
stringList.remove(0);
intList.add(6);
System.out.println(String.join(" ", stringList));
System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private static final class ObservableList<T, E> extends AbstractList<E> {
// original list; only this one could be used to add value
private final List<T> base;
// current snapshot; could be used to remove value;
private final List<E> snapshot;
private final Map<Function<T, ?>, List> cache;
public static <T, E> List<E> createBase(List<T> base) {
Objects.requireNonNull(base);
if (base instanceof ObservableList)
throw new IllegalArgumentException();
return new ObservableList<>(base, null, new HashMap<>());
}
public static <T, R> List<R> createBase(List<T> obsrv, Function<T, R> func) {
Objects.requireNonNull(obsrv);
Objects.requireNonNull(func);
if (!(obsrv instanceof ObservableList))
throw new IllegalArgumentException();
return new ObservableList<>(((ObservableList<T, R>)obsrv).base, func, ((ObservableList<T, R>)obsrv).cache);
}
#SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
private ObservableList(List<T> base, Function<T, E> func, Map<Function<T, ?>, List> cache) {
this.base = base;
snapshot = func != null ? base.stream().map(func).collect(Collectors.toList()) : (List<E>)base;
this.cache = cache;
cache.put(func, snapshot);
}
#Override
public E get(int index) {
return snapshot.get(index);
}
#Override
public int size() {
return base.size();
}
#Override
public void add(int index, E element) {
if (base != snapshot)
super.add(index, element);
base.add(index, (T)element);
cache.forEach((func, list) -> {
if (func != null)
list.add(index, func.apply((T)element));
});
}
#Override
public E remove(int index) {
E old = snapshot.remove(index);
for (List<?> back : cache.values())
if (back != snapshot)
back.remove(index);
return old;
}
}
System.out.println(String.join(" ", stringList));
System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
}
private static final class ObservableList<E> extends AbstractList<E> {
private final List<List<?>> cache;
private final List<E> base;
public static <E> List<E> create(List<E> delegate) {
if (delegate instanceof ObservableList)
return new ObservableList<>(((ObservableList<E>)delegate).base, ((ObservableList<E>)delegate).cache);
return new ObservableList<>(delegate, new ArrayList<>());
}
public static <T, R> List<R> create(List<T> delegate, Function<T, R> func) {
List<R> base = delegate.stream().map(func).collect(Collectors.toList());
List<List<?>> cache = delegate instanceof ObservableList ? ((ObservableList<T>)delegate).cache : new ArrayList<>();
return new ObservableList<>(base, cache);
}
#SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
private ObservableList(List<E> base, List<List<?>> cache) {
this.base = base;
this.cache = cache;
cache.add(base);
}
#Override
public E get(int index) {
return base.get(index);
}
#Override
public int size() {
return base.size();
}
#Override
public void add(int index, E element) {
for (List<?> back : cache)
back.add(index, element);
}
#Override
public E remove(int index) {
E old = base.remove(index);
for (List<?> back : cache)
if (back != base)
back.remove(index);
return old;
}
}

You need to create a wrapper on top of the first list, considering your example, the List<Integer>.
Now if you want List<String> to reflect all the runtime changes done to List<Integer>, you have two solutions.
Don't create initial List<String>, use a method or a wrapper which will always return the transformed values from List<Integer>, so you'll never have a static List<String>.
Create a wrapper around List<Integer>, which should have a reference to List<String>, and override the add(), addAll(), remove() and removeAll() methods. In the overridden methods, change the state of your List<String>.

Another option would be to use the JavaFX ObservableList class which can wrap an existing list with an observable layer on which you can define the operations you want to propagate.
Here is an example which propagates from the string list to the integer list:
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
ObservableList<String> strings = FXCollections.observableList(strList);
strings.addListener((ListChangeListener<String>) change -> {
if(change.next()) {
if (change.wasAdded()) {
change.getAddedSubList().stream().map(Integer::valueOf).forEach(intList::add);
} else if (change.wasRemoved()) {
change.getRemoved().stream().map(Integer::valueOf).forEach(intList::remove);
}
}
});
strList = strings;
strList.add("1");
strList.add("2");
strList.add("2");
System.out.println(intList);
strList.remove("1");
System.out.println(intList);
If you execute this code you will see this output on the console:
[1, 2, 2]
[2, 2]

Try to implement a Thread for this. The example below simulates the context you have presented, but will always have some 100% busy core.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class Main {
static List<Integer> intListProperty;
static List<String> stringList;
public static void main(String... args) throws InterruptedException {
Main m = new Main();
m.execute();
}
private void updateAlways(Main main) {
class OneShotTask implements Runnable {
Main main;
OneShotTask(Main main) {
this.main = main;
}
public void run() {
while (main.intListProperty == main.getIntListProperty()) {}
main.intListProperty = getIntListProperty();
main.stringList = main.intListProperty.stream().map(s -> String.valueOf(s)).collect(Collectors.toList());
main.updateAlways(main);
}
}
Thread t = new Thread(new OneShotTask(main));
t.start();
}
public void execute() throws InterruptedException {
System.out.println("Starting monitoring");
stringList = new ArrayList<>();
intListProperty = new ArrayList<>();
intListProperty.add(1);
intListProperty.add(2);
intListProperty.add(3);
updateAlways(this);
while(true) {
Thread.sleep(1000);
System.out.println("\nintListProperty: " + intListProperty.toString()); // will print: [1, 2, 3, 4]
System.out.println("stringList: " + stringList.toString()); // will print: [2, 3]
}
}
// simulated
//thirdPartyBean.getIntListProperty();
private List<Integer> getIntListProperty() {
long timeInMilis = System.currentTimeMillis();
if(timeInMilis % 5000 == 0 && new Random().nextBoolean()) {
Object[] objects = intListProperty.toArray();
// change memory position
intListProperty = new ArrayList<>();
intListProperty = new ArrayList(Arrays.asList(objects));
intListProperty.add(new Random().nextInt());
}
return intListProperty;
}
}

Because of your example I am assuming that the method, you have no access to, only modifies the list and does not access the data itself.
You could use Raw Types.
List list = new ArrayList<Object>();
If you want to access the data you have to convert everything to the desired type.
list.stream().map(String::valueOf).<do_something>.collect(toList())
Not the cleanest solution but might work for you.
I think the cleanest solution would be to implement a wrapper as you already stated.
Example using the System.out:
public static void testInteger(List<Integer> list) {
list.add(3);
list.remove(0);
}
public static void testString(List<String> list) {
list.add("4");
list.remove(0);
}
public static void main(String...args) {
List list = new ArrayList<Object>(Arrays.asList("1", "2"));
testInteger(list);
System.out.println(list.toString()); // will print: [2, 3]
testString(list);
System.out.println(list.toString()); // will print: [3, 4]
}
You always use the same reference, that way you dont need to worry about inconsistencies and its more performant then to always transform the objects.
But something like this would break the code:
public static void main(String...args) {
List list = new ArrayList<Object>(Arrays.asList("1", "2"));
testInteger(list);
System.out.println(list.toString()); // will print: [2, 3]
testString(list);
System.out.println(list.toString()); // will print: [3, 4]
accessData(list); //Will crash
}
public static void accessData(List<Integer> list) {
Integer i = list.get(0); //Will work just fine
i = list.get(1); //Will result in an Class Cast Exception even tho the Method might define it as List<Integer>
}
RawTypes allow you to pass the list to every Method that take a 'List' as argument. But you lose Typesafety, that may or may not be a problem in your case.
As long as the methods only access the elements they added you will have no problem.

Related

Checking for duplicates in an ArrayList with a Comparator

I want to implement a method, that removes duplicates from an ArrayList, by creating a new one without them. I already asked a question about the time complexity of this algorithm, but apparently it doesn't even work. I am pretty sure the error is the equality check but I am not very familiar with Comparators and ArrayLists so any help is appreciated. Here's the code:
public static <T> ArrayList<T> noDups(Comparator<T> cmp, ArrayList<T> l) {
ArrayList<T> noDups = new ArrayList<T>();
for(T o : l) {
if(!isAlreadyInArrayList2(cmp, o, l))
noDups.add(o);
}
return noDups;
}
With iterator:
public static <T> boolean isAlreadyInArrayList(Comparator<T> cmp, T o, ArrayList<T> l) {
Iterator<T> i = l.iterator();
if (o==null) {
while (i.hasNext())
if (i.next()==null)
return true;
} else {
while (i.hasNext())
if (cmp.compare(o, i.next()) == 0)
return true;
}
return false;
}
With a for loop:
public static <T> boolean isAlreadyInArrayList2(Comparator<T> cmp, T o, ArrayList<T> l) {
for(T obj : l) {
if (cmp.compare(o, obj) == 0)
return true;
}
return false;
}
Here is my test which produces an empty ArrayList:
public static void main(String[] args) {
Comparator<Integer> natural = Comparator.<Integer>naturalOrder();
ArrayList<Integer> test = new ArrayList<>(Arrays.asList(1, 5, 4, 2, 2, 0, 1, 4, 2));
System.out.println(test);
ArrayList<Integer> testWithoutDuplicates = noDups(natural,test);
System.out.println(testWithoutDuplicates);
}
You are passing the wrong List to your isAlreadyInArrayList2, change it to the following:
public static <T> ArrayList<T> noDups(Comparator<T> cmp, ArrayList<T> l) {
ArrayList<T> noDups = new ArrayList<T>();
for (T o : l) {
if (!isAlreadyInArrayList2(cmp, o, noDups))
noDups.add(o);
}
return noDups;
}

Predicate generic method

I wrote a Predicate code that takes any Object and tests it for the following conditions:
if Object type is String and contains "k" then it should return true.
if Object type is Integer and greater than 100 then it should return true.
if Object type is Employee which is class and having salary of employee greater than 60000, it should return true.
After writing that Predicate method I wrote the remove method that removes values from list according to Predicate method.
public class ConditionalRemove {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(Arrays.asList("ramesh", "kushal", "suresh", "kc"));
System.out.println(conditionalRemove(list));
}
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
ConditionCheck<T> cond = new ConditionCheck<>();
for (T t : list) {
if (cond.test(t)) {
list.remove(t);
}
}
return list;
}
static class ConditionCheck<T> implements Predicate<T> {
#Override
public boolean test(T t) {
if (t instanceof String) {
return (((String) t).contains("k"));
} else if (t instanceof Integer) {
return ((int) t > 100);
} else if (t instanceof Employee) {
return ((int) ((Employee) t).getSalary() < 60000);
}
return true;
}
}
}
After compiling this code I found Exception in thread "main" java.util.ConcurrentModificationException
The issue is you are updating the list when you are iterating over that. The issue can be fixed by updating code as
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
ConditionCheck<T> cond = new ConditionCheck<>();
Iterator it = list.iterator();
while(it.hasNext())
{
it.next();
if (cond.test(t)) {
it.remove();
}
}
return list;
}
Since you're using Java 8, a functional approach would be to create a new filtered list:
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
return list.stream()
.filter(new ConditionCheck<>())
.collect(Collectors.toList());
}
You can even replace the static inner class by just a method:
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
return list.stream()
.filter(ConditionalRemove::test)
.collect(Collectors.toList());
}
private static <T> boolean test(T t) {
// your predicate implementation...
}
Don't reinvent the wheel: Use Collection#removeIf():
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
list.removeIf(new ConditionCheck<>());
return list;
}
At one line, this is hardly worth the effort of creating a method to call... just make the single line call in-line:
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("ramesh", "kushal", "suresh", "kc"));
list.removeIf(new ConditionCheck<>());
System.out.println(list);
}
Some data structures throws the java.util.ConcurrentModificationException when you modify them during an iteration, in order to do that with success you need use a synchronized structure such as "CopyOnWriteArrayList", this is the java doc reference
Hope this can help you!
Regards.

Removing duplicates from List which uses generics

Problem : I have a utility function which takes in a generic list to remove duplicates, Now when I use it for List<String> the match should case insensitive. The code uses streams (Java 8+) and i want to keep it that way.
Note : code is in JAVA 8+
Code :
public static <T> List<T> removeDuplicates(List<T> inputList) {
List<T> result = null;
if (inputList != null && inputList.size() > 0) {
result = inputList.parallelStream().distinct().collect(Collectors.toList());
}
return result;
}
EG:
List<String> inputList = new ArrayList<String>();
inputList.add("a");
inputList.add("A");
inputList.add("abc");
inputList.add("ABC");
inputList.add("c");
When we call removeDuplicates(inputList) and print it
Values:
a
abc
c
I don't really care if it choose ABC over abc or A over a but it should be there only once.
Is there an elegant way of solving this issue without doing an instanceof check ?
If the caller knows the type of T at compile time, you can have it pass an optional Comparator<T> to the method, and filter out duplicates using a TreeSet:
public static <T> List<T> removeDuplicates(List<T> inputList) {
// null uses natural ordering
return removeDuplicates(inputList, null);
}
public static <T> List<T> removeDuplicates(List<T> inputList, Comparator<? super T> comparator) {
Set<T> set = new TreeSet<>(comparator);
set.addAll(inputList);
return new ArrayList<>(set);
}
public static void main(String[] args) {
System.out.println(removeDuplicates(Arrays.asList(1, 2, 2, 3)));
System.out.println(removeDuplicates(Arrays.asList("a", "b", "B", "c"), String.CASE_INSENSITIVE_ORDER));
}
Output:
[1, 2, 3]
[a, b, c]
You can extend your method to accept also a function to be applied in map on your stream.
This function will be generic with the same T, so this will solve the need for instanceof. In the String case insensitive example, the function will be String::toLowerCase.
public static <T> List<T> removeDuplicates(List<T> inputList, Function<T,T> function) {
List<T> result = null;
if (inputList != null && inputList.size() > 0) {
result = inputList.parallelStream()
.map(function)
.distinct()
.collect(Collectors.toList());
}
return result;
}
And if you want to keep the same API for the types that don't need it, just add this overload:
public static <T> List<T> removeDuplicates(List<T> inputList) {
return removeDuplicates(inputList, Function.identity());
}
If you want behavior that differs from the default equals behavior you can roll your own String:
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.stream.Collectors;
public class MyString {
private final String value;
public MyString(final String value) {
this.value = value;
}
#Override
public String toString() {
return value;
}
public String getValue() {
return value;
}
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyString myString = (MyString) o;
return StringUtils.equalsIgnoreCase(myString.value, value);
}
#Override
public int hashCode() {
return value.toUpperCase().hashCode();
}
public static void main(String... args) {
// args = {aa AA aA bb Bb cc bb CC}
System.out.println(Arrays.stream(args).map(MyString::new).collect(Collectors.toSet()));
// prints: [aa, bb, cc]
}
}

Get object class and use as argument for generic constructor

I'm sure this has been asked before but I am new to Java and unfamiliar with the exact terminology I'm looking for.
I have a BST class:
public class BinarySearchTree<T extends Comparable> {
/* ... */
}
Then I wrote some tests for it:
public static void main(String[] args) {
Integer[] integerItems = {1, 7, 8, 2, -1, -10, 100, 12, 32};
String[] stringItems = {"jungi", "phil", "bob", "leslie", "tyler", "clarence"};
Comparable[][] comparableLists = {integerItems, stringItems};
for (Comparable[] list : comparableLists) {
BinarySearchTree<>...
}
}
I am confused at this step. How can I recover the types (String[], Integer[], etc.) from the list and use them as an argument? I want to have something like this:
for (Comparable[] list : comparableLists) {
BinarySearchTree<typeOf(list)> root = new BinarySearchTree<typeOf(list)>();
/* ... tests ... */
}
One option I found here was just to list out all the possible supported types. This seems really silly because I don't know all the supported types. Maybe these types will change, etc. but I had it hard-coded.
How can I deal with this best?
EDIT:
So just to be a little more specific, here is the BST implementation:
public class BinarySearchTree<T extends Comparable> {
private T value;
private BinarySearchTree<T> leftChild;
private BinarySearchTree<T> rightChild;
public BinarySearchTree() {
}
public BinarySearchTree(T v) {
value = v;
createChildren();
}
public void createChildren() {
leftChild = new BinarySearchTree<T>();
rightChild = new BinarySearchTree<T>();
}
public void insert(T v) {
if (value == null) {
value = v;
createChildren();
} else if (v < value) {
leftChild.insert(v);
}
rightChild.insert(v);
}
public boolean valueExists(T v) {
if (value == null) {
return false;
} else if (value == v) {
return true;
} else if (v < value) {
return leftChild.valueExists(v);
}
return rightChild.valueExists(v);
}
public String toString() {
String bstStringBuilder = "";
if (value == null) {
return "";
}
bstStringBuilder += leftChild + " ";
bstStringBuilder += value + " ";
bstStringBuilder += rightChild;
return bstStringBuilder;
}
}
If I use #OldCurmudgeon's suggestion the main() looks like this:
public static void main(String[] args) {
Integer[] integerItems = {1, 7, 8, 2, -1, -10, 100, 12, 32};
String[] stringItems = {"jungi", "phil", "bob", "leslie", "tyler", "clarence"};
Comparable[][] comparableLists = {integerItems, stringItems};
for (Comparable[] list : comparableLists) {
BinarySearchTree<Comparable> root = new BinarySearchTree<Comparable>();
for (Comparable item : list) {
root.insert(item);
}
System.out.println(root);
}
}
This produces the following compiler error:
BinarySearchTree.java:26: error: bad operand types for binary operator '<'
} else if (v < value) {
^
first type: T
second type: T
where T is a type-variable:
T extends Comparable declared in class BinarySearchTree
BinarySearchTree.java:37: error: bad operand types for binary operator '<'
} else if (v < value) {
^
first type: T
second type: T
where T is a type-variable:
T extends Comparable declared in class BinarySearchTree
2 errors
Perhaps this is more helpful?
This is the point of using a common interface to multiple types of objects. Use Comparable.
for (Comparable[] list : comparableLists) {
BinarySearchTree<Comparable> tree = ...
Generic method to the rescue!
Look:
class BinarySearchTree<T extends Comparable<T>> {
void put(T item) {}
}
class Test {
public static <T extends Comparable<T>> void putIntoATree(BinarySearchTree<T> tree, T[] items) {
for (T item : items)
tree.put(item);
}
public static void main(String[] args) {
Integer[] integerItems = {1, 7, 8, 2, -1, -10, 100, 12, 32};
BinarySearchTree<Integer> integerTree = new BinarySearchTree<>();
putIntoATree(integerTree, integerItems);
String[] stringItems = {"jungi", "phil", "bob", "leslie", "tyler", "clarence"};
BinarySearchTree<String> stringTree = new BinarySearchTree<>();
putIntoATree(stringTree, stringItems);
}
}
Presence of arrays holding generified types, as in your example, usually forces a cast somewhere (or leaves 'unchecked' warning). That's why I've avoided using an array in my example.
One way to eliminate a cast and make code type-safe is to wrap array into a generic class which will make javac happy:
class BinarySearchTree<T extends Comparable<T>> {
void put(T item) {}
}
class Test {
public static <T extends Comparable<T>> void putIntoATree(BinarySearchTree<T> tree, T[] items) {
for (T item : items)
tree.put(item);
}
private static class ArrayAndATree<T extends Comparable<T>> {
final T[] contents;
final BinarySearchTree<T> aTree;
private ArrayAndATree(T[] contents) {
this.contents = contents;
aTree = new BinarySearchTree<>();
}
}
public static void main(String[] args) {
Integer[] integerItems = {1, 7, 8, 2, -1, -10, 100, 12, 32};
String[] stringItems = {"jungi", "phil", "bob", "leslie", "tyler", "clarence"};
ArrayAndATree<Integer> integerArrayAndATree = new ArrayAndATree<>(integerItems);
ArrayAndATree<String> stringArrayAndATree = new ArrayAndATree<>(stringItems);
ArrayAndATree<?> taggedArrays[] = {integerArrayAndATree, stringArrayAndATree};
for (ArrayAndATree<?> a : taggedArrays) {
putIntoATree(a.aTree, a.contents);
process(a);
}
}
private static <T extends Comparable<T>> void process(ArrayAndATree<T> a) {
putIntoATree(a.aTree, a.contents);
}
}
You don't need to know the concrete type at runtime, generics are applied at compile.
So in your code you can do this:
public static void main(String[] args) {
Integer[] integerItems = {1, 7, 8, 2, -1, -10, 100, 12, 32};
String[] stringItems = {"jungi", "phil", "bob", "leslie", "tyler", "clarence"};
Comparable[][] comparableLists = {integerItems, stringItems};
for (Comparable[] list : comparableLists) {
BinarySearchTree b = BinarySearchTree.class.newInstance();
for (Object element : list) {
b.add(element); // or whatever method you want to use
}
}
}
You just need to be careful to not use the wrong type now as the compiler isn't giving you any guidance

Combine multiple Collections into a single logical Collection?

Assume, I have a constant number of collections (e.g. 3 ArrayLists) as members of a class. Now, I want to expose all the elements to other classes so they can simply iterate over all elements (ideally, read only).
I'm using guava collections and I wonder how I could use guava iterables/iterators to generate a logical view on the internal collections without making temporary copies.
With Guava, you can use Iterables.concat(Iterable<T> ...), it creates a live view of all the iterables, concatenated into one (if you change the iterables, the concatenated version also changes). Then wrap the concatenated iterable with Iterables.unmodifiableIterable(Iterable<T>) (I hadn't seen the read-only requirement earlier).
From the Iterables.concat( .. ) JavaDocs:
Combines multiple iterables into a
single iterable. The returned iterable
has an iterator that traverses the
elements of each iterable in inputs.
The input iterators are not polled
until necessary. The returned
iterable's iterator supports remove()
when the corresponding input iterator
supports it.
While this doesn't explicitly say that this is a live view, the last sentence implies that it is (supporting the Iterator.remove() method only if the backing iterator supports it is not possible unless using a live view)
Sample Code:
final List<Integer> first = Lists.newArrayList(1, 2, 3);
final List<Integer> second = Lists.newArrayList(4, 5, 6);
final List<Integer> third = Lists.newArrayList(7, 8, 9);
final Iterable<Integer> all =
Iterables.unmodifiableIterable(
Iterables.concat(first, second, third));
System.out.println(all);
third.add(9999999);
System.out.println(all);
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 9999999]
Edit:
By Request from Damian, here's a similar method that returns a live Collection View
public final class CollectionsX {
static class JoinedCollectionView<E> implements Collection<E> {
private final Collection<? extends E>[] items;
public JoinedCollectionView(final Collection<? extends E>[] items) {
this.items = items;
}
#Override
public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
#Override
public void clear() {
for (final Collection<? extends E> coll : items) {
coll.clear();
}
}
#Override
public boolean contains(final Object o) {
throw new UnsupportedOperationException();
}
#Override
public boolean containsAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean isEmpty() {
return !iterator().hasNext();
}
#Override
public Iterator<E> iterator() {
return Iterables.concat(items).iterator();
}
#Override
public boolean remove(final Object o) {
throw new UnsupportedOperationException();
}
#Override
public boolean removeAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
#Override
public boolean retainAll(final Collection<?> c) {
throw new UnsupportedOperationException();
}
#Override
public int size() {
int ct = 0;
for (final Collection<? extends E> coll : items) {
ct += coll.size();
}
return ct;
}
#Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
#Override
public <T> T[] toArray(T[] a) {
throw new UnsupportedOperationException();
}
#Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
}
/**
* Returns a live aggregated collection view of the collections passed in.
* <p>
* All methods except {#link Collection#size()}, {#link Collection#clear()},
* {#link Collection#isEmpty()} and {#link Iterable#iterator()}
* throw {#link UnsupportedOperationException} in the returned Collection.
* <p>
* None of the above methods is thread safe (nor would there be an easy way
* of making them).
*/
public static <T> Collection<T> combine(
final Collection<? extends T>... items) {
return new JoinedCollectionView<T>(items);
}
private CollectionsX() {
}
}
Plain Java 8 solutions using a Stream.
Constant number
Assuming private Collection<T> c, c2, c3.
One solution:
public Stream<T> stream() {
return Stream.concat(Stream.concat(c.stream(), c2.stream()), c3.stream());
}
Another solution:
public Stream<T> stream() {
return Stream.of(c, c2, c3).flatMap(Collection::stream);
}
Variable number
Assuming private Collection<Collection<T>> cs:
public Stream<T> stream() {
return cs.stream().flatMap(Collection::stream);
}
If you're using at least Java 8, see my other answer.
If you're already using Google Guava, see Sean Patrick Floyd's answer.
If you're stuck at Java 7 and don't want to include Google Guava, you can write your own (read-only) Iterables.concat() using no more than Iterable and Iterator:
Constant number
public static <E> Iterable<E> concat(final Iterable<? extends E> iterable1,
final Iterable<? extends E> iterable2) {
return new Iterable<E>() {
#Override
public Iterator<E> iterator() {
return new Iterator<E>() {
final Iterator<? extends E> iterator1 = iterable1.iterator();
final Iterator<? extends E> iterator2 = iterable2.iterator();
#Override
public boolean hasNext() {
return iterator1.hasNext() || iterator2.hasNext();
}
#Override
public E next() {
return iterator1.hasNext() ? iterator1.next() : iterator2.next();
}
};
}
};
}
Variable number
#SafeVarargs
public static <E> Iterable<E> concat(final Iterable<? extends E>... iterables) {
return concat(Arrays.asList(iterables));
}
public static <E> Iterable<E> concat(final Iterable<Iterable<? extends E>> iterables) {
return new Iterable<E>() {
final Iterator<Iterable<? extends E>> iterablesIterator = iterables.iterator();
#Override
public Iterator<E> iterator() {
return !iterablesIterator.hasNext() ? Collections.emptyIterator()
: new Iterator<E>() {
Iterator<? extends E> iterableIterator = nextIterator();
#Override
public boolean hasNext() {
return iterableIterator.hasNext();
}
#Override
public E next() {
final E next = iterableIterator.next();
findNext();
return next;
}
Iterator<? extends E> nextIterator() {
return iterablesIterator.next().iterator();
}
Iterator<E> findNext() {
while (!iterableIterator.hasNext()) {
if (!iterablesIterator.hasNext()) {
break;
}
iterableIterator = nextIterator();
}
return this;
}
}.findNext();
}
};
}
You could create a new List and addAll() of your other Lists to it. Then return an unmodifiable list with Collections.unmodifiableList().
Here is my solution for that:
EDIT - changed code a little bit
public static <E> Iterable<E> concat(final Iterable<? extends E> list1, Iterable<? extends E> list2)
{
return new Iterable<E>()
{
public Iterator<E> iterator()
{
return new Iterator<E>()
{
protected Iterator<? extends E> listIterator = list1.iterator();
protected Boolean checkedHasNext;
protected E nextValue;
private boolean startTheSecond;
public void theNext()
{
if (listIterator.hasNext())
{
checkedHasNext = true;
nextValue = listIterator.next();
}
else if (startTheSecond)
checkedHasNext = false;
else
{
startTheSecond = true;
listIterator = list2.iterator();
theNext();
}
}
public boolean hasNext()
{
if (checkedHasNext == null)
theNext();
return checkedHasNext;
}
public E next()
{
if (!hasNext())
throw new NoSuchElementException();
checkedHasNext = null;
return nextValue;
}
public void remove()
{
listIterator.remove();
}
};
}
};
}

Categories

Resources