Looping over All Possible combinations of ArrayList - java

I want to loop over the same list to process possible combinations of that list. For example : From a list consisting [1,2,3] I want to get an ArrayList which looks like this: [[1,2], [1,3], [2,3]]
I am processing a list of nodes instead of integers. For now i am trying something like the following :
ArrayList<ArrayList<Node>> saveList = new ArrayList<ArrayList<Node>>();
for (Node n1 : nodes)
ArrayList<Node> saveList2 = new ArrayList<Node>();
for (Node n2 : nodes)
if n2.name == n1.name
continue;
saveList2.add(n1).add(n2);
if (!saveList.containsAll(saveList2))
then process graph;
else continue;
I don't process the same node and avoid the combination already processed. Is there a better solution ?

Using a combinatorics library may be a bit overkill in your case. Your task is indeed finding combinations of size 2, but the fact that the size is two simplifies it drastically.
A good old index-based for-loop does the trick here, with no check for duplicates necessary. Notice how the second loop starts from i + 1. Go over the algorithm in a scratchpad and you will see how this avoids duplicates.
List<List<Node>> pairs = new ArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
for (int j = i + 1; j < nodes.size(); j++) {
pairs.add(Arrays.asList(nodes.get(i), nodes.get(j)));
}
}

If the task is not of academic nature or does not consist of implementing an algorithm, I would use a library and focus on the core of the task the application is supposed to solve. Such a library would be for example combinatoricslib3. Google guava or Apache commons certainly have similar methods. With combinatoricslib3 the solution to your issue above would be a one-liner:
Generator.combination(1,2,3)
.simple(2)
.stream()
.forEach(System.out::println);
Output:
[1, 2]
[1, 3]
[2, 3]
or something like:
List<List<String>> result = Generator.combination("FOO", "BAR", "BAZ")
.simple(2)
.stream()
.collect(Collectors.toList());
System.out.println(result);
to get
[[FOO, BAR], [FOO, BAZ], [BAR, BAZ]]
It works not only for primitive data types like ints or strings as shown above, you can also use your own custom objects and use a list of your objects as a parameter. Assuming you have a Node class:
public class Node {
String name;
// getter, setter, toString ...
}
List<Node> nodeList = List.of(new Node("node1"), new Node("node2"), new Node("node3"));
Generator.combination(nodeList)
.simple(2)
.stream()
.forEach(System.out::println);
Output:
[Node(name=node1), Node(name=node2)]
[Node(name=node1), Node(name=node3)]
[Node(name=node2), Node(name=node3)]
To use the lib add the dependency to your pom.xml or download the jar and add to classpath. mvn dependency:
<dependency>
<groupId>com.github.dpaukov</groupId>
<artifactId>combinatoricslib3</artifactId>
<version>3.3.2</version>
</dependency>

Try this.
static <T> List<List<T>> combinations(List<T> list, int n) {
int length = list.size();
List<List<T>> result = new ArrayList<>();
T[] selections = (T[])new Object[n];
new Object() {
void select(int start, int index) {
if (index >= n)
result.add(List.of(selections));
else if (start < length){
selections[index] = list.get(start);
select(start + 1, index + 1);
select(start + 1, index);
}
}
}.select(0, 0);
return result;
}
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 3);
System.out.println(combinations(list, 2));
}
output:
[[1, 2], [1, 3], [2, 3]]

Related

What is an efficient in place way of sorting an array based on an index array?

