Best way to make conjunctions and disjunctions over a collection in java - java

What's the most efficient way to create an and and ormethods over two ArrayLists?
//not coded in java
HashSet<String> first = {"hello", "to", "you"}
HashSet<String> second = {"hello", "to", "me"}
HashSet<String> and = and(first, second) = {"hello", "to"}
HashSet<String> or = or(first, second) = {"hello", "to", "you", "me"}
I need to implement those two methods (pretty easy) but I would need to do it efficiently, because I will and and or over collections with hundreds of Strings. Any tip?

To avoid confusion I'll call the methods intersection and union as the meanings of AND and OR are a little ambiguous.
There is a retainAll method on Set that will do the job of the intersection. You need to take heed of my caveats in another answer (of mine) on SO.
There is an addAll method on Collection that will do the job of union.
Here is an example:
public static void main(String[] args) throws Exception {
final Set<String> first = new HashSet<>(Arrays.asList("hello", "to", "you"));
final Set<String> second = new HashSet<>(Arrays.asList("hello", "to", "me"));
System.out.println(intersection(first, second));
System.out.println(union(first, second));
}
public static Set<String> intersection(final Set<String> first, final Set<String> second) {
final Set<String> copy = new HashSet<>(first);
copy.retainAll(second);
return copy;
}
public static Set<String> union(final Set<String> first, final Set<String> second) {
final Set<String> copy = new HashSet<>(first);
copy.addAll(second);
return copy;
}
Note use usage of Set rather than List. This serves two purposes:
Set has O(1) contains as opposed to O(n) for a List. This helps in the intersection case.
Set guarantees uniqueness. This helps in the union case.
Also note that I copy the collections before carrying out the operations - as Java passes references by value not copying would cause the original collection to be changed.
If you need to preserve order, you will need to use a LinkedHashSet as a HashSet has no order.

You can use Set for that, just need to cast.
List<String> list = new ArrayList<String>();
Set<String> set = new HashSet<String>(list);
And use its methods to do that

You want to make the intersection and the union of two ArrayLists.
I think that this is a duplicate question.
I suggest to take a look at this thread:
Intersection and union of ArrayLists in Java

Related

Java - Add one element to an immutable list

I need an immutable list where I can get derive a second immutable list preserving all elements of the previous list plus an additional element in Java (without additional libraries).
Note: This question is similar to What is an efficient and elegant way to add a single element to an immutable set? but I need a list and don't have Guava.
What I have tried so far:
var list = List.of(someArrayOfInitialElements);
var newList = Stream.concat(list.stream(), Stream.of(elementToAppend))
.collect(CollectorsCollectors.toUnmodifiableList());
That would work but creating a stream and copying elements one by one seems inefficient to me. You could basically bulk copy memory given that List.of() stores data in a field-based or array-based data structure.
Is there a more efficient solution than using streams? A better data structure in the Java standard library that I am missing?
I would create a new ArrayList append the element and then return that as an unmodifiable list. Something like,
private static <T> List<T> appendOne(List<T> al, T t) {
List<T> bl = new ArrayList<>(al);
bl.add(t);
return Collections.unmodifiableList(bl);
}
And to test it
public static void main(String[] args) {
List<String> al = appendOne(new ArrayList<>(), "1");
List<String> bl = appendOne(al, "2");
System.out.println(bl);
}
I get (unsurprisingly):
[1, 2]
See this code run at IdeOne.com.
The Answer by Frisch is correct, and should be accepted. One further noteā€¦
Calling Collections.unmodifiableList produces a collection that is a view onto the original mutable list. So a modification to the original list will "bleed through" to the not-so-immutable second list.
This issue does not apply to the correct code shown in that Answer, because the new ArrayList object deliberately goes out-of-scope. Therefore that new list cannot be accessed for modification. But in other coding scenarios, this issue could be a concern.
List.copyOf
If you want an independent and truly immutable second list, use List.copyOf in Java 10+. This returns an unmodifiable list.
return List.copyOf( bl ) ;
Both answers are great, I would create a bit more generic solution:
private static <T> List<T> append(final List<T> al, final T... ts) {
final List<T> bl = new ArrayList<>(al);
for (final T t : ts) {
bl.add(t);
}
return List.copyOf(bl);
}
It can be used exactly like previous answer:
List<String> al = append(new ArrayList<>(), "1");
List<String> bl = append(al, "2");
System.out.println(bl);
But also slightly more efficient:
List<String> bl = append(new ArrayList<>(), "1", "2");
System.out.println(bl);

Why Collection<String>s= new TreeSet<>(), TreeSet<String>s= new TreeSet<>() and Set<String>s = new TreeSet<>() produces same result?

