Considering the code below and the fact that the 4 HashSets are populated elsewhere.
My aim is to contain all element(s) that are common in all 4 HashSets.
My question is that first of all, am I doing it right? Secondly, if I'm doing it right, is there a better way to do it? If not, then what solution do I have for this problem?
static Set<String> one=new HashSet<>();
static Set<String> two=new HashSet<>();
static Set<String> three=new HashSet<>();
static Set<String> four=new HashSet<>();
private static void createIntersectionQrels() {
ArrayList<String> temp = new ArrayList<>();
Set<String> interQrels = new HashSet<>();
temp.addAll(one);
one.retainAll(two);
interQrels.addAll(one);
one.addAll(temp);
one.retainAll(three);
interQrels.addAll(one);
one.addAll(temp);
one.retainAll(four);
interQrels.addAll(one);
one.addAll(temp);
interQrels.retainAll(two);
interQrels.retainAll(three);
interQrels.retainAll(four);
}
I think you can simply can call retainAll() on the first set, using the second, third, and fourth sets as parameters:
private static Set<String> getIntersectionSet() {
// create a deep copy of one (in case you don't wish to modify it)
Set<String> interQrels = new HashSet<>(one);
interQrels.retainAll(two); // intersection with two (and one)
interQrels.retainAll(three); // intersection with three (and two, one)
interQrels.retainAll(four); // intersection four (and three, two, one)
return interQrels;
}
I'm a bit new to Java 8, but this seems pretty readable:
Set<String> intersection = one.stream()
.filter(two::contains)
.filter(three::contains)
.filter(four::contains)
.collect(Collectors.toSet());
Here's a quick Junit test to try out:
#Test
public void testIntersectionBetweenSets() {
Collection<String> one = new HashSet<>(4);
one.add("Larry");
one.add("Mark");
one.add("Henry");
one.add("Andrew");
Set<String> two = new HashSet<>(2);
two.add("Mark");
two.add("Andrew");
Set<String> three = new HashSet<>(3);
three.add("Mark");
three.add("Mary");
three.add("Andrew");
Set<String> four = new HashSet<>(3);
four.add("Mark");
four.add("John");
four.add("Andrew");
Set<String> intersection = one.stream()
.filter(two::contains)
.filter(three::contains)
.filter(four::contains)
.collect(Collectors.toSet());
Collection<String> expected = new HashSet<>(2);
expected.add("Andrew");
expected.add("Mark");
Assert.assertEquals(expected, intersection);
}
I would think the best way to handle this is with Groovy. I know you didn't ask for groovy, but anytime I can convert all that code into one line, it's hard to resist.
println one.intersect(two).intersect(three).intersect(four)
Related
I created two sets:
public static Set<String> COMMON_ENGLISH_WORDS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
public static Set<String> NON_ENGLISH_WORDS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
I kept all my common english words arround 58000 and non english word arround 1700 in two files seperately. And I am loading from file and asigning to above two variables. That assigment is happening properly that I check by debug.
public static void finalNonEnglishWords(){
ToolsConstants.COMMON_ENGLISH_WORDS = CSVFileUtil.readCSVToTreeSet(ToolsConstants.COMMON_ENGLISH_WORDS_FILE);
ToolsConstants.NON_ENGLISH_WORDS = CSVFileUtil.readCSVToTreeSet(ToolsConstants.NON_ENGLISH_WORDS_FILE);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
ToolsConstants.NON_ENGLISH_WORDS.removeAll(ToolsConstants.COMMON_ENGLISH_WORDS);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
}
But it not removing.
I am seeing same number in output.
But I saw both files, there are some common words.
I did the same sample with just 7 elements but it working perfectly. And fallowed the same way only different is number of elements in collection.
public static void removeAllDemo(){
List<String> list1 = new ArrayList<>(
Arrays.asList("BOB", "Joe", "john", "MARK","MARk", "dave", "Bill")
);
List<String> list2 = Arrays.asList("JOE", "MARK", "DAVE", "Ravi");
// Add all values of list1 in a case insensitive collection
Set<String> set1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set1.addAll(list1);
// Add all values of list2 in a case insensitive collection
Set<String> set2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set2.addAll(list2);
// Remove all common Strings ignoring case
System.out.println(set1);
set1.removeAll(set2);
System.out.println(set1);
// Keep in list1 only the remaining Strings ignoring case
list1.retainAll(set1);
}
So generally you should apply the same approach as that implemented in the demo method:
appropriate empty sets with the case-insensitive order are already created
use addAll to populate the sets
remove duplicates from the set of non-English words
public static void finalNonEnglishWords() {
ToolsConstants.COMMON_ENGLISH_WORDS.addAll(
CSVFileUtil.readCSVToTreeSet(ToolsConstants.COMMON_ENGLISH_WORDS_FILE)
);
ToolsConstants.NON_ENGLISH_WORDS.addAll(
CSVFileUtil.readCSVToTreeSet(ToolsConstants.NON_ENGLISH_WORDS_FILE)
);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
ToolsConstants.NON_ENGLISH_WORDS.removeAll(
ToolsConstants.COMMON_ENGLISH_WORDS
);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
}
I'm trying to do a Java problem that's noted in the textbook "Building Java Programs" 4th Ed. The problem is number 11 in Chapter 11, Page 751:
Write a method called symmetricSetDifference that accepts two sets as parameters and returns a new Set containing their symmetric difference(that is, the set of elements contained in either of the two sets, but not in both) For an example: The difference between the sets[1,4,7,9] and [2,4,5,6,7] is [1,2,5,6,9].
The symmetricSetDifference method:
public static Set<Integer>symmetricSetDifference(Set<Integer>list1, Set<Integer>list2) {
Set<Integer>set1 = new HashSet<>();
set1.add(1);
set1.add(4);
set1.add(7);
set1.add(9);
Set<Integer>set2 = new HashSet<>();
set2.add(2);
set2.add(4);
set2.add(5);
set2.add(6);
set2.add(7);
Set<Integer>diff = new HashSet<>(set1);
diff.addAll(set2);
Set<Integer>curr = new HashSet<>(set1);
curr.retainAll(set2);
diff.removeAll(curr);
System.out.println(diff);
return diff;
}
This is the main class. It's in a separate file:
public class TestPointClass {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
newSet = MyUtils.symmetricSetDifference(List1, List2);
}
The problem is that I get an "illegal expression" error along with a series of "cannot find the identifier errors. I was wondering if anyone has any pointers on what I can do? (I've checked for any duplicate issues and could not find anything similar, thus apologies in advance if there were)
do the setup of your both Sets (set1,set2) outside the method and add them as parameter:
Set<Integer>set1 = new HashSet<>();
set1.add(1);
...
Set<Integer>set2 = new HashSet<>();
set2.add(2);
...
MyUtils.symmetricSetDifference(set1 , set2 );
if you want to use the return type you have to do it like that:
Set<Integer> result = MyUtils.symmetricSetDifference(set1 , set2 );
I recently had this problem.
This is my code
for(int i=1; i<=repeticiones;i++){
posiblesComunes.removeIf(p->!periodos.get(i).contains(p));
}
periodos is a List(Set(String)), posiblesComunes is a Set(String)
What I need to do is get only the strings that are in all of the Set(String) in periodos.
I tried to do that using a for, but I get the next message:
Local variable i defined in an enclosing scope must be final or effectively final
Is there any way to fix that? Or another way to get those elements?
Thanks!
edit:
Just an example
periodos = {("1-1-16","6-12-16"),("1-1-16","2-8-15"),("3-7-08","1-1-16")}
What I need to get is "1-1-16", the one in common.
edit2:
an example of periodosComunes (for the for loop):
periodosComunes = ("1-1-16","6-2-16")
You can not access the local variable i from a lambda expression, because it gets modified during the loop. The simplest fix is to capture the current value of i in another immutable variable:
for(int i=1; i<=repeticiones;i++) {
int finalI = i;
posiblesComunes.removeIf(p -> !periodos.get(finalI).contains(p));
}
Note that the variable of a for-each loop does not have this problem:
for(Set<String> set: periodos.subList(1, repeticiones))
posiblesComunes.removeIf(p -> !set.contains(p));
but in the end, you are overusing Java 8 features here. This operation can be done with the original Collections API since Java 2:
for(Set<String> set: periodos.subList(1, repeticiones))
posiblesComunes.retainAll(set);
which would also work with your original loop:
for(int i=1; i<=repeticiones; i++)
posiblesComunes.retainAll(periodos.get(i));
Here, you could also add a short-cut, as the set never grows, so if there are no common elements, you can stop once the set became empty
for(int i=1; i<=repeticiones && !posiblesComunes.isEmpty(); i++)
posiblesComunes.retainAll(periodos.get(i));
How about this:
Set<String> periodosComunes = Set.of("1-1-16","6-2-16");
List<Set<String>> periodos = List.of(
Set.of("1-1-16","6-12-16"),
Set.of("1-1-16","2-8-15"),
Set.of("3-7-08","1-1-16")
);
List<String> result = periodosComunes.stream()
.filter(x -> periodos.stream()
.allMatch(y -> y.contains(x))
)
.collect(Collectors.toList());
// result = [1-1-16]
I used collection literals from Java 9 to save me some typing, but that's irrelevant to the solution.
Not all lists provide iterators with the ability to remove their items; however, if you select the right kind of list, this is built into the Iterator interface.
Iterator i = list.iterator();
while (i.hasNext()) {
if (i.next().equals(bad)) {
i.remove();
}
}
The simplicity of the solution is enough you might consider skipping the Stream based approach, and unlike some kinds of modifications, Iterator removal will not throw a ConcurrentModificationException.
That should work (if I understand well your needs):
import java.util.stream.*;
import java.util.*;
public class P {
public static void main(String []a) {
// Init.
Set<String> periodosComunes = new HashSet<>(); periodosComunes.add("1-1-16"); periodosComunes.add("6-2-16");
Set<String> s1 = new HashSet<>(); s1.add("1-1-16"); s1.add("6-12-16");
Set<String> s2 = new HashSet<>(); s2.add("1-1-16"); s2.add("2-8-15");
Set<String> s3 = new HashSet<>(); s3.add("1-1-16"); s3.add("3-7-08");
List<Set<String>> periodos = new ArrayList<>(); periodos.add(s1); periodos.add(s2); periodos.add(s3);
// Computes the set of commons...
Set<String> r = periodosComunes.stream().filter(p->periodos.stream().allMatch(s->s.contains(p))).collect(Collectors.toSet());
System.out.println(r);
}
}
The set of common periods initially in periodosCommunes is in r. Now you can use that set to remove appropriate ones in the original set if needed:
periodosComunes.removeIf(s->!r.contains(s));
I am currently working on one of the usecases where you are given 6 strings which has 3 oldValues and 3 newValues like given below:
String oldFirstName = "Yogend"
String oldLastName = "Jos"
String oldUserName = "YNJos"
String newFirstName = "Yogendra"
String newLastName ="Joshi"
String newUserName = "YNJoshi"
now what I basically want to do is compare each of the oldValue with its corresponding new value and return true if they are not equal i.e
if(!oldFirstName.equalsIgnoreCase(newFirstName)) {
return true;
}
Now, since I am having 3 fields and it could very well happen that in future we might have more Strings with old and new value I am looking for an optimum solution which could work in all cases no matter how many old and new values are added and without having gazillions of if else clauses.
One possibility I thought was of having Old values as OldArrayList and new values as newArraylist and then use removeAll where it would remove the duplicate values but that is not working in some cases.
Can anyone on stack help me out with some pointers on how to optimum way get this done.
Thanks,
Yogendra N Joshi
you can use lambdaj (download here,website) and hamcrest (download here,website), this libraries are very powerfull for managing collections, the following code is very simple and works perfectly:
import static ch.lambdaj.Lambda.filter;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static org.hamcrest.Matchers.isIn;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> oldNames = Arrays.asList("nameA","nameE","nameC","namec","NameC");
List<String> newNames = Arrays.asList("nameB","nameD","nameC","nameE");
List<String> newList = filter(having(on(String.class), isIn(oldNames)),newNames);
System.out.print(newList);
//print nameC, nameE
}
}
With this libraries you can solve your problem in one line. You must add to your project: hamcrest-all-1.3.jar and lambdaj-2.4.jar Hope this help serve.
NOTE: This will help you assuming you can have alternatives to your code.
You can use two HashMap<yourFieldName, yourFieldValue> instead of two Arrays / Lists / Sets of Strings (or multiple random Strings);
Then you need a method to compare each value of both maps by their keys;
The result will be an HashMap<String,Boolean> containing the name of each field key, and true if the value is equal in both maps, while false if it is different.
No matter how many fields you will add in the future, the method won't change, while the result will.
Running Example: https://ideone.com/dIaYsK
Code
private static Map<String,Boolean> scanForDifferences(Map<String,Object> mapOne,
Map<String,Object> mapTwo){
Map<String,Boolean> retMap = new HashMap<String,Boolean>();
Iterator<Map.Entry<String, Object>> it = mapOne.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Object> entry = (Map.Entry<String,Object>)it.next();
if (mapTwo.get(entry.getKey()).equals(entry.getValue()))
retMap.put(entry.getKey(), new Boolean(Boolean.TRUE));
else
retMap.put(entry.getKey(), new Boolean(Boolean.FALSE));
it.remove(); // prevent ConcurrentModificationException
}
return retMap;
}
Test Case Input
Map<String,Object> oldMap = new HashMap<String,Object>();
Map<String,Object> newMap = new HashMap<String,Object>();
oldMap.put("initials","Y. J.");
oldMap.put("firstName","Yogend");
oldMap.put("lastName","Jos");
oldMap.put("userName","YNJos");
oldMap.put("age","33");
newMap.put("initials","Y. J.");
newMap.put("firstName","Yogendra");
newMap.put("lastName","Joshi");
newMap.put("userName","YNJoshi");
newMap.put("age","33");
Test Case Run
Map<String,Boolean> diffMap = Main.scanForDifferences(oldMap, newMap);
Iterator<Map.Entry<String, Boolean>> it = diffMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Boolean> entry = (Map.Entry<String,Boolean>)it.next();
System.out.println("Field [" + entry.getKey() +"] is " +
(entry.getValue()?"NOT ":"") + "different" );
}
You should check too if a value is present in one map and not in another one.
You could return an ENUM instead of a Boolean with something like EQUAL, DIFFERENT, NOT PRESENT ...
You should convert your String to some Set.
One set for OLD and another for NEW. And your goal of varity number of elements will also be resolved using same.
As it's set order of it will be same.
I love lambdaj and use it a lot, but I can't seem to figure out if it is possible to sort a list using multiple sort conditions.
Here is an example using Google Collections. Can the same thing be done in lambdaj?
Sorted first by color then by name:
Function<Fruit, Color> getColorFunction = new Function<Fruit, Color>() {
public Color apply(Fruit from) {
return from.getColor();
}
};
Function<Fruit, String> getNameFunction = new Function<Fruit, String>() {
public String apply(Fruit from) {
return from.getName();
}
};
Ordering<Fruit> colorOrdering = Ordering.natural().onResultOf(getColorFunction);
Ordering<Fruit> nameOrdering = Ordering.natural().onResultOf(getNameFunction);
Ordering<Fruit> colorAndNameOrdering = colorOrdering.compound(nameOrdering);
ImmutableSortedSet<Fruit> sortedFruits = ImmutableSortedSet.orderedBy(
colorAndNameOrdering).addAll(fruits).build();
Examples of doing that aren't provided on official Lambdaj page, but under hood each time when you are calling sort Lambdaj creates comparator for given argument. Below is an example how to sort by multiple attributes.
Sorting:
public static void main(String... args) {
final Comparator byName = new ArgumentComparator(on(Fruit.class).getName());
final Comparator byColor = new ArgumentComparator(on(Fruit.class).getColor());
final Comparator orderBy = ComparatorUtils.chainedComparator(byName, byColor);
final List<Fruit> unsorted = Arrays.asList(...);
final List<Fruit> sorted = sort(unsorted, on(Fruit.class), orderBy);
System.out.println(sorted);
}
Downside:
There's one non-lambdaj 3rd party method usage. ComparatorUtils.chainedComparator - it's from apache commons collections. If you don't have this as a dependency in your project you can write your own. it just iterates thru all comparators untill non zero returned.