In certain machine learning algorithms the columns of the matrix are rotated and sorted based relevance of each column. New data to come should be transformed in the same order. So if my initial sort gives me [0,2,1,3] as an index array, than new data should also be ordered in this way: first, third, second, fourth element. That's why I wanted to create a sorted index array, that could later on be used as a source for reordering new data. I've managed to do that in the implementation below.
My question is about the use of the index array for reoordering new data. In my implementation I first create a clone of the new data array. Than it's easy to just copy elements from my source array to the proper index in the target array. Is this the most efficient way to do it? Or is there a more efficient way, for instance by sorting the data in place?
import java.util.stream.*;
import java.util.*;
public class IndexSorter<T> {
private final int[] indices;
private final int[] reverted;
public IndexSorter(T[] data, Comparator<T> comparator){
// generate index array based on initial data and a comparator:
indices = IntStream.range(0, data.length)
.boxed()
.sorted( (a, b) -> comparator.compare(data[a],data[b]))
.mapToInt(a -> a)
.toArray();
// also create an index array to be able to revert the sort
reverted = new int[indices.length];
for(int i=0;i<indices.length;i++){
reverted[indices[i]] = i;
}
}
// sort new data based on initial array
public T[] sort(T[] data){
return sortUsing(data, indices);
}
// revert sorted data
public T[] revert(T[] data){
return sortUsing(data, reverted);
}
private T[] sortUsing(T[] data, int[] ind){
if(data.length != indices.length){
throw new IllegalArgumentException(
String.format("Data length does not match: (%s, should be: %s) "
, data.length, indices.length));
}
// create a copy of the data (efficively this just creates a new array)
T[] sorted = data.clone();
// fill the copy with the sorted data
IntStream.range(0, ind.length)
.forEach(i -> sorted[i]=data[ind[i]]);
return sorted;
}
}
class App {
public static void main(String args[]){
IndexSorter<String> sorter = new IndexSorter<>(args, String::compareTo);
String[] data = sorter.sort(args);
System.out.println(Arrays.toString(data));
data = sorter.revert(data);
System.out.println(Arrays.toString(data));
data = IntStream.range(0, data.length)
.mapToObj(Integer::toString)
.toArray(String[]::new);
data = sorter.sort(data);
System.out.println(Arrays.toString(data));
data = sorter.revert(data);
System.out.println(Arrays.toString(data));
}
}
I would not recommend copying data. Because this is a memory allocation that can be quite expensive. It is much more efficient to sort data in place with library methods, like as Arrays.sort
I've found a way to sort in place, using a BitSet to keep track of what indexes are having the right element. It is in the method sortUsing. I hope someone will have a use for this algorithm.
You can test it like this:
java App this is just some random test to show the result
Then outcome will first show you the sorted result, than the reverted result.
The same index array is also used for ordering an int array of indexes, and the reverted version:
[is, just, random, result, show, some, test, the, this, to]
[this, is, just, some, random, test, to, show, the, result]
[1, 2, 4, 9, 7, 3, 5, 8, 0, 6]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Here is the code:
import java.util.stream.*;
import java.util.*;
public class IndexSorter<T> {
private final int[] indices;
private final int[] reverted;
private final BitSet done;
public IndexSorter(T[] data, Comparator<T> comparator){
// generate index array based on initial data and a comparator:
indices = IntStream.range(0, data.length)
.boxed()
.sorted( (a, b) -> comparator.compare(data[a],data[b]))
.mapToInt(a -> a)
.toArray();
// also create an index array to be able to revert the sort
reverted = new int[indices.length];
for(int i=0;i<indices.length;i++){
reverted[indices[i]] = i;
}
done = new BitSet(data.length);
}
// sort new data based on initial array
public void sort(T[] data){
sortUsing(data, indices);
}
// revert sorted data
public void revert(T[] data){
sortUsing(data, reverted);
}
private void sortUsing(T[] data, int[] ind){
if(data.length != indices.length){
throw new IllegalArgumentException(
String.format("Data length does not match: (%s, should be: %s) "
, data.length, indices.length));
}
int ia=0, ib=0, x = 0;
T a = null, b = null;
for (int i=0; i< data.length && done.cardinality()<data.length; i++){
ia = i;
ib = ind[ia];
if(done.get(ia)){ // index is already done
continue;
}
if(ia==ib){ // element is at the right place
done.set(ia);
continue;
}
x = ia; // start a loop at x = ia
// some next index will be x again eventually
a = data[ia]; // keep element a as the last value after the loop
while(ib!=x && !done.get(ia) ){
b = data[ib]; // element from index b must go to index a
data[ia]=b;
done.set(ia);
ia = ib;
ib = ind[ia]; // get next index
}
data[ia]=a; // set value a to last index
done.set(ia);
}
done.clear();
}
}
class App {
public static void main(String args[]){
IndexSorter<String> sorter = new IndexSorter<>(args, String::compareTo);
sorter.sort(args);
System.out.println(Arrays.toString(args));
sorter.revert(args);
System.out.println(Arrays.toString(args));
String[] data = IntStream.range(0, args.length)
.mapToObj(Integer::toString)
.toArray(String[]::new);
sorter.sort(data);
System.out.println(Arrays.toString(data));
sorter.revert(data);
System.out.println(Arrays.toString(data));
}
}

