How to convert a Set<Set> to an ArrayList<ArrayList> - java

How would I add all the elements of a Set<<Set<String>> var to an ArrayList<<ArrayList<String>>? Of course I'm aware of the naive approach of just adding them.
private static ArrayList<ArrayList<String>> groupAnagrams(ArrayList<String> words){
ArrayList<ArrayList<String>> groupedAnagrams = new ArrayList<>();
AbstractMap<String, String> sortedWords = new HashMap<>();
Set<Set<String>> sameAnagramsSet = new HashSet<>();
for(String word : words){
char[] wordToSort = word.toCharArray();
Arrays.sort(wordToSort);
sortedWords.put(word, new String(wordToSort));
}
for(Map.Entry<String, String> entry: sortedWords.entrySet() ){
Set<String> sameAnagrams = new HashSet<>();
sameAnagrams.add(entry.getKey());
for(Map.Entry<String, String> toCompare : sortedWords.entrySet()){
if(entry.getValue().equals(toCompare.getValue())){
sameAnagrams.add(toCompare.getKey());
}
}
if(sameAnagrams.size()>0){
sameAnagramsSet.add(sameAnagrams);
}
}
//-->this line does not work! return new ArrayList<ArrayList<String>>(sameAnagramsSet);
}

With Java 8, you can do:
return sameAnagramsSet.stream()
.map(ArrayList::new)
.collect(toList());
although it returns a List<ArrayList<String>>.
What it does:
.stream() returns a Stream<Set<String>>
.map(ArrayList::new) is equivalent to .map(set -> new ArrayList(set)) and basically replaces each set by an array list
collect(toList()) places all the newly created lists in one list

Since you want to convert each element from a Set to an ArrayList, you'll have to do at least a little of this with an explicit loop, I think (unless you're using Java 8 or a third-party library):
Set<Set<String>> data = . . .
ArrayList<List<String>> transformed = new ArrayList<List<String>>();
for (Set<String> item : data) {
transformed.add(new ArrayList<String>(item));
}
Note that I changed the type of the transformed list from ArrayList<ArrayList<String>> to ArrayList<List<String>>. Generally it's preferable to program to an interface, but if you really need a list that must contain specifically instances of ArrayList, you can switch it back.

Related

When to use <> when initiating an ArrayList (or any other object)?