I don't understand why all three are same. Collection is an interface and TreeSet and Set are Classes.
public static void main(String[] args) {
String[] text = {"i", "came", "i", "saw", "i", "left"};
Set<String> s = new TreeSet<>(); // output = 4 distinct words: [came, i, left, saw]
// Collection<String>s = new TreeSet<>(); // output = 4 distinct words: [came, i, left, saw]
// TreeSet<String>s = new TreeSet<>(); // output = 4 distinct words: [came, i, left, saw]
for(String a: text)
s.add(a);
System.out.println(s.size()+" distinct words: "+s);
}
The result is same because all three are using the same implementation class which is TreeSet. Since Set and Collection are the parent interfaces of the TreeSet class you can point to a TreeSet implementation using the their reference. This is how polymorphism works in OOP.
But although the reference is of Set or Collection the implementation is an instance of the class TreeSet which does not allow duplicates and sorts the elements as per their definition of the Comparable interface (natural ordering). So in all three you are seeing that the duplicates are eliminated.
This is because you are using the same implementation in all three cases and assigning to its supertypes. Please look at polymorphism.

How to merge multiple (arbitrary number of) Set<String> into one in Java?

I have multiple Set<String> that I need to merge into one Set<String>. How do I do this operation in Java? Note, I am using using the guava API as best as I can to help out. For example, I have 3 classes as follows.
public class One {
public static Set<String> SET = Sets.newHashSet("a","b","c");
}
public class Two {
public static Set<String> SET = Sets.newHashSet("a","d","e","f");
}
public class Three {
public static Set<String> SET = Sets.newHashSet("w","x","y","f");
}
Now, I need to merge any combination of these sets into one. For, example, I may need to merge
One.SET + Two.SET + Three.SET into one to produce { "a","b","c","d","e","f","w","x","y" },
One.SET + Three.SET into one to produce { "a","b","c","w","x","y","f" },
Two.SET + Three.SET into one to produce { "a","d","e","f","w","x","y" },
and so on
I created a method to merge an array of sets, Set<String>[], but that doesn't work (explained here by this SO post Creating an array of Sets in Java). Here's the code to merge. It works (compiles).
public static Set<String> immutableSetOf(Set<String>[] sets) {
Set<String> set = new HashSet<String>();
for(Set<String> s : sets) {
set.addAll(s);
}
return ImmutableSet.copyOf(set);
}
Here's the calling code; it doesn't work (doesn't compile).
Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });
So, I modified my merging method to operate on List<Set<String>> instead of Set<String>[]. Only the argument type changed, but I put it here for completeness.
public static Set<String> immutableSetOf(List<Set<String>> sets) {
Set<String> set = new HashSet<String>();
for(Set<String> s : sets) {
set.addAll(s);
}
return ImmutableSet.copyOf(set);
}
So, now my calling code looks like the following.
Set<String> set = Utils.immutableSetOf(
Lists.newArrayList(
One.SET, Two.SET));
This code does not compile, since Lists.newArrayList(...) is returning Set<String> and not List<Set<String>>. The method Lists.newArrayList(...) is overloaded, and the signature of the method that is used when I pass in sets is, List.newArrayList(Iterable<? extends E> elements).
So, the question is, how do I define a method to merge an arbitrary number of Set<String> while considering the calling code? I note that the compilation problems are on the calling code (not the merging method), but perhaps the solution also relates to the merging code?
Update: I also tried varargs but that produces its own warning (Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning?). The merging method signature is now the following.
public static Set<String> immutableSetOf(Set<String>... sets)
The calling code is now the following, and I get "Type safety: A generic array of Set is created for a varargs parameter".
Set<String> set = Utils.immutableSetOf(One.SET, Two.SET);
Update: For the accepted answer, I did the following.
#SuppressWarnings("unchecked")
Set<String> set = Utils.immutableSetOf(Set[] { One.SET, Two.SET });
Recommend com.google.common.collect.Sets#union(set1, set2) to get the merge instead of Set.addAll under hood, since Guava is already in your dependencies.
Reason: it's view which is memory effective, and also unmodifiable.
plus: I should have post it as a comment, sorry.
How about creating a collection of your input sets, then using flatMap?
Set<String> allElements = ImmutableSet.of(
One.SET,
Two.SET,
Three.SET
).stream().flatMap(Collection::stream).collect(Collectors.toSet());
In your previous attempt, you had written
Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });
This does not work because, generics array creation is not allowed in java
Try this:
#SuppressWarnings("unchecked")
Set<String>[] mySet = new Set[] { One.SET, Two.SET };
Set<String> set = Utils.immutableSetOf(mySet);
The reason this works is because, we create a new Set[] without specifying the generics type and assign it to the reference Set[] mySet (since this is an unchecked operation, we have to add #SuppressWarnings("unchecked") to it)
You could use:
Set<String> set = immutableSetOf(Arrays.asList(One.SET, Two.SET));
Using your definition of immutableSetOf(List<Set<String>> sets). No warnings or suppressed warnings.
I'm thinking that FluentIterable (in 18.0 and above) can help here, specifically the append method.
With that, we need to define a convenient helper method - and yes, we're going to use varargs for this.
public <T extends Comparable<T>> Set<T> mergeSets(Set<T> initial,
Set<T>... rest) {
FluentIterable<T> result = FluentIterable.from(initial);
for(Set<T> set : rest) {
result = result.append(set);
}
return new TreeSet<>(result.toSet());
}
The resultant set here is in natural order. If you don't want a TreeSet and you don't want to mutate your collection afterwards, then omit the new TreeSet<> piece, and loosen the bound on T.

What does List<Set<Integer>> mean?

One of the questions I have been given asks:
All the lines should be stored in an object of
type List<Set<Integer>>.
How do you write this in Java, as in how do you initialise this list? I've never seen this before.
Please provide a link to an explanation as i'm not sure what this is called in Java so have no idea about how to learn about it. Thank You.
Its a List of Sets where each Set can hold only Integers.
Set<Integer> singlesSet = new HashSet<>();
singlesSet.add(1);
singlesSet.add(2);
Set<Integer> tensSet = new HashSet<>();
tensSet.add(10);
tensSet.add(20);
List<Set<Integer>> list = new ArrayList<>();
list.add(singlesSet);
list.add(tensSet);
System.out.println(list);
Example of usages of Set and List. Note that elements in a TreeSet are always sorted.
List<Set<Integer>> listofsets = new ArrayList<Set<Integer>>();
Set<Integer> set1 = new TreeSet<Integer>();
set1.add(1);
set1.add(2);
Set<Integer> set2 = new TreeSet<Integer>();
set2.add(6);
set2.add(4);
listofsets.add(set);
// listofsets = {{1,2}, {4,6}}
Like this List<Set<Integer>> yourList = new ArrayList<Set<Integer>>();?
You may want to take a look at https://docs.oracle.com/javase/7/docs/api/java/util/List.html
The short way:
List<Set<Integer>> list = new ArrayList<Set<Integer>>();
Set<Integer> set = new HashSet<Integer>();
list.add(set);
set.add(1);
set.add(2);
....
What is the difference between Set and List?
In Java, the List interface represents an abstract list of things. Any class the implements List (for example, LinkedList) must implement its methods and behave according to its contract.
You can essentially think of it as an array, but keep in mind that arrays are only one kind of list, and that implementations of List do no have to use arrays internally.
The Set also represents a collection of elements, but with no relationship or connection between them. Visually, you can think of a set as a sort of bag of things. You can add and remove things from the bag, but none of the items need to be related.
An Integer, of course, is just an object wrapper around Java's int primitive.
As such, a List<Set<Integer>> object would be similar to a two-dimensional array, only without a defined order in the second dimension.
You would initialize a List<Set<Integer>> as follows:
List<Set<Integer>> myList = new ArrayList<HashSet<Integer>>();
Where ArrayList and HashSet can be any classes that implement List and Set, respectively.

Obtaining array element differences in Java

Say I have two string arrays:
String[] first = new String[]{"12","23","44","67"};
String[] second= new String[]{"12","22","46","67"};
I searched for a function like PHP's array_diff which will give me the difference of these two arrays like this:
{"23","44"}
Is there a in-built function for this operation, or should I create a for loop and check for the differences ?
You can create two Sets from these arrays, like:
List<String> firstList = Arrays.asList(first);
List<String> secondList = Arrays.asList(second);
Set<String> firstSet = new HashSet<String>(first);
Set<String> secondSet = new HashSet<String>(second);
and then use the removeAll method:
firstSet.removeAll(secondList);
secondSet.removeAll(firstList);
so now firstList contains all the elements that are only available in the first array and secondList only the elements available in the second array.
A set that will contain only the elements available in one of the sets (without elements available in both sets) can be created using:
new HashSet<String>(firstSet).addAll(secondSet);
Guava's Sets class has a difference method.
so
Set<String> diff = Sets.difference(newHashSet(first), newHashSet(second));
PHP arrays are not arrays at all, that's why there is such weird method for diff.
If you want difference between two sets (A - B) in mathematical sense, then
1) use sets
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
2) use difference method (contains all elements in set1 that not in set2)
set1.removeAll(set2)
Note, this is assymetric difference.

Categories