Merge sets when two elements in common

This is the follow up of compare sets
I have
Set<Set<Node>> NestedSet = new HashSet<Set<Node>>();
[[Node[0], Node[1], Node[2]], [Node[0], Node[2], Node[6]], [Node[3], Node[4], Node[5]] [Node[2], Node[6], Node[7]] ]
I want to merge the sets when there are two elements in common. For example 0,1,2 and 0,2,6 has two elements in common so merging them to form [0,1,2,6].
Again [0,1,2,6] and [2,6,7] has 2 and 6 common. so merging them and getting [0,1,2,6,7].
The final output should be :
[ [Node[0], Node[1], Node[2], Node[6], Node[7]], [Node[3], Node[4], Node[5]] ]
I tried like this :
for (Set<Node> s1 : NestedSet ) {
Optional<Set<Node>> findFirst = result.stream().filter(p -> { HashSet<Node> temp = new HashSet<>(s1);
temp.retainAll(p);
return temp.size() == 2; }).findFirst();
if (findFirst.isPresent()){
findFirst.get().addAll(s1);
}
else {
result.add(s1);
}
}
But the result I got was :
[[Node[0], Node[1], Node[2], Node[6], Node[7]], [Node[3], Node[4], Node[5]], [Node[0], Node[2], Node[6], Node[7]]]
Any idea ? Is there any way to get the desired output?
Some considerations:
Each time you apply a merge, you have to restart the procedure and iterate over the modified collection. Because of this, the iteration order of the input set is important, if you want your code to be deterministic you may want to use collections that give guarantees over their iteration order (e.g. use LinkedHashSet (not HashSet) or List.
Your current code has side effects as it modifies the supplied sets when merging. In general I think it helps to abstain from creating side effects whenever possible.
The following code does what you want:
static <T> List<Set<T>> mergeSets(Collection<? extends Set<T>> unmergedSets) {
final List<Set<T>> mergedSets = new ArrayList<>(unmergedSets);
List<Integer> mergeCandidate = Collections.emptyList();
do {
mergeCandidate = findMergeCandidate(mergedSets);
// apply the merge
if (!mergeCandidate.isEmpty()) {
// gather the sets to merge
final Set<T> mergedSet = Sets.union(
mergedSets.get(mergeCandidate.get(0)),
mergedSets.get(mergeCandidate.get(1)));
// removes both sets using their index, starts with the highest index
mergedSets.remove(mergeCandidate.get(0).intValue());
mergedSets.remove(mergeCandidate.get(1).intValue());
// add the mergedSet
mergedSets.add(mergedSet);
}
} while (!mergeCandidate.isEmpty());
return mergedSets;
}
// O(n^2/2)
static <T> List<Integer> findMergeCandidate(List<Set<T>> sets) {
for (int i = 0; i < sets.size(); i++) {
for (int j = i + 1; j < sets.size(); j++) {
if (Sets.intersection(sets.get(i), sets.get(j)).size() == 2) {
return Arrays.asList(j, i);
}
}
}
return Collections.emptyList();
}
For testing this method I created two helper methods:
static Set<Integer> set(int... ints) {
return new LinkedHashSet<>(Ints.asList(ints));
}
#SafeVarargs
static <T> Set<Set<T>> sets(Set<T>... sets) {
return new LinkedHashSet<>(Arrays.asList(sets));
}
These helper methods allow to write very readable tests, for example (using the numbers from the question):
public static void main(String[] args) {
// prints [[2, 6, 7, 0, 1]]
System.out.println(mergeSets(sets(set(0, 1, 2, 6), set(2, 6, 7))));
// prints [[3, 4, 5], [0, 2, 6, 1, 7]]
System.out.println(
mergeSets(sets(set(0, 1, 2), set(0, 2, 6), set(3, 4, 5), set(2, 6, 7))));
}
I'm not sure why you are getting that result, but I do see another problem with this code: It is order-dependent. For example, even if the code worked as intended, it would matter whether [Node[0], Node[1], Node[2]] is compared first to [Node[0], Node[2], Node[6]] or [Node[2], Node[6], Node[7]]. But Sets don't have a defined order, so the result is either non-deterministic or implementation-dependent, depending on how you look at it.
If you really want deterministic order-dependent operations here, you should be using List<Set<Node>>, rather than Set<Set<Node>>.
Here's a clean approach using recursion:
public static <T> Set<Set<T>> mergeIntersectingSets(Collection<? extends Set<T>> unmergedSets) {
boolean edited = false;
Set<Set<T>> mergedSets = new HashSet<>();
for (Set<T> subset1 : unmergedSets) {
boolean merged = false;
// if at least one element is contained in another subset, then merge the subsets
for (Set<T> subset2 : mergedSets) {
if (!Collections.disjoint(subset1, subset2)) {
subset2.addAll(subset1);
merged = true;
edited = true;
}
}
// otherwise, add the current subset as a new subset
if (!merged) mergedSets.add(subset1);
}
if (edited) return mergeIntersectingSets(mergedSets); // continue merging until we reach a fixpoint
else return mergedSets;
}

Combine values with Java8 stream

If I have a list with integers, is there a way to construct another list, where integers are summed if the difference to the head of the new list is below a threashold? I would like to solve this using Java 8 streams. It should work similar to the Scan operator of RxJava.
Example: 5, 2, 2, 5, 13
Threashold: 2
Result: 5, 9, 13
Intermediate results:
5
5, 2
5, 4 (2 and 2 summed)
5, 9 (4 and 5 summed)
5, 9, 13
Sequential Stream solution may look like this:
List<Integer> result = Stream.of(5, 2, 2, 5, 13).collect(ArrayList::new, (list, n) -> {
if(!list.isEmpty() && Math.abs(list.get(list.size()-1)-n) < 2)
list.set(list.size()-1, list.get(list.size()-1)+n);
else
list.add(n);
}, (l1, l2) -> {throw new UnsupportedOperationException();});
System.out.println(result);
Though it looks not much better as good old solution:
List<Integer> input = Arrays.asList(5, 2, 2, 5, 13);
List<Integer> list = new ArrayList<>();
for(Integer n : input) {
if(!list.isEmpty() && Math.abs(list.get(list.size()-1)-n) < 2)
list.set(list.size()-1, list.get(list.size()-1)+n);
else
list.add(n);
}
System.out.println(list);
Seems that your problem is not associative, so it cannot be easily parallelized. For example, if you split the input into two groups like this (5, 2), (2, 5, 13), you cannot say whether the first two items of the second group should be merged until the first group is processed. Thus I cannot specify the proper combiner function.
As Tagir Valeev observed, (+1) the combining function is not associative, so reduce() won't work, and it's not possible to write a combiner function for a Collector. Instead, this combining function needs to be applied left-to-right, with the previous partial result being fed into the next operation. This is called a fold-left operation, and unfortunately Java streams don't have such an operation.
(Should they? Let me know.)
It's possible to sort-of write your own fold-left operation using forEachOrdered while capturing and mutating an object to hold partial state. First, let's extract the combining function into its own method:
// extracted from Tagir Valeev's answer
void combine(List<Integer> list, int n) {
if (!list.isEmpty() && Math.abs(list.get(list.size()-1)-n) < 2)
list.set(list.size()-1, list.get(list.size()-1)+n);
else
list.add(n);
}
Then, create the initial result list and call the combining function from within forEachOrdered:
List<Integer> result = new ArrayList<>();
IntStream.of(5, 2, 2, 5, 13)
.forEachOrdered(n -> combine(result, n));
This gives the desired result of
[5, 9, 13]
In principle this can be done on a parallel stream, but performance will probably degrade to sequential given the semantics of forEachOrdered. Also note that the forEachOrdered operations are performed one at a time, so we needn't worry about thread safety of the data we're mutating.
I know that the Stream's masters "Tagir Valeev" and "Stuart Marks" already pointed out that reduce() will not work because the combining function is not associative, and I'm risking a couple of downvotes here. Anyway:
What about if we force the stream to be sequential? Wouldn't we be able then to use reduce? Isn't the associativity property only needed when using parallelism?
Stream<Integer> s = Stream.of(5, 2, 2, 5, 13);
LinkedList<Integer> result = s.sequential().reduce(new LinkedList<Integer>(),
(list, el) -> {
if (list.isEmpty() || Math.abs(list.getLast() - el) >= 2) {
list.add(el);
} else {
list.set(list.size() - 1, list.getLast() + el);
}
return list;
}, (list1, list2) -> {
//don't really needed, as we are sequential
list1.addAll(list2); return list1;
});
Java 8 way is define custom IntSpliterator class:
static class IntThreasholdSpliterator extends Spliterators.AbstractIntSpliterator {
private PrimitiveIterator.OfInt it;
private int threashold;
private int sum;
public IntThreasholdSpliterator(int threashold, IntStream stream, long est) {
super(est, ORDERED );
this.it = stream.iterator();
this.threashold = threashold;
}
#Override
public boolean tryAdvance(IntConsumer action) {
if(!it.hasNext()){
return false;
}
int next = it.nextInt();
if(next<threashold){
sum += next;
}else {
action.accept(next + sum);
sum = 0;
}
return true;
}
}
public static void main( String[] args )
{
IntThreasholdSpliterator s = new IntThreasholdSpliterator(3, IntStream.of(5, 2, 2, 5, 13), 5);
List<Integer> rs= StreamSupport.intStream(s, false).mapToObj(Integer::valueOf).collect(toList());
System.out.println(rs);
}
Also you can hack it as
List<Integer> list = Arrays.asList(5, 2, 2, 5, 13);
int[] sum = {0};
list = list.stream().filter(s -> {
if(s<=2) sum[0]+=s;
return s>2;
}).map(s -> {
int rs = s + sum[0];
sum[0] = 0;
return rs;
}).collect(toList());
System.out.println(list);
But I am not sure that this hack is good idea for production code.

Removing Duplicate Values from ArrayList

I have one Arraylist of String and I have added Some Duplicate Value in that. and i just wanna remove that Duplicate value So how to remove it.
Here Example I got one Idea.
List<String> list = new ArrayList<String>();
list.add("Krishna");
list.add("Krishna");
list.add("Kishan");
list.add("Krishn");
list.add("Aryan");
list.add("Harm");
System.out.println("List"+list);
for (int i = 1; i < list.size(); i++) {
String a1 = list.get(i);
String a2 = list.get(i-1);
if (a1.equals(a2)) {
list.remove(a1);
}
}
System.out.println("List after short"+list);
But is there any Sufficient way remove that Duplicate form list. with out using For loop ?
And ya i can do it by using HashSet or some other way but using array list only.
would like to have your suggestion for that. thank you for your answer in advance.
You can create a LinkedHashSet from the list. The LinkedHashSet will contain each element only once, and in the same order as the List. Then create a new List from this LinkedHashSet. So effectively, it's a one-liner:
list = new ArrayList<String>(new LinkedHashSet<String>(list))
Any approach that involves List#contains or List#remove will probably decrease the asymptotic running time from O(n) (as in the above example) to O(n^2).
EDIT For the requirement mentioned in the comment: If you want to remove duplicate elements, but consider the Strings as equal ignoring the case, then you could do something like this:
Set<String> toRetain = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
toRetain.addAll(list);
Set<String> set = new LinkedHashSet<String>(list);
set.retainAll(new LinkedHashSet<String>(toRetain));
list = new ArrayList<String>(set);
It will have a running time of O(n*logn), which is still better than many other options. Note that this looks a little bit more complicated than it might have to be: I assumed that the order of the elements in the list may not be changed. If the order of the elements in the list does not matter, you can simply do
Set<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
set.addAll(list);
list = new ArrayList<String>(set);
if you want to use only arraylist then I am worried there is no better way which will create a huge performance benefit. But by only using arraylist i would check before adding into the list like following
void addToList(String s){
if(!yourList.contains(s))
yourList.add(s);
}
In this cases using a Set is suitable.
You can make use of Google Guava utilities, as shown below
list = ImmutableSet.copyOf(list).asList();
This is probably the most efficient way of eliminating the duplicates from the list and interestingly, it preserves the iteration order as well.
UPDATE
But, in case, you don't want to involve Guava then duplicates can be removed as shown below.
ArrayList<String> list = new ArrayList<String>();
list.add("Krishna");
list.add("Krishna");
list.add("Kishan");
list.add("Krishn");
list.add("Aryan");
list.add("Harm");
System.out.println("List"+list);
HashSet hs = new HashSet();
hs.addAll(list);
list.clear();
list.addAll(hs);
But, of course, this will destroys the iteration order of the elements in the ArrayList.
Shishir
Java 8 stream function
You could use the distinct function like above to get the distinct elements of the list,
stringList.stream().distinct();
From the documentation,
Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.
Another way, if you do not wish to use the equals method is by using the collect function like this,
stringList.stream()
.collect(Collectors.toCollection(() ->
new TreeSet<String>((p1, p2) -> p1.compareTo(p2))
));
From the documentation,
Performs a mutable reduction operation on the elements of this stream using a Collector.
Hope that helps.
Simple function for removing duplicates from list
private void removeDuplicates(List<?> list)
{
int count = list.size();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
if (list.get(i).equals(list.get(j)))
{
list.remove(j--);
count--;
}
}
}
}
Example:
Input: [1, 2, 2, 3, 1, 3, 3, 2, 3, 1, 2, 3, 3, 4, 4, 4, 1]
Output: [1, 2, 3, 4]
List<String> list = new ArrayList<String>();
list.add("Krishna");
list.add("Krishna");
list.add("Kishan");
list.add("Krishn");
list.add("Aryan");
list.add("Harm");
HashSet<String> hs=new HashSet<>(list);
System.out.println("=========With Duplicate Element========");
System.out.println(list);
System.out.println("=========Removed Duplicate Element========");
System.out.println(hs);
I don't think the list = new ArrayList<String>(new LinkedHashSet<String>(list)) is not the best way , since we are using the LinkedHashset(We could use directly LinkedHashset instead of ArrayList),
Solution:
import java.util.ArrayList;
public class Arrays extends ArrayList{
#Override
public boolean add(Object e) {
if(!contains(e)){
return super.add(e);
}else{
return false;
}
}
public static void main(String[] args) {
Arrays element=new Arrays();
element.add(1);
element.add(2);
element.add(2);
element.add(3);
System.out.println(element);
}
}
Output:
[1, 2, 3]
Here I am extending the ArrayList , as I am using the it with some changes by overriding the add method.
public List<Contact> removeDuplicates(List<Contact> list) {
// Set set1 = new LinkedHashSet(list);
Set set = new TreeSet(new Comparator() {
#Override
public int compare(Object o1, Object o2) {
if(((Contact)o1).getId().equalsIgnoreCase(((Contact)2).getId()) ) {
return 0;
}
return 1;
}
});
set.addAll(list);
final List newList = new ArrayList(set);
return newList;
}
This will be the best way
List<String> list = new ArrayList<String>();
list.add("Krishna");
list.add("Krishna");
list.add("Kishan");
list.add("Krishn");
list.add("Aryan");
list.add("Harm");
Set<String> set=new HashSet<>(list);
It is better to use HastSet
1-a) A HashSet holds a set of objects, but in a way that it allows you to easily and quickly determine whether an object is already in the set or not. It does so by internally managing an array and storing the object using an index which is calculated from the hashcode of the object. Take a look here
1-b) HashSet is an unordered collection containing unique elements. It has the standard collection operations Add, Remove, Contains, but since it uses a hash-based implementation, these operation are O(1). (As opposed to List for example, which is O(n) for Contains and Remove.) HashSet also provides standard set operations such as union, intersection, and symmetric difference.Take a look here
2) There are different implementations of Sets. Some make insertion and lookup operations super fast by hashing elements. However that means that the order in which the elements were added is lost. Other implementations preserve the added order at the cost of slower running times.
The HashSet class in C# goes for the first approach, thus not preserving the order of elements. It is much faster than a regular List. Some basic benchmarks showed that HashSet is decently faster when dealing with primary types (int, double, bool, etc.). It is a lot faster when working with class objects. So that point is that HashSet is fast.
The only catch of HashSet is that there is no access by indices. To access elements you can either use an enumerator or use the built-in function to convert the HashSet into a List and iterate through that.Take a look here
Without a loop, No! Since ArrayList is indexed by order rather than by key, you can not found the target element without iterate the whole list.
A good practice of programming is to choose proper data structure to suit your scenario. So if Set suits your scenario the most, the discussion of implementing it with List and trying to find the fastest way of using an improper data structure makes no sense.
public static void main(String[] args) {
#SuppressWarnings("serial")
List<Object> lst = new ArrayList<Object>() {
#Override
public boolean add(Object e) {
if(!contains(e))
return super.add(e);
else
return false;
}
};
lst.add("ABC");
lst.add("ABC");
lst.add("ABCD");
lst.add("ABCD");
lst.add("ABCE");
System.out.println(lst);
}
This is the better way
list = list.stream().distinct().collect(Collectors.toList());
This could be one of the solutions using Java8 Stream API. Hope this helps.
public void removeDuplicates() {
ArrayList<Object> al = new ArrayList<Object>();
al.add("java");
al.add('a');
al.add('b');
al.add('a');
al.add("java");
al.add(10.3);
al.add('c');
al.add(14);
al.add("java");
al.add(12);
System.out.println("Before Remove Duplicate elements:" + al);
for (int i = 0; i < al.size(); i++) {
for (int j = i + 1; j < al.size(); j++) {
if (al.get(i).equals(al.get(j))) {
al.remove(j);
j--;
}
}
}
System.out.println("After Removing duplicate elements:" + al);
}
Before Remove Duplicate elements:
[java, a, b, a, java, 10.3, c, 14, java, 12]
After Removing duplicate elements:
[java, a, b, 10.3, c, 14, 12]
Using java 8:
public static <T> List<T> removeDuplicates(List<T> list) {
return list.stream().collect(Collectors.toSet()).stream().collect(Collectors.toList());
}
In case you just need to remove the duplicates using only ArrayList, no other Collection classes, then:-
//list is the original arraylist containing the duplicates as well
List<String> uniqueList = new ArrayList<String>();
for(int i=0;i<list.size();i++) {
if(!uniqueList.contains(list.get(i)))
uniqueList.add(list.get(i));
}
Hope this helps!
private static void removeDuplicates(List<Integer> list)
{
Collections.sort(list);
int count = list.size();
for (int i = 0; i < count; i++)
{
if(i+1<count && list.get(i)==list.get(i+1)){
list.remove(i);
i--;
count--;
}
}
}
public static List<String> removeDuplicateElements(List<String> array){
List<String> temp = new ArrayList<String>();
List<Integer> count = new ArrayList<Integer>();
for (int i=0; i<array.size()-2; i++){
for (int j=i+1;j<array.size()-1;j++)
{
if (array.get(i).compareTo(array.get(j))==0) {
count.add(i);
int kk = i;
}
}
}
for (int i = count.size()+1;i>0;i--) {
array.remove(i);
}
return array;
}
}

Index related sorting problem in Java

This is the requirement where I am facing problem finding the solution.
Problem:
I have ArrayList with data 20, 10, 30, 50, 40, 10.
If we sort this in ascending order the result will be 10, 10, 20, 30, 40, 50.
But I need the result as 3, 1, 4, 6, 5, 2.(The index of each element after sorting).
Strictly this should work even if there are repetitive elements in the list.
Please share your idea/approach solving this problem.
Here is my solution. We define a comparator to sort a list of indices based on the corresponding object in the list. That gives us a list of indices which is effectively a map: indices[i] = x means that the element at location x in the original list is at element i in the sorted list. We can then create a reverse mapping easily enough.
Output is the indices starting from 0: [2, 0, 3, 5, 4, 1]
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class LookupComparator<T extends Comparable<T>> implements Comparator<Integer> {
private ArrayList<T> _table;
public LookupComparator(ArrayList<T> table) {
_table = table;
}
public int compare(Integer o1, Integer o2) {
return _table.get(o1).compareTo(_table.get(o2));
}
}
public class Test {
public static <T extends Comparable<T>> ArrayList<Integer> indicesIfSorted(ArrayList<T> list) {
ArrayList<Integer> indices = new ArrayList<Integer>();
for (int i = 0; i < list.size(); i++)
indices.add(i);
Collections.sort(indices, new LookupComparator(list));
ArrayList<Integer> finalResult = new ArrayList<Integer>();
for (int i = 0; i < list.size(); i++)
finalResult.add(0);
for (int i = 0; i < list.size(); i++)
finalResult.set(indices.get(i), i);
return finalResult;
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(20);
list.add(10);
list.add(30);
list.add(50);
list.add(40);
list.add(10);
ArrayList<Integer> indices = indicesIfSorted(list);
System.out.println(indices);
}
}
My idea is creating 1 more attribute call index beside your value (in each data of aray). It will hold your old index, then u can take it out for using.
Building off what Hury said, I think the easiest way I can see to do this is to make a new data type that looks something like:
public class Foo {
private Integer value;
private int origPosition;
private int sortedPosition;
/*Constructors, getters, setters, etc... */
}
And some psuedo code for what to do with it...
private void printSortIndexes(ArrayList<Integer> integerList) {
// Create an ArrayList<Foo> from integerList - O(n)
// Iterate over the list setting the origPosition on each item - O(n)
// Sort the list based on value
// Iterate over the list setting the sortedPosition on each item - O(n)
// Resort the list based on origPositon
// Iterate over the lsit and print the sortedPositon - O(n)
}
That won't take long to implement, but it is horribly inefficient. You are throwing in an extra 4 O(n) operations, and each time you add or remove anything from your list, all the positions stored in the objects are invalidated - so you'd have to recaculate everything. Also it requires you to sort the list twice.
So if this is a one time little problem with a small-ish data set it will work, but if you trying to make something to use for a long time, you might want to try to think of a more elegant way to do it.
Here is the approach of adding an index to each element, written out in Scala. This approach makes the most sense.
list.zipWithIndex.sortBy{ case (elem, index) => elem }
.map{ case (elem, index) => index }
In Java you would need to create a new object that implements comperable.
class IndexedItem implements Comparable<IndexedItem> {
int index;
int item;
public int compareTo(IndexItem other) {
return this.item - other.item;
}
}
You could then build a list of IndexedItems, sort it with Collection.sort, and then pull out the indices.
You could also use Collections.sort on the original list followed by calls to indexOf.
for (int elem : originalList) {
int index = newList.indexOf(elem);
newList.get(index) = -1; // or some other value that won't be found in the list
indices.add(index);
}
This would be very slow (all the scans of indexOf), but would get the job done if you only need to do it a few times.
A simplistic approach would be to sort the list; then loop on the original list, find the index of the element in the sorted list and insert that into another list.
so a method like
public List<Integer> giveSortIndexes(List<Integer> origList) {
List<Integer> retValue = new ArrayList<Integer>();
List<Integer> originalList = new ArrayList<Integer>(origList);
Collections.sort(origList);
Map<Integer, Integer> duplicates = new HashMap<Integer, Integer>();
for (Integer i : originalList) {
if(!duplicates.containsKey(i)) {
retValue.add(origList.indexOf(i) + 1);
duplicates.put(i, 1);
} else {
Integer currCount = duplicates.get(i);
retValue.add(origList.indexOf(i) + 1 + currCount);
duplicates.put(i, currCount + 1);
}
}
return retValue;
}
I haven't tested the code and it might need some more handling for duplicates.

Categories