I have a List that contains duplicate ArrayList.
I'm looking for a solution to remove them.
Here is an example:
listOne = [[1, 0], [0, 1], [3, 2], [2, 3]]
This set contains duplicate List. Normally i want to get :
theListAfterTransformation = [[1, 0],[3, 2]]
Here is my tiny example, i tried to use the Set but it didn't work well.
public class Example {
public static void main( String[] args ) {
ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
ArrayList<Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(0);
ArrayList<Integer> list2 = new ArrayList<>(); list2.add(0); list2.add(1);
ArrayList<Integer> list3 = new ArrayList<>(); list3.add(3); list3.add(2);
ArrayList<Integer> list4 = new ArrayList<>(); list4.add(2); list4.add(3);
lists.add(list1);lists.add(list2);lists.add(list3);lists.add(list4);
System.out.println(getUnduplicateList(lists));
}
public static ArrayList<ArrayList<Integer>> getUnduplicateList( ArrayList<ArrayList<Integer>> lists) {
Iterator iterator = lists.iterator();
Set<ArrayList<Integer>> set = new HashSet<>();
while (iterator.hasNext()){
ArrayList<Integer> list = (ArrayList<Integer>) iterator.next();
set.add(list);
}
return new ArrayList<>(set);
}
}
Note that is a tiny example from my project and it will be very hard to use a solution that change many thing in this implementation.
So take into account that the getUnduplicateList should keep the same signature. the good idea will be to change only the implementation.
This program print the same list as the input. any idea please.
A couple notes on terminology—Set is a distinct data structure from List, where the former is unordered and does not allow duplicates, while the latter is a basic, linear collection, that's generally ordered, and allows duplicates. You seem to be using the terms interchangeably, which may be part of the issue you're having: Set is probably the appropriate data structure here.
That said, it seems that your code is relying on the List API, so we can follow that along. Note that you should, in general, code to the interface (List), rather than the specific class (ArrayList).
Additionally, consider using the Arrays.asList shorthand method for initializing a list (note that this returns an immutable list).
Finally, note that a HashSet eliminates duplicates by checking if both objects have the same hashCode. Lists containing the same elements are still not considered to be the same list unless the elements appear in the same order, and will typically not be treated as duplicates. Sets, however, implement equals and hashCode in such a way that two sets containing exactly the same elements are considered equal (order doesn't matter).
Using your original starting collection, you can convert each inner-list to a set. Then, eliminate duplicates from the outer collection. Finally, convert the inner-collections back to lists, to maintain compatibility with the rest of your code (if needed). This approach will work regardless of the size of the inner-lists.
You can simulate these steps using a Stream, and using method references to convert to and from the Set, as below.
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.stream.Collectors;
public class Example {
public static void main( String[] args ) {
List<Integer> list1 = Arrays.asList(1, 0);
List<Integer> list2 = Arrays.asList(0, 1);
List<Integer> list3 = Arrays.asList(3, 2);
List<Integer> list4 = Arrays.asList(2, 3);
List<List<Integer>> lists = Arrays.asList(list1, list2, list3, list4);
System.out.println(getUnduplicateList(lists));
}
public static List<List<Integer>> getUnduplicateList(List<List<Integer>> lists) {
return lists
.stream()
.map(HashSet::new)
.distinct()
.map(ArrayList::new)
.collect(Collectors.toList());
}
}
You need to convert the inner lists to sets as well.
Another solution is to sort your lists and then run them through distinct Although this is not very efficient and you will also obtain a set of sorted lists:
Set<List<Integer>> collect = set.stream()
.map(list -> {
list.sort(Comparator.comparingInt(Integer::intValue));
return list;
})
.distinct()
.collect(Collectors.toSet());
Related
I have multiple parallel ArrayList, I am sorting one of them (i.e. indexes).
ArrayList<Integer> indexes = {2,3,1};
ArrayList<String> names = {"two","three","one"};
ArrayList<String> upper = {"TWO","THREE","ONE"};
I want to synchronise the sorting of the ArrayList 'indexes' with the others ArrayList.
I am wondering in the Collections.sort(list) would give me a clue?
It sounds like you want to sort one array by its values, and then rearrange two other arrays so that the arrangement of their values matches the sort order of the first array.
An easy way to do this is to sort an array of indexes into the ordering that you want, and then use this to rearrange the other arrays into the same order. Since one of your arrays is already called "indexes" I'll call this new array the "permutation".
First, create the permutation array by generating index values from zero to size-1 and then sorting them. They end up being sorted not according to their own values, but by the values in your index array:
List<Integer> indexes = List.of(2,3,1);
List<String> names = List.of("two","three","one");
List<String> upper = List.of("TWO","THREE","ONE");
List<Integer> permutation = IntStream.range(0, indexes.size())
.boxed()
.sorted(comparing(indexes::get))
.collect(toCollection(ArrayList::new));
(So far, this is similar to the technique from Eritrean's answer.)
Now, we need to rearrange some data array according to the arrangement from the permutation array. Since we're doing this multiple times, here's a function that does that:
static <T> List<T> permute(List<Integer> permutation, List<T> list) {
return IntStream.range(0, permutation.size())
.mapToObj(i -> list.get(permutation.get(i)))
.toList();
}
Now it's a simple matter to apply this to each of the data arrays:
System.out.println(permute(permutation, indexes));
System.out.println(permute(permutation, names));
System.out.println(permute(permutation, upper));
The result is
[1, 2, 3]
[one, two, three]
[ONE, TWO, THREE]
Note that this creates new lists in the desired arrangement. It's possible to permute the data arrays in-place, but it's somewhat more work, though not intractable. (Search for "[java] permute array in place" for ideas.)
Create a priority list from your indexes and use the index of the elemnts as a sorting criteria:
public static void main(String args[]) {
List<Integer> indexes = new ArrayList<>(List.of(2,3,1));
List<String> names = new ArrayList<>(List.of("two","three","one"));
List<String> upper = new ArrayList<>(List.of("TWO","THREE","ONE"));
List<Integer> priority = IntStream.range(0, indexes.size())
.boxed()
.sorted(Comparator.comparingInt(indexes::get))
.collect(Collectors.toList());
names.sort(Comparator.comparingInt(i -> priority.indexOf(names.indexOf(i))));
upper.sort(Comparator.comparingInt(i -> priority.indexOf(upper.indexOf(i))));
indexes.sort(Comparator.comparingInt(i -> priority.indexOf(indexes.indexOf(i))));
System.out.println(indexes);
System.out.println(names);
System.out.println(upper);
}
You can use a container as an intermediary step (though, it may be better to modify your code to simply use the container instead of 3 separate lists):
public class Container {
final int id;
final String name;
final String upper;
... // constructor + getters (or create a record if you're using j14+)
}
public static void main(String args[]) {
List<Container> values = indexes.stream()
.sorted()
.map(index -> new Container(index, names.get(index), upper.get(index)))
.collect(Collectors.toList());
List<String> sortedNames = values.stream()
.map(value -> value.getName())
.collect(Collectors.toList());
List<String> sortedUpper = values.stream()
.map(value -> value.getUpper())
.collect(Collectors.toList());
}
I'd like to initialize a nested list without using a for-loop, the root list which is: cakeList will contain another ones eg.(100).
My code:
1. ArrayList<ArrayList<Integer>> cakeList = new ArrayList<>();
2. for (int i=0;i<100;i++) cakeList.add(new ArrayList<>());
I've tried:
import java.util.ArrayList;
public class Solution {
public static void main(String[] args) {
ArrayList<ArrayList<Integer>> cakeList = new ArrayList<>(100);
cakeList.forEach(e-> new ArrayList<>());
System.out.println(cakeList.size());
cakeList.get(0);
}
}
but as you maybe know, it complies, but throws an error al line #7 because cakeList is empty whe I try to cakeList.get(0).
IndexOutOfBoundsException: Index: 0, Size: 0
Thank you in advance!
It is convenient to use Stream::generate and Stream::limit to create predefined number of objects, so for the 2D list this would look as follows:
List<ArrayList<Integer>> list2D = Stream.generate(ArrayList<Integer>::new)
.limit(100) // Stream<ArrayList<Integer>>
.collect(Collectors.toList());
list2D.get(0).add(111); // 111 added to one entry
Also, IntStream with a fixed range may be used:
List<ArrayList<Integer>> list2D = IntStream.range(0, 100)
.mapToObj(i -> new ArrayList<Integer>())
.collect(Collectors.toList());
If it is important to have specific ArrayList<ArrayList> as a result, a collector Collectors.toCollection may be used:
ArrayList<ArrayList<Integer>> list2D = IntStream.range(0, 100)
.mapToObj(i -> new ArrayList<Integer>())
.collect(Collectors.toCollection(ArrayList::new));
There is also a method Collections.nCopies but it creates N copies of the same object which may have a side effect of applying a change in one element to all other "copy" elements in the list.
List<ArrayList<Integer>> cakeList = Collections.nCopies(100, new ArrayList<>());
cakeList.get(0).add(111); // 111 is added to "all" entries in cakeList
Your initialization is fine, but post initialization, your lists are all empty, as you would expect docs (see Constructors) .
Per the methods section of the docs,
cakeList.get(0); // "Returns the element at the specified position in this list."
Since your list is empty, there is no element at the specified position.
The only way to create an ArrayList that is non-empty is to use the constructor (see docs) which takes in a Collection.
Example:
ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
numbers.get(0); //will return 1
Your goal of not wanting to use a for-loop for initialization is understandable, but see this post for the speed comparison of the two methods. Note the method call
cakeList.forEach(e-> new ArrayList<>());
is equivalent to
Iterator<T> iter = cakeList.iterator();
while (iter.hasNext()) {
iter.next().add(new ArrayList<Integer>();
}
A conclusion you will reach is that there is no shortcut to filling arrays, it will take O(n) where n is your array size.
I'm using a set to get a list of duplicate items from an ArrayList (which is populated from a database)
void getDuplicateHashTest() {
List<BroadcastItem> allDataStoreItems = itemsDAO.getAllItems();
Set<BroadcastItem> setOfAllData = new HashSet<>(allDataStoreItems);
List<BroadcastItem> diff = new ArrayList<>(setOfAllData);
allDataStoreItems.removeAll(diff);
}
So at the last line, all the items which are not duplicates should be removed from the list of all items.
The problem is when I print allDataStoreItems.size() I get 0
The set and the sublist print the correct number of items.
What am I doing wrong?
List#removeAll removes all occurrences of the given elements, not just one of each (in contrast to List#remove which only removes the first occurrence). So setOfAllData contains one copy of each element in your list, and then you remove all occurrences of each of those elements, meaning you'll always end up with an empty list.
To know how to fix this I'd need to know more about what you want the result to be. Do you want one copy of each element removed? If so, you could do that with:
List<BroadcastItem> allDataStoreItems = itemsDAO.getAllItems();
Set<BroadcastItem> setOfAllData = new HashSet<>(allDataStoreItems);
setOfAllData.forEach(allDataStoreItems::remove);
Its simple if you want to store only duplicates find the below code.
Set<BroadcastItem> duplicates = new HashSet<>;
Set<BroadcastItem> allItems=new HashSet<>
for(BroadcastItem b:allDataStoreItems){
boolean x=allItems.add(b);
if(x==false){
duplicates.add(b);
}
}
As already pointed out in the answer by jacobm : The Collection#removeAll method will remove all occurrences of a particular element. But the alternative of creating a list and calling remove repeatedly is not really a good solution: On a List, the remove call will usually have O(n) complexity, so figuring out the duplicates like this will have quadratic complexity.
A better solution is the one that was already mentioned by shamsher Khan in his answer (+1!) : You can iterate over the list, and keep track of the elements that have already seen, using a Set.
This solution has a complexity of O(n).
It's not clear whether you want the list or the set of all duplicates. For example, when the input is [1, 2,2,2, 3], should the result be [2,2] or just [2]? However, you can simply compute the list of duplicates, and make its elements unique in a second step, if necessary.
Here is an example:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class FindDuplicatesInList
{
public static void main(String[] args)
{
List<Integer> list = Arrays.asList(0,1,1,1,2,3,3,4,5,6,7,7,7,8);
List<Integer> duplicates = computeDuplicates(list);
// Prints [1, 1, 3, 7, 7]
System.out.println(duplicates);
// Prints [1, 3, 7]
System.out.println(makeUnique(duplicates));
}
private static <T> List<T> makeUnique(List<? extends T> list)
{
return new ArrayList<T>(new LinkedHashSet<T>(list));
}
private static <T> List<T> computeDuplicates(List<? extends T> list)
{
Set<T> set = new HashSet<T>();
List<T> duplicates = new ArrayList<T>();
for (T element : list)
{
boolean wasNew = set.add(element);
if (!wasNew)
{
duplicates.add(element);
}
}
return duplicates;
}
}
I want to shuffle an ArrayList but based on some custom conditions:
if my array list was something like [1, 4, 5, 6, 9, 45, 67],
I want to shuffle it but make sure 5, 6, 9 always appear together.
Is there any method available in Collections class to do this?
I have tried doing this, but it throws ConcurrentModificationException
List<Integer> y= new ArrayList<>();
y.add(1);
y.add(4);
y.add(5);
y.add(6);
y.add(9);
y.add(45);
y.add(67);
List<Integer> z = y.subList(2, 5);
y.removeAll(z);
Collections.shuffle(y);
int index = ThreadLocalRandom.current()
.nextInt(0, y.size() + 1);
y.addAll(index,z);
It sounds like your data should really be a list of lists, especially since its likely that you will have more than 1 group that needs to stay together.
You can always flatten it when you need.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<List<Integer>> y = new ArrayList<List<Integer>>();
y.add(new ArrayList<Integer>(Arrays.asList(1)));
y.add(new ArrayList<Integer>(Arrays.asList(4)));
y.add(new ArrayList<Integer>(Arrays.asList(5, 6, 9)));
y.add(new ArrayList<Integer>(Arrays.asList(45)));
y.add(new ArrayList<Integer>(Arrays.asList(67)));
Collections.shuffle(y);
List<Integer> flatList = new ArrayList<>();
y.forEach(flatList::addAll);
}
}
A simple way of doing this is to store your target elements in a separate List:
List<Integer> target = new ArrayList<>();
target.add(5);
target.add(6);
target.add(9);
Then shuffle your main list:
Collections.shuffle(y);
Then get a random number from 0 -> y.size().
Random ran = new Random();
int pos = ran.nextInt(y.size());
And insert your target list into your original list:
y.addAll(pos, target);
Note: this assumes your original list has the target 3 numbers removed already.
Without seeing your code, I'd think that the ConcurrentModificationExceptions are thrown because you try to remove the group elements from the list or to add them back in while iterating it. Changing a collection while iterating it leads to those exceptions: Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop.
It will get a lot easier if you do not treat the groups as the exception, but as the norm. What I mean by this is that you should convert your List<?> into a List<List<?>> where each sub list contains either one element or one of the groups. You can then shuffle that list easily with Collections.shuffle() and flatten it again.
Look at this rough implementation:
List<Integer> ints = new ArrayList<>(asList(2, 3, 5, 4, 8, 7, 11, 55));
List<List<Integer>> groups = asList(asList(5, 4), asList(7, 11));
// remove all the grouped elements from the list
groups.forEach(ints::removeAll);
// wrap the single elements into list and join them with the groups
List<List<Integer>> wrapped = Stream.concat(ints.stream().map(Arrays::asList),
groups.stream())
.collect(Collectors.toList());
Collections.shuffle(wrapped);
// flatten the list into single elements again
List<Integer> shuffled = wrapped.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println(shuffled); // e.g. [55, 3, 7, 11, 2, 8, 5, 4]
// ----- ----
Note that while this is quite readable, it is probably not the most efficient or error proof solution. But it should give you an idea how to tackle the problem.
Edit after comment from Gonen I. Here is a helper method to only remove the exact sequences and not random parts of them all over the list:
private static <T> void removeSequence(List<T> list, List<T> sequence)
{
int indexOfSubList = Collections.lastIndexOfSubList(list, sequence);
while (indexOfSubList != -1)
{
for (int j = 0; j < sequence.size(); j++)
{
list.remove(indexOfSubList);
}
indexOfSubList = Collections.lastIndexOfSubList(list, sequence);
}
}
Use it by replacing groups.forEach(ints::removeAll); by groups.forEach(group -> removeSequence(ints, group));
Suppose I have the code:
List<String> list = new ArrayList<String>();
list.add("A"); //want
list.add("B");
list.add("C");
list.add("D"); //want
list.add("E"); //want
and I'd like to create a new list that only has the elements "A", "D", E".
The List class has a method subList that will work if you only have 1 single continuous range, but what if you want multiple ranges, or multiple discrete elements?
Is there any method in java or library that allows me to do something like:
List<String> subList = NeatListUtilities.subList(list, 0, 3, 4);
None that I know of, however it is fairly simple to implement.
(No error / range checking, assumed use of ArrayList)
public static <T> List<T> subList(List<T> src, int... indices) {
List<T> result = new ArrayList<>(indices.length);
for(int i : indices) {
result.add(src.get(i));
}
return result;
}
A concise way to do what you want with Stream:
List<String> subList = IntStream.of(0, 3, 4)
.mapToObj(i -> list.get(i))
.collect(Collectors.toList());
So, probably you don't need a library for this.