I have a question regarding the initiation of ArrayList in Java.
As far as I know I have to specify the type of my ArrayList when initialize it.
For example:
ArrayList<String> names = new ArrayList<String>();
or
ArrayList<String> names = new ArrayList<>();
However, when I want to write a code to return an ArrayList of List,
which is from the values of a map
Map<String, List> map = new HashMap<>();
If I tried to use the following, error happens
return new ArrayList<>(map.values())
or
return new ArrayList<List<String>>(map.values())
Instead, I can only use the one, without the <>.
return new ArrayList(map.values())
Could anyone let me know when I should or should not use the <> when I initialize an ArrayList(or any other objects)?
The original code is as written below
public List<List<String>> groupAnagrams(String[] strs) {
if (strs.length == 0) return new ArrayList<>();
Map<String, List> res = new HashMap<>();
for (String s : strs) {
char[] charArray = s.toCharArray();
Arrays.sort(charArray);
String key = String.valueOf(charArray);
if (!res.containsKey(key)) res.put(key, new ArrayList());
res.get(key).add(s);
}
return new ArrayList<List<String>>(res.values());
}
public List<List<String>> groupAnagrams(String[] strs) {
...
Map<String, List> res = new HashMap<>();
...
res.put(key, new ArrayList());
...
return new ArrayList<List<String>>(res.values());
}
The problem here is that res.values() isn't a Collection<List<String>>, which is what would be required: it's a Collection<List>.
The elements of res.values() are raw types: you've used List without specifying the type parameters.
Raw types disable type checking, meaning you can end up putting things into collections with types you don't want.
Every time you use a generic type, make sure it has type parameters (or diamonds, if allowed:
public List<List<String>> groupAnagrams(String[] strs) {
...
Map<String, List<String>> res = new HashMap<>();
...
res.put(key, new ArrayList<>());
...
return new ArrayList<>(res.values());
}
Because the type of map value is List , but you define the type of list value is string.
And suggest use guava Multimap instead of Map<String ,List>

Searching a Hashmap for values of type list, retrieving an element of list and adding it to a new Collection to return

i'm pretty new when it comes to Java but i'll hopefully clear this up.
I currently have one class and within that class i have a TreeMap called "departments" which takes an argument of >:
TreeMap <String, List<String>> department;
Within each department such as HR, Builders etc there are a list of names of people who work there. Such as:
HR: Janet, Jones, Bob
What i'd like to do is search through department to find all departments (keys) that contain someone who's called "bob" for instance and add them to a collection to make a return.
Can anyone help with this, i've been pulling my hair out for a few days! So far i'm this far with the method although clearly nowhere near complete!
public List<String> selectValues( String... aValue)
{
for(String eachDept : department.keySet()){
Collection<String> peopleInTheDept = department.get(eachDept);
for(String person : aValue){
if(peopleInTheDept.contains(person)){
result.add(person);
}
}
}
System.out.println(result);
}
Just like OH GOD SPIDERS predicted, there is a Java 8 stream solution:
TreeMap <String, List<String>> department = new TreeMap<>();
department.put("AB", Arrays.asList("Bob", "Truus", "Miep"));
department.put("CD", Arrays.asList("Jan", "Kees", "Huub"));
department.put("EF", Arrays.asList("Jan", "Piet", "Bert"));
String aValue = "Jan";
Map<String,List<String>> result = department.entrySet().stream()
// filter out those departments that don't contain aValue
.filter(entry -> entry.getValue().contains(aValue))
// collect the matching departments back into a map
.collect(Collectors.toMap(k -> k.getKey(), k -> k.getValue()));
// print the result
result.forEach((k,v)-> System.out.println(k + " " + v.toString()));
Which prints:
EF [Jan, Piet, Bert]
CD [Jan, Kees, Huub]
Pre Java 8 solution:
Map<String, List<String>> result2 = new TreeMap<>();
for(String eachDept : department.keySet()){
List<String> peopleInTheDept = department.get(eachDept);
if(peopleInTheDept.contains(aValue)){
result2.put(eachDept, department.get(eachDept));
}
}
for (String s : result2.keySet()){
System.out.println(s + " " + result2.get(s));
}
This prints exactly the same as my Java 8 code.
Here is some java 8 Streams fancy solution to get the information and fill your list.
public void selectValues(String... values)
{
for(String value : values) {
results.addAll(department.entrySet()
.stream()
.filter(entry -> entry.getValue().contains(value))
.map(entry -> entry.getKey())
.collect(Collectors.toList()));
}
}
What I understand that you need a list of department who's Value contain certain name. Here is the solution of Java 8 version.
public static void main(String[] args) {
// Initialization of map
Map<String, List<String>> department = new TreeMap<>();
department.put("HR", new ArrayList<>());
department.put("ACC", new ArrayList<>());
department.put("MK", new ArrayList<>());
department.get("HR").add("bob");
department.get("HR").add("John");
department.get("ACC").add("Kim");
department.get("ACC").add("bob");
department.get("MK").add("XXX");
// Here is the solution
// depts is a list of string which contains the name of
// department who's value contains 'bob'
List<String > depts = department.entrySet()
.stream()
.filter(i -> i.getValue().contains("bob"))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
// Printing out the result
depts.stream().forEach(System.out::println);
}
In traditional way
List<String> depts = new ArrayList<>();
for (Map.Entry<String , List<String >> it :
department.entrySet()) {
if (it.getValue().contains("bob"))
depts.add(it.getKey());
}
Java8's streams is certainly an elegant solution...but if you are restricted with the use of java 8 then how about your departments be of type TreeMap<String, Set<String>>. If you insist in using a list its very similiar. Just change the Set to List. There is a contains method in List interface too.
TreeMap<String, List<String>> departments = new TreeMap<String, List<String>>();
ArrayList<String> myList = new ArrayList<String>();
myList.add("person1");
departments.put("dep1", myList);
String[] aValue = {"person1","person2"};
public void selectValues( String... aValue)
{
for(String eachDept : departments.keySet()){
List<String> peopleInTheDept = departments.get(eachDept);
for(String person : aValue){
if(peopleInTheDept.contains(person)){
result.add(person)
}
}
}
System.out.println(result);
}
Something similar to this maybe?

Split 1 ArrayList into multiple ones based on condition

I have a big ArrayList containing strings. I want to split it, based on a condition that an element meets. For example, it could be the string length if ArrayList contained String. What is the most efficient (not the easiest) way to do that ?
['a', 'bc', 'defe', 'dsa', 'bb']
after would result to :
['a'], ['bc', 'bb'], ['dsa'], ['defe']
It's easy and fairly efficient to do it using Java 8 streams:
Collection<List<String>> output = input.stream()
.collect(Collectors.groupingBy(String::length))
.values();
If you run it with this input:
List<String> input = Arrays.asList("a", "bc", "defe", "dsa", "bb");
You will get this output:
[[a], [bc, bb], [dsa], [defe]]
A non-stream version would do the same thing, i.e. build a Map<K, List<V>> where V is your value type (e.g. String in your case), and K is the type of the grouping value (e.g. Integer for the length).
Doing it yourself (like shown in answer by palako) might be slightly more efficient at runtime, but likely not in any way that matters.
Staying with Java 8, that would be this:
Map<Integer, List<String>> map = new HashMap<>();
for (String value : input)
map.computeIfAbsent(value.length(), ArrayList::new).add(value);
Collection<List<String>> output = map.values();
For earlier versions of Java, you can't use computeIfAbsent(), so:
Map<Integer, List<String>> map = new HashMap<Integer, List<String>>();
for (String value : input) {
Integer length = Integer.valueOf(value.length()); // box only once
List<String> list = map.get(length);
if (list == null)
map.put(length, list = new ArrayList<String>());
list.add(value);
}
Collection<List<String>> output = map.values();
The most efficient way is to iterate the original list just once. What you do is you create buckets and add to those buckets.
public class Q1 {
public static void main(String[] args) {
String[] original = {"a","bc","defe","dsa","bb"};
List<String> originalValues = new ArrayList<String>(Arrays.asList(original));
Map<Integer, List<String>> orderedValues = new HashMap<Integer, List<String>>();
Iterator<String> it = originalValues.iterator();
while (it.hasNext()) {
String currentElement = it.next();
int length = currentElement.length();
if(!orderedValues.containsKey(length)) {
orderedValues.put(length, new ArrayList<String>());
}
orderedValues.get(length).add(currentElement);
}
System.out.println(orderedValues.values());
}
}
You might be tempted to use an array of arrays instead of a Map, and use the size of the string as the index to the array position, but then you need to watch out for a case where you don't have strings of a certain length. Imagine a case where you only have a string in the original list, but it has 100 characters. You would have 99 empty positions and one string in the array at position 100.

Remove duplicates (both values) - duplicate values from an ArrayList

I have an ArrayList with the following strings;
List<String> e = new ArrayList<String>();
e.add("123");
e.add("122");
e.add("125");
e.add("123");
I want to check the list for duplicates and remove them from the list. In this case my list will only have two values, and in this example it would be the values 122 and 125, and the two 123s will go away.
What will be the best way to this? I was thinking of using a Set, but that will only remove one of the duplicates.
In Java 8 you can do:
e.removeIf(s -> Collections.frequency(e, s) > 1);
If !Java 8 you can create a HashMap<String, Integer>. If the String already appears in the map, increment its key by one, otherwise, add it to the map.
For example:
put("123", 1);
Now let's assume that you have "123" again, you should get the count of the key and add one to it:
put("123", get("aaa") + 1);
Now you can easily iterate on the map and create a new array list with keys that their values are < 2.
References:
ArrayList#removeIf
Collections#frequency
HashMap
You can also use filter in Java 8
e.stream().filter(s -> Collections.frequency(e, s) == 1).collect(Collectors.toList())
You could use a HashMap<String, Integer>.
You iterate over the list and if the Hash map does not contain the string, you add it together with a value of 1.
If, on the other hand you already have the string, you simply increment the counter. Thus, the map for your string would look like this:
{"123", 2}
{"122", 1}
{"125", 1}
You would then create a new list where the value for each key is 1.
Here is a non-Java 8 solution using a map to count occurrences:
Map <String,Integer> map = new HashMap<String, Integer>();
for (String s : list){
if (map.get(s) == null){
map.put(s, 1);
}
else {
map.put(s, map.get(s) + 1);
}
}
List<String> newList = new ArrayList<String>();
// Remove from list if there are multiples of them.
for (Map.Entry<String, String> entry : map.entrySet())
{
if(entry.getValue() > 1){
newList.add(entry.getKey());
}
}
list.removeAll(newList);
Solution in ArrayList
public static void main(String args[]) throws Exception {
List<String> e = new ArrayList<String>();
List<String> duplicate = new ArrayList<String>();
e.add("123");
e.add("122");
e.add("125");
e.add("123");
for(String str : e){
if(e.indexOf(str) != e.lastIndexOf(str)){
duplicate.add(str);
}
}
for(String str : duplicate){
e.remove(str);
}
for(String str : e){
System.out.println(str);
}
}
The simplest solutions using streams have O(n^2) time complexity. If you try them on a List with millions of entries, you'll be waiting a very, very long time. An O(n) solution is:
list = list.stream()
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
.entrySet()
.stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Here, I used a LinkedHashMap to maintain the order. Note that static imports can simplify the collect part.
This is so complicated that I think using for loops is the best option for this problem.
Map<String, Integer> map = new LinkedHashMap<>();
for (String s : list)
map.merge(s, 1, Integer::sum);
list = new ArrayList<>();
for (Map.Entry<String, Integer> e : map.entrySet())
if (e.getValue() == 1)
list.add(e.getKey());
List<String> e = new ArrayList<String>();
e.add("123");
e.add("122");
e.add("125");
e.add("123");
e.add("125");
e.add("124");
List<String> sortedList = new ArrayList<String>();
for (String current : e){
if(!sortedList.contains(current)){
sortedList.add(current);
}
else{
sortedList.remove(current);
}
}
e.clear();
e.addAll(sortedList);
I'm a fan of the Google Guava API. Using the Collections2 utility and a generic Predicate implementation it's possible to create a utility method to cover multiple data types.
This assumes that the Objects in question have a meaningful .equals
implementation
#Test
public void testTrimDupList() {
Collection<String> dups = Lists.newArrayList("123", "122", "125", "123");
dups = removeAll("123", dups);
Assert.assertFalse(dups.contains("123"));
Collection<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
dups2 = removeAll(123, dups2);
Assert.assertFalse(dups2.contains(123));
}
private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
return Collections2.filter(collection, new Predicate<T>(){
#Override
public boolean apply(T arg0) {
return !element.equals(arg0);
}});
}
Thinking about this a bit more
Most of the other examples in this page are using the java.util.List API as the base Collection. I'm not sure if that is done with intent, but if the returned element has to be a List, another intermediary method can be used as specified below. Polymorphism ftw!
#Test
public void testTrimDupListAsCollection() {
Collection<String> dups = Lists.newArrayList("123", "122", "125", "123");
//List used here only to get access to the .contains method for validating behavior.
dups = Lists.newArrayList(removeAll("123", dups));
Assert.assertFalse(dups.contains("123"));
Collection<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
//List used here only to get access to the .contains method for validating behavior.
dups2 = Lists.newArrayList(removeAll(123, dups2));
Assert.assertFalse(dups2.contains(123));
}
#Test
public void testTrimDupListAsList() {
List<String> dups = Lists.newArrayList("123", "122", "125", "123");
dups = removeAll("123", dups);
Assert.assertFalse(dups.contains("123"));
List<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
dups2 = removeAll(123, dups2);
Assert.assertFalse(dups2.contains(123));
}
private <T> List<T> removeAll(final T element, List<T> collection) {
return Lists.newArrayList(removeAll(element, (Collection<T>) collection));
}
private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
return Collections2.filter(collection, new Predicate<T>(){
#Override
public boolean apply(T arg0) {
return !element.equals(arg0);
}});
}
Something like this (using a Set):
Set<Object> blackList = new Set<>()
public void add(Object object) {
if (blackList.exists(object)) {
return;
}
boolean notExists = set.add(object);
if (!notExists) {
set.remove(object)
blackList.add(object);
}
}
If you are going for set then you can achieve it with two sets. Maintain duplicate values in the other set as follows:
List<String> duplicateList = new ArrayList<String>();
duplicateList.add("123");
duplicateList.add("122");
duplicateList.add("125");
duplicateList.add("123");
duplicateList.add("127");
duplicateList.add("127");
System.out.println(duplicateList);
Set<String> nonDuplicateList = new TreeSet<String>();
Set<String> duplicateValues = new TreeSet<String>();
if(nonDuplicateList.size()<duplicateList.size()){
for(String s: duplicateList){
if(!nonDuplicateList.add(s)){
duplicateValues.add(s);
}
}
duplicateList.removeAll(duplicateValues);
System.out.println(duplicateList);
System.out.println(duplicateValues);
}
Output: Original list: [123, 122, 125, 123, 127, 127]. After removing
duplicate: [122, 125] values which are duplicates: [123, 127]
Note: This solution might not be optimized. You might find a better
solution than this.
With the Guava library, using a multiset and streams:
e = HashMultiset.create(e).entrySet().stream()
.filter(me -> me.getCount() > 1)
.map(me -> me.getElement())
.collect(toList());
This is pretty, and reasonably fast for large lists (O(n) with a rather large constant factor). But it does not preserve order (LinkedHashMultiset can be used if that is desired) and it creates a new list instance.
It is also easy to generalise, to instead remove all triplicates for example.
In general the multiset data structure is really useful to keep in ones toolbox.

Two-dimensional ArrayList

Just a very small question... I seem to run into too much complexity here: I have to realize an index-structure like {42, someString}. I tried:
Object entry[][] = new Object[1][1];
ArrayList<Object> my_list = new ArrayList<Object>();
However that looks really strange. Isn't there a better much simpler solution to just store some Integer and a String? I need to perfrom search for the Strings and return the Integer... so I thought Collections and ArrayLists are good friends in the Java API.
Solution: use a Map
Uhm, do you perhaps need a Map?
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Some String", 42);
// or, more correctly:
map.put("Some String", Integer.valueOf(42));
You can search it using
Integer result = map.get("Some String");
Reference: Sun Java Tutorial > Collection Trail > Interfaces > The Map Interface
Fixing the OP's Code
BTW, the code in the question is flawed. Here's how you would do it if you wanted to use a List of object arrays (which you shouldn't):
// single dimension, not multi-dimension
Object[] entry = new Object[]{"Some String",Integer.valueOf(42)};
// use interface as variable, not implementation type
// generic type is object array, not object
List<Object[]> myList = new ArrayList<Object[]>();
// add array to list
myList.add(entry);
Now you could search like this:
for(final Object[] candidate : myList){
if("Some String".equals(candidate[0])){
System.out.println("Result: " + candidate[1]);
break;
}
}
However, this is just for reference, don't do it this way. The Collections Framework contains solutions for almost all standard cases. Use a Map.
Make a tuple class
public Class IntegerStringTuple {
private Integer number;
private String string;
//setters and getters etc.
}
If I understand correctly you should use a Map.
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(42, "someString");
String str = map.get(42);
Simply use a HashMap
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("foo",42);
why not use a map?
Map<String,Object>
It sounds like you want a Map
I would use a Map. Maps are used to store key value pairs.
Map<String, Integer> map = new HashMap<String, Integer>();
Map may not be used instead of an ArrayList when you require the order to be maintained.
ArrayList arr1 = new ArrayList();
ArrayList arr2 = new ArrayList();
arr2.add(1);
arr2.add(2);
arr2.add(3);
arr1.add(arr2);
for(int i=0;i<arr1.size();i++){
System.out.println("i:"+arr1.get(i));
for(int j=0;j<((ArrayList)arr1.get(i)).size();j++){
System.out.println("j:"+((ArrayList)arr1.get(i)).get(j));
}
}
output: i:[1, 2, 3]
j:1
j:2
j:3
ArrayList<String> lcname = new ArrayList<String>();
lcname.add(cname);
ArrayList<String> lsize = new ArrayList<String>();
lsize.add(size);
Dictionary dictionary = new Hashtable();
Hashtable<String, ArrayList<ArrayList>> hashtable =
new Hashtable<String, ArrayList<ArrayList>>();
hashtable.put(fname, new ArrayList<>());
hashtable.get(fname).add(lcname);
hashtable.get(fname).add(lsize);
System.out.println(hashtable);
Here is the code for dictionaries of list(list).
OUTPUT
{file name=[[column name], [size]]}

Categories

Resources