I am brand new to Java :)
I have 2 String lists and I was wondering what would be the most efficient way to compare the two and have a resulting array which contains strings that are not in the other. For example, I have a list called oldStrings and one called Strings. I have seen the Comparator function but don't fully understand how it works, right now I was thinking I could create a for loop, loop through each string and then save that string:
for (final String str : oldStrings) {
if(!strings.contains(str))
{
getLogger().info(str + " is not in strings list ");
}
}
There's going to be up to 200 strings in this list. Would this be the best way to go about this? Thank you!
Collection firstList = new ArrayList() {{
add("str1");
add("str2");
}};
Collection secondList = new ArrayList() {{
add("str1");
add("str3");
add("str4");
}};
System.out.println("First List: " + firstList);
System.out.println("Second List: " + secondList);
// Here is main part
secondList.removeAll(firstList);
System.out.println("Result: " + secondList);
Update:
More sophisticated version of code
Collection<String> firstList = new ArrayList<String>();
firstList.add("str1");
firstList.add("str2");
Collection<String> secondList = new ArrayList<String>();
secondList.add("str1");
secondList.add("str2");
secondList.add("str3");
System.out.println("First List: " + firstList);
System.out.println("Second List: " + secondList);
// Here is main part
secondList.removeAll(firstList);
Update:
To Get acctual difference between both String list go for this.
Set<String> setOne = new HashSet<String>();
Set<String> setTwo = new HashSet<String>();
setOne.add("1");
setOne.add("2");
setOne.add("5");
setTwo.add("1");
setTwo.add("3");
setTwo.add("4");
Set<String> setTwoDummy = new HashSet<String>(setTwo);
setTwo.retainAll(setOne);
setTwoDummy.addAll(setOne);
setTwoDummy.removeAll(setTwo);
System.out.println(""+setTwoDummy);
First, the problem with your solution is that it will only find elements that are in oldStrings and not strings. If you're going with this approach then you need to loop on the other list as well.
If this is not for homework then check out CollectionUtils.disjunction from Apache Commons Collections.
Compare two lists of strings and have a
resulting array which contains strings
that are not in the other.
The description is ambiguous because we don't we don't know if we need just non matching strings from the first list, the second list, or both. Below is pseudo code for both.
for (String str : oldStrings)
{
if(strings.contains(str))
{
intersectionList.add(str);
}
}
oldStrings.removeAll(intersectionList);
strings.removeAll(intersectionList);
result = strings.addAll(oldStrings).toArray();
Or
copyStrings = strings.clone();
strings.removeAll(oldStrings);
oldStrings.removeAll(copyStrings);
result = strings.addAll(oldStrings).toArray();
You should be using Google Guava's Sets utilities.
Set<String> s = Sets.newHashSet("a", "b", "c", "d");
Set<String> t = Sets.newHashSet("f", "g", "a", "c");
Sets.SetView<String> difference = Sets.difference(s, t);
System.out.println(difference); // prints [b, d]
Related
I've searched through many questions on this site with somewhat similar underlying concepts, however after many hours of attempting to solve this problem myself and reviewing I am still lost. If there is another question that answers this I will be more than happy to give it a look over.
Ultimately I want to create a recursive method such that it takes two lists and returns a Set of String lists:
//Example of such a function definition
private static Set<List<String>> myRecursiveMethod(List<String> listOne,
List<String> listTwo) {
}
When I say "Set of String lists" I mean specifically the following:
(Note:"AD" == "DA")
// if the two following lists are INPUTTED into myRecursiveMethod();
// listOne = ["A","B"]
// listTwo = ["C","D"]
// the following set is OUTPUTTED: [["AC","BD"],["AD","BC"]]
Such that if there were three elements in both listOne and listTwo, there would be SIX elements in the set. i.e:
// listOne = ["A","B","C"]
// listTwo = ["D","E","F"]
// OUTPUTTED: [["AD","BE","CF"],["AD","BF","CE"],["BD","AE","CF"],
// ["BD","AF","CE"],["CD","AE","BF"],["CD","AF","BE"]]
I tried writing this using a double enhanced FOR loop so I could understand the logic. My FOR loop approach is terrible and only works for the HARD-CODED limit of list.size() == 2.
// Create Lists and append elements
List<String> listOne = new ArrayList<String>();
listOne.add("A");
listOne.add("B");
List<String> listTwo = new ArrayList<String>();
listTwo.add("C");
listTwo.add("D");
// List One = ["A","B"]
// List Two = ["C","D"]
// Create new List
List<List<String>> newList = new ArrayList<List<String>>();
Integer counter = 0;
for (String s : listOne) {
counter++;
for (String p : listTwo) {
// A HARD-CODED bad implementation of this method
if (counter < 3) {
List<String> newListTwo = new ArrayList<String>();
newListTwo.add(s.concat(p));
newList.add(newListTwo);
} else if (!(counter % 2 == 0)) {
newList.get(1).add(s.concat(p));
} else {
newList.get(0).add(s.concat(p));
}
}
}
System.out.println(newList); // = [["AC","BD"],["AD","BC"]]
Also you can note that I defined List<List<String>> Rather than Set<List<String>>. This was due to my badly coded attempted which relies on the list.get() method.
So my current recursive method is as follows:
private static Set<List<String>> myRecursiveMethod(List<String> listOne,
List<String> listTwo)
{
//Base Case:
if (listOne.isEmpty){
return new HashSet<List<String>>;
}
//Recursive Case:
else {
String listOneFirst = listOne.get(0);
String listTwoFirst = listTwo.get(0);
List<String> sampleList = new ArrayList<String>();
sampleList.add(listOneFirst+listTwoFirst);
Set<List<String>> newSet = new HashSet<List<String>>(myRecursiveMethod())
newSet.add(sampleList);
return newSet;
}
}
This method only acts like this currently:
INPUT:
List One = ["A","B"]
List Two = ["C","D"]
OUTPUT:
[["AC"]["BD"]]
DESIRED OUTPUT:
[["AC","BD"],["AD","BC"]]
EDIT:
After reviewing responses my W.I.P code for the class:
private static Set<List<String>> myRecursiveMethod(List<String> listOne,
List<String> listTwo) {
//Backup Case (user enters an empty list)
if (listOne.isEmpty()){
return new HashSet<List<String>>();
}
// Base Case:
if (listOne.size() == 1) {
List<String> mergedStrings = new ArrayList<>();
for (String s : listTwo) {
mergedStrings.add(listOne.get(0).concat(s));
}
Set<List<String>> builtHashSet = new HashSet<List<String>();
builtHashSet.add(mergedStrings);
return builtHashSet;
}
// Recursive Case:
else {
// Ensure original list values arn't changed.
List<String> newListOne = new ArrayList<String>(listOne);
List<String> newListTwo = new ArrayList<String>(listTwo);
//first two elements...I don't think this is correct
String listOneFirst = newListOne.get(0);
String listTwoFirst = newListTwo.get(0);
List<String> sampleList = new ArrayList<String>();
sampleList.add(listOneFirst + listTwoFirst);
//used for making recursive case smaller
newListOne.remove(0);
// Calls recursion
Set<List<String>> newSet = new HashSet<List<String>>(
myRecursiveMethod(newListOne, newListTwo));
newSet.add(sampleList);
return newSet;
}
}
I think the problem is here:
if (listOne.isEmpty){
return new HashSet<List<String>>;
}
You are correct, at some point your recursion has to end, and you have to start building the desired output. But the desired output is not a Set with an empty list. It is a Set containing some lists with some content. Thus: don't wait until listOne is empty. Instead:
if (listOne.size() == 1) {
List<String> mergedStrings = new ArrayList<>();
mergedStrings = ... merge the ONE listOne entry with all listTwo entries
Set<List<String>> rv = new HashSet<>();
rv.add(mergedStrings);
return rv;
}
In other words: you use recursion to reduce the length of the first list by one. And when only one element is left in that list, it is time to merge in the second list.
Now lets look into how to "use" that (calling the method rec for brevity); putting down some pseudo code to show the steps we need:
rec([a, b], [c,d]) -->
rec([a], [c,d]) X rec([b], [c, d]) -->
<[ac, ad]> X <[bc, bd]> -->
<[ac, ad], [bc, bd]>
"X" meaning "joining" two results from recursive calls; should be as easy as:
Set<List<String>> rec1 = rec(...);
return rec1.addAll(rec2 ...
There are a lot of useful new things in Java 8. E.g., I can iterate with a stream over a list of objects and then sum the values from a specific field of the Object's instances. E.g.
public class AClass {
private int value;
public int getValue() { return value; }
}
Integer sum = list.stream().mapToInt(AClass::getValue).sum();
Thus, I'm asking if there is any way to build a String that concatenates the output of the toString() method from the instances in a single line.
List<Integer> list = ...
String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class
Suppose that list contains integers 1, 2 and 3, I expect that concatenated is "123" or "1,2,3".
One simple way is to append your list items in a StringBuilder
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
StringBuilder b = new StringBuilder();
list.forEach(b::append);
System.out.println(b);
you can also try:
String s = list.stream().map(e -> e.toString()).reduce("", String::concat);
Explanation: map converts Integer stream to String stream, then its reduced as concatenation of all the elements.
Note: This is normal reduction which performs in O(n2)
for better performance use a StringBuilder or mutable reduction similar to F. Böller's answer.
String s = list.stream().map(Object::toString).collect(Collectors.joining(","));
Ref: Stream Reduction
There is a collector joining in the API.
It's a static method in Collectors.
list.stream().map(Object::toString).collect(Collectors.joining(","))
Not perfect because of the necessary call of toString, but works. Different delimiters are possible.
Just in case anyone is trying to do this without java 8, there is a pretty good trick. List.toString() already returns a collection that looks like this:
[1,2,3]
Depending on your specific requirements, this can be post-processed to whatever you want as long as your list items don't contain [] or , .
For instance:
list.toString().replace("[","").replace("]","")
or if your data might contain square brackets this:
String s=list.toString();
s = s.substring(1,s.length()-1)
will get you a pretty reasonable output.
One array item on each line can be created like this:
list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")
I used this technique to make html tooltips from a list in a small app, with something like:
list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")
If you have an array then start with Arrays.asList(list).toString() instead
I'll totally own the fact that this is not optimal, but it's not as inefficient as you might think and is pretty straightforward to read and understand. It is, however, quite inflexible--in particular don't try to separate the elements with replaceAll if your data might contain commas and use the substring version if you have square brackets in your data, but for an array of numbers it's pretty much perfect.
There is a method in the String API for those "joining list of string" usecases, you don't even need Stream.
List<String> myStringIterable = Arrays.asList("baguette", "bonjour");
String myReducedString = String.join(",", myStringIterable);
// And here you obtain "baguette,bonjour" in your myReducedString variable
The other answers are fine. However, you can also pass Collectors.toList() as parameter to Stream.collect() to return the elements as an ArrayList.
System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
StringListName = ObjectListName.stream().map( m -> m.toString() ).collect( Collectors.toList() );
List<String> list = Arrays.asList("One", "Two", "Three");
list.stream()
.reduce("", org.apache.commons.lang3.StringUtils::join);
Or
List<String> list = Arrays.asList("One", "Two", "Three");
list.stream()
.reduce("", (s1,s2)->s1+s2);
This approach allows you also build a string result from a list of objects
Example
List<Wrapper> list = Arrays.asList(w1, w2, w2);
list.stream()
.map(w->w.getStringValue)
.reduce("", org.apache.commons.lang3.StringUtils::join);
Here the reduce function allows you to have some initial value to which you want to append new string
Example:
List<String> errors = Arrays.asList("er1", "er2", "er3");
list.stream()
.reduce("Found next errors:", (s1,s2)->s1+s2);
Testing both approaches suggested in Shail016 and bpedroso answer (https://stackoverflow.com/a/24883180/2832140), the simple StringBuilder + append(String) within a for loop, seems to execute much faster than list.stream().map([...].
Example: This code walks through a Map<Long, List<Long>> builds a json string, using list.stream().map([...]:
if (mapSize > 0) {
StringBuilder sb = new StringBuilder("[");
for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
sb.append("{\"" + entry.getKey().toString() + "\":[");
sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
}
sb.delete(sb.length()-2, sb.length());
sb.append("]");
System.out.println(sb.toString());
}
On my dev VM, junit usually takes between 0.35 and 1.2 seconds to execute the test. While, using this following code, it takes between 0.15 and 0.33 seconds:
if (mapSize > 0) {
StringBuilder sb = new StringBuilder("[");
for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {
sb.append("{\"" + entry.getKey().toString() + "\":[");
for (Long tid : entry.getValue()) {
sb.append(tid.toString() + ", ");
}
sb.delete(sb.length()-2, sb.length());
sb.append("]}, ");
}
sb.delete(sb.length()-2, sb.length());
sb.append("]");
System.out.println(sb.toString());
}
A clean way to do this is by mapping the elements of the list to string and then using the joining operation in Collectors class.
List<Integer> ls = new ArrayList<Integer>();
ls.add(1);
ls.add(2);
ls.add(3);
String s = ls.stream().map(Object::toString).collect(Collectors.joining(","));
String actual = list.stream().reduce((t, u) -> t + "," + u).get();
I'm going to use the streams api to convert a stream of integers into a single string. The problem with some of the provided answers is that they produce a O(n^2) runtime because of String building. A better solution is to use a StringBuilder, and then join the strings together as the final step.
// Create a stream of integers
String result = Arrays.stream(new int[]{1,2,3,4,5,6 })
// collect into a single StringBuilder
.collect(StringBuilder::new, // supplier function
// accumulator - converts cur integer into a string and appends it to the string builder
(builder, cur) -> builder.append(Integer.toString(cur)),
// combiner - combines two string builders if running in parallel
StringBuilder::append)
// convert StringBuilder into a single string
.toString();
You can take this process a step further by converting the collection of object to a single string.
// Start with a class definition
public static class AClass {
private int value;
public int getValue() { return value; }
public AClass(int value) { this.value = value; }
#Override
public String toString() {
return Integer.toString(value);
}
}
// Create a stream of AClass objects
String resultTwo = Arrays.stream(new AClass[]{
new AClass(1),
new AClass(2),
new AClass(3),
new AClass(4)
})
// transform stream of objects into a single string
.collect(StringBuilder::new,
(builder, curObj) -> builder.append(curObj.toString()),
StringBuilder::append
)
// finally transform string builder into a single string
.toString();
Can we try this.
public static void main(String []args){
List<String> stringList = new ArrayList<>();
for(int i=0;i< 10;i++){
stringList.add(""+i);
}
String stringConcated = String.join(",", stringList);
System.out.println(stringConcated);
}
Also, you can do like this.
List<String> list = Arrays.asList("One", "Two", "Three");
String result = String.join(", ", list);
System.out.println(result);
With Java 8+
String s = Arrays.toString(list.stream().toArray(AClass[]::new));
Not the most efficient, but it is a solution with a small amount of code.
I am trying to figure out how to add a string, into a string ArrayList, between two strings that are already in. So if I have this
ArrayList<String> List = new ArrayList<String>();
List.add("Yes");
List.add("No");
List.add("Maybe");
How would I go along putting the word "Or" between them and make the ArrayList contain
"Yes" "Or" "No" "Or" "Maybe"?
I have three advices.
First, to name the variables, start with lower-case.
Second, use List as type of variable, instead of ArrayList, you will thank me later, trust me.
Third, to do what you ask for, there is overloaded method add for choosing position :
List<String> list = new ArrayList<String>();
list.add("Yes");
list.add("No");
list.add(1,"Maybe"); //insert into position 1 and shift everything to the right.
For this example, if you use System.out.println(list);, you will get this output :
[Yes, Maybe, No]
For adding Or instruction, it would be like this :
List<String> list = new ArrayList<String>();
list.add("Yes");
list.add("No");
list.add("Maybe");
list.add(1, "Or");
list.add(3, "Or");
System.out.println(list);
Output :
[Yes, Or, No, Or, Maybe]
Also, if you want to make your program more re-usable, you can write a method, that will do this for you for any case of list :
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Yes");
list.add("No");
list.add("Maybe");
list.add("Probably");
list.add("Never");
List<String> orList = addOr(list);
System.out.println(orList);
}
public static List<String> addOr(List<String> list){
List<String> newList = new ArrayList<>();
int count = 0;
for(String text : list){
count++;
newList.add(text);
if (count != list.size()){
newList.add("Or");
}
}
return newList;
}
Having this output :
[Yes, Or, No, Or, Maybe, Or, Probably, Or, Never]
However, if you want to use that list for outputing some message for user, it is not good idea to add "Or", because it is really not part of information. Rather it is good, to create method, which will create output String you desire.
This code
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Yes");
list.add("No");
list.add("Maybe");
list.add("Probably");
list.add("Never");
String niceOutput = addOr(list);
System.out.println("Choose from following options: " + niceOutput);
}
public static String addOr(List<String> list){
String orText = "";
int count = 0;
for(String text : list){
count++;
orText += '\'' + text + '\'';
if (count != list.size()){
orText += " or ";
}
}
return orText;
}
Having this output :
Choose from following options: 'Yes' or 'No' or 'Maybe' or 'Probably' or 'Never'
According to Add object to ArrayList at specified index
List.add(1, "or")
List.add(3, "or")
This should solve your problem.
I have to count the number of unique words from a text document using Java. First I had to get rid of the punctuation in all of the words. I used the Scanner class to scan each word in the document and put in an String ArrayList.
So, the next step is where I'm having the problem! How do I create a method that can count the number of unique Strings in the array?
For example, if the array contains apple, bob, apple, jim, bob; the number of unique values in this array is 3.
public countWords() {
try {
Scanner scan = new Scanner(in);
while (scan.hasNext()) {
String words = scan.next();
if (words.contains(".")) {
words.replace(".", "");
}
if (words.contains("!")) {
words.replace("!", "");
}
if (words.contains(":")) {
words.replace(":", "");
}
if (words.contains(",")) {
words.replace(",", "");
}
if (words.contains("'")) {
words.replace("?", "");
}
if (words.contains("-")) {
words.replace("-", "");
}
if (words.contains("‘")) {
words.replace("‘", "");
}
wordStore.add(words.toLowerCase());
}
} catch (FileNotFoundException e) {
System.out.println("File Not Found");
}
System.out.println("The total number of words is: " + wordStore.size());
}
Are you allowed to use Set? If so, you HashSet may solve your problem. HashSet doesn't accept duplicates.
HashSet noDupSet = new HashSet();
noDupSet.add(yourString);
noDupSet.size();
size() method returns number of unique words.
If you have to really use ArrayList only, then one way to achieve may be,
1) Create a temp ArrayList
2) Iterate original list and retrieve element
3) If tempArrayList doesn't contain element, add element to tempArrayList
Starting from Java 8 you can use Stream:
After you add the elements in your ArrayList:
long n = wordStore.stream().distinct().count();
It converts your ArrayList to a stream and then it counts only the distinct elements.
I would advice to use HashSet. This automatically filters the duplicate when calling add method.
Although I believe a set is the easiest solution, you can still use your original solution and just add an if statement to check if value already exists in the list before you do your add.
if( !wordstore.contains( words.toLowerCase() )
wordStore.add(words.toLowerCase());
Then the number of words in your list is the total number of unique words (ie: wordStore.size() )
This general purpose solution takes advantage of the fact that the Set abstract data type does not allow duplicates. The Set.add() method is specifically useful in that it returns a boolean flag indicating the success of the 'add' operation. A HashMap is used to track the occurrence of each original element. This algorithm can be adapted for variations of this type of problem. This solution produces O(n) performance..
public static void main(String args[])
{
String[] strArray = {"abc", "def", "mno", "xyz", "pqr", "xyz", "def"};
System.out.printf("RAW: %s ; PROCESSED: %s \n",Arrays.toString(strArray), duplicates(strArray).toString());
}
public static HashMap<String, Integer> duplicates(String arr[])
{
HashSet<String> distinctKeySet = new HashSet<String>();
HashMap<String, Integer> keyCountMap = new HashMap<String, Integer>();
for(int i = 0; i < arr.length; i++)
{
if(distinctKeySet.add(arr[i]))
keyCountMap.put(arr[i], 1); // unique value or first occurrence
else
keyCountMap.put(arr[i], (Integer)(keyCountMap.get(arr[i])) + 1);
}
return keyCountMap;
}
RESULTS:
RAW: [abc, def, mno, xyz, pqr, xyz, def] ; PROCESSED: {pqr=1, abc=1, def=2, xyz=2, mno=1}
You can create a HashTable or HashMap as well. Keys would be your input strings and Value would be the number of times that string occurs in your input array. O(N) time and space.
Solution 2:
Sort the input list.
Similar strings would be next to each other.
Compare list(i) to list(i+1) and count the number of duplicates.
In shorthand way you can do it as follows...
ArrayList<String> duplicateList = new ArrayList<String>();
duplicateList.add("one");
duplicateList.add("two");
duplicateList.add("one");
duplicateList.add("three");
System.out.println(duplicateList); // prints [one, two, one, three]
HashSet<String> uniqueSet = new HashSet<String>();
uniqueSet.addAll(duplicateList);
System.out.println(uniqueSet); // prints [two, one, three]
duplicateList.clear();
System.out.println(duplicateList);// prints []
duplicateList.addAll(uniqueSet);
System.out.println(duplicateList);// prints [two, one, three]
public class UniqueinArrayList {
public static void main(String[] args) {
StringBuffer sb=new StringBuffer();
List al=new ArrayList();
al.add("Stack");
al.add("Stack");
al.add("over");
al.add("over");
al.add("flow");
al.add("flow");
System.out.println(al);
Set s=new LinkedHashSet(al);
System.out.println(s);
Iterator itr=s.iterator();
while(itr.hasNext()){
sb.append(itr.next()+" ");
}
System.out.println(sb.toString().trim());
}
}
3 distinct possible solutions:
Use HashSet as suggested above.
Create a temporary ArrayList and store only unique element like below:
public static int getUniqueElement(List<String> data) {
List<String> newList = new ArrayList<>();
for (String eachWord : data)
if (!newList.contains(eachWord))
newList.add(eachWord);
return newList.size();
}
Java 8 solution
long count = data.stream().distinct().count();
I am a beginner to java, and need some help.
I am trying to convert an Abstract Data type Foo which is an associated list to an Arraylist of the strings B. How do you loop through the list and add each string to the array.
I may be over thinking it, but I am lost now.
Thanks for the help in advance.
Instantiate a new ArrayList:
List<String> myList = new ArrayList<String>();
Iterate over your data structure (with a for loop, for instance, more details on your code would help.) and for each element (yourElement):
myList.add(yourElement);
If you have an arraylist of String called 'foo', you can easily append (add) it to another ArrayList, 'list', using the following method:
ArrayList<String> list = new ArrayList<String>();
list.addAll(foo);
that way you don't even need to loop through anything.
You should be able to do something like:
ArrayList<String> list = new ArrayList<String>();
for( String s : foo )
{
list.add(s);
}
Array list can be implemented by the following code:
Arraylist<String> list = new ArrayList<String>();
list.add(value1);
list.add(value2);
list.add(value3);
list.add(value4);
Well, you have to iterate through your abstract type Foo and that depends on the methods available on that object. You don't have to loop through the ArrayList because this object grows automatically in Java. (Don't confuse it with an array in other programming languages)
Recommended reading.
Lists in the Java Tutorial
thanks for the help, I've solved my problem :) Here is the code if anyone else needs it :D
import java.util.*;
public class HelloWorld {
public static void main(String[] Args) {
Map<Integer,List<Integer>> map = new HashMap<Integer,List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(9);
list.add(11);
map.put(1,list);
int First = list.get(1);
int Second = list.get(2);
if (First < Second) {
System.out.println("One or more of your items have been restocked. The current stock is: " + First);
Random rn = new Random();
int answer = rn.nextInt(99) + 1;
System.out.println("You are buying " + answer + " New stock");
First = First + answer;
list.set(1, First);
System.out.println("There are now " + First + " in stock");
}
}
}
If you're using Java 9, there's an easy way with less number of lines without needing to initialize or add method.
List<String> list = List.of("first", "second", "third");