Reduce repeated elements in a list - java

To compact in a new list without repeated elements, there may be null elements. I need help .
I have this error "Tester Error (Individual Delivery 4): the method returns []instead of [1] when invoked with [1]".
public <E> PositionList<E> compactar (Iterable<E> lista)
{
if(lista == null){
throw new IllegalArgumentException();
}// if
PositionList<E> newLista = new NodePositionList<E>();
Iterator<E>iterator = lista.iterator();
while(iterator.hasNext())
{
if(eqNull(iterator,iterator.next())) {
newLista.addLast(iterator.next());
}//if
}//while
return newLista;
}//compactar
//-------------assistant--------------------------------------------------------------
public static boolean eqNull (Object o1, Object o2)
{
return o1 == o2 || o1!= null && o1.equals(o2);
}// de eqNUll
}// Operacioncompactar

Not clear if you want to remove all dupes or just remove nulls. These remove dupes.
List<Comparable> l = ...; // The source list
List<Comparable> nl = new ArrayList<>(); // New list without dupes
Collections.sort(l);
Comparable last = UUIDs.randomUUID().toString(); // This one will never be in our source list
for (Comparable c: l) {
if (!Objects.equal(c, last)) { // Or some other null-safe equals function
nl.add(c);
}
last = c;
}
Running time O(nlogn). Elements in list of course need to implement a sensible equals() and hashCode().
Set<?> set = new HashSet<String>(); // Track "seen"
List<?> l = ...; // Original list
List<?> nl = new ArrayList<>(); // New list without dupes
for (Object o: l) {
if (!set.contains(o)) {
nl.add(o);
}
set.add(o);
}
Running time is O(n), since hashset is constant lookup time. Or simpler:
Set<?> set = new HashSet<String>(); // Will be collection without dupes
List<?> l = ...; // Original list
for (Object o: l) {
set.add(o); // Rely on set semantics to remove dupes
}
List<?> nl = new ArrayList<>(set);
Of course the objects in the list must implement a sensible equals() and hashCode().

With java 8, you can use Stream distinct to solve that.
The sample code as below. Hope it will help you.
// input data
List<String> a = new ArrayList<>();
a.add("A");
a.add("A");
a.add("A");
a.add(null);
a.add(null);
Iterator source = a.iterator();
Iterable<String> iterable = () -> source;
// logic code
Iterator temp = StreamSupport
.stream(iterable.spliterator(), false)
.distinct()
.collect(Collectors.toList()).iterator();
Iterable result = () -> temp;
// verify result
StreamSupport
.stream(result.spliterator(), false)
.forEach(System.out::println)
;

Related

How to Sort a Nested List of strings

I want to sort the list by values in the list. I want to do multisorting based on few parameters in the list. Providing sample example how data looks like.
Note: I don't have feasibility to convert List<List<String>> into a list of objects.
List<List<String>> data = new ArrayList<>();
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
list1.add("Siva");
list1.add("20");
list1.add("Hyd");
list1.add("TA");
list1.add("India");
list2.add("Suresh");
list2.add("22");
list2.add("Banglore");
list2.add("KA");
list2.add("India");
list3.add("Ramesh");
list3.add("24");
list3.add("Chennai");
list3.add("TN");
list3.add("India");
data.add(list1);
data.add(list2);
data.add(list2);
I want to do multi sorting based on name, age and city.
It's just sample data. List of lists is dynamic. Sorting parameters will also change sometimes.
I want to do sorting on a list of lists of strings only.
Expected Output: List<List<String>> sortedData
Solution by Maintaining your Structure
If you can't really create a class wrapping the data in your nested List (for whatever reason), you could use the collection stream and define the sorted operation's logic as follows:
List<List<String>> listRes = data.stream()
.sorted((x, y) -> {
int res = x.get(0).compareTo(y.get(0)); //Comparing by name
if (res != 0) return res;
res = Integer.valueOf(x.get(1)).compareTo(Integer.valueOf(y.get(1))); //Comparing by age (numeric value)
if (res != 0) return res;
return x.get(2).compareTo(y.get(2)); //Comapring by city
})
.collect(Collectors.toList());
Link to test the code above:
https://ideone.com/RhW1VI
Alternative Solution
However, as it has been pointed out in the comments, a better approach would be to create a custom class representing your data in the nested List. Perhaps a simple record if you're using Java 14 or later with a factory method to retrieve an instance of your class from a nested List.
Then, with a stream you could map each nested list to your custom class and sort it with a Comparator.
Here is a snippet of the implementation:
public static void main(String[] args) {
List<List<String>> data = /* ... your initialization ... */
List<MyClass> listSorted = data.stream()
.map(list -> MyClass.createMyClass(list))
.sorted(Comparator.comparing(MyClass::getName).thenComparing(MyClass::getAge).thenComparing(MyClass::getCity))
.collect(Collectors.toList());
System.out.println(listSorted);
}
Mapping record
record MyClass(String name, int age, String city, String code, String country) {
public static MyClass createMyClass(List<String> list) {
if (list == null || list.size() < 5) {
return null;
}
MyClass mc = new MyClass();
mc.name = list.get(0);
mc.age = Integer.valueOf(list.get(1));
mc.city = list.get(2);
mc.code = list.get(3);
mc.country = list.get(4);
return mc;
}
}
Here there is also a link with both implementations:
https://ideone.com/UK9trV
In order to impose the order of lists inside a nested list need to define a Comparator.
As you've said that contents of the list will can't be predicted in advance, I assume that nested lists might be of arbitrary size and their sizes might not be equal.
A comparator that can handle such case might be written like that:
Comparator<List<String>> listComparator = new Comparator<>() {
#Override
public int compare(List<String> o1, List<String> o2) {
int limit = Math.min(o1.size(), o2.size());
for (int i = 0; i < limit; i++) {
int localResult = o1.get(i).compareTo(o2.get(i));
if (localResult != 0)
return localResult;
}
return o1.size() - o2.size();
}
};
In order to sort the list, you can apply method sort() on it (available with Java 8+) which expects a comparator:
data.sort(listComparator);
And you can make a defensive copy of the list before applying sort(), if its initial order might be useful for you:
List<List<String>> initialOrder = new ArrayList<>(data);
data.sort(listComparator);

Using an Iterator inside a for loop to remove elements from an ArrayList

I have an ArrayList of objects that have a version number as a field. I want to do some work on that ArrayList, but I only want the most recent version of the object. I was thinking of coding as such:
ArrayList<ObjectVO> ObjectList = getObjectList();
for(ObjectVO myVO : ObjectList) {
Iterator<ObjectVO> iter = ObjectList.iterator();
while(iter.hasNext()) {
ObjectVO checkVO = iter.next();
if(myVO.getID().equals(checkVO.getID()) {
//they are the same object ID. Check the version number, remove it lower
if(myVO.getVersion() > checkVO.getVersion()) {
iter.remove();
}
}
}
}
Is this valid? I don't know if the fact that we are in a for loop originally would break the mutability of the ArrayList at runtime.
No, this won't work. iter.remove() will cause the out for loop to fail with ConcurrentModificationException.
Instead of doing this, you can do this with indexed for loops, and a BitSet to keep track of things you want to remove:
BitSet toRemove = new BitSet();
for (int m = 0; m < ObjectList.size(); ++m) {
if (toRemove.get(m)) continue;
ObjectVO myVO = ObjectList.get(m);
for (int c = 0; c < ObjectList.size(); ++c) {
if (toRemove.get(c)) continue;
ObjectVO checkVO = ObjectList.get(c);
if(myVO.getID().equals(checkVO.getID()) {
//they are the same object ID. Check the version number, remove it lower
if(myVO.getVersion() > checkVO.getVersion()) {
toRemove.set(c);
}
}
}
}
This is basically your code, but it doesn't do the removal yet. Then you can sweep through the list after and remove them:
int dst = 0;
for (int src = 0; src < ObjectList.size(); ++src) {
if (!toRemove.get(src)) {
ObjectList.set(dst++, ObjectList.get(src));
}
}
ObjectList.subList(dst, ObjectList.size()).clear();
The point of using a BitSet like this is that removal from an ArrayList is inefficient if you are removing from anywhere other than the end, because it requires all of the elements "to the right" of the element you remove to be shuffled along by one position. The loop with the set/get and clear allows you to only move each of the retained elements once.
You can do a bit better than the quadratic loop, though, if you group the list elements by things with the same ID: then you don't need to keep on checking the entire list:
BitSet toKeep = new BitSet();
IntStream.range(0, ObjectList.size())
.mapToObj(a -> a)
.collect(
groupingBy(a -> ObjectList.get(a).getID(),
maxBy(comparingInt(a -> ObjectList.get(a).getVersion()))))
.values()
.forEach(a -> toKeep.set(a));
int dst = 0;
for (int src = 0; src < ObjectList.size(); ++src) {
if (toKeep.get(src)) {
ObjectList.set(dst++, ObjectList.get(src));
}
}
ObjectList.subList(dst, ObjectList.size()).clear();
Assuming you have the memory, rather than do an O(N^2) operation, you could do this more efficiently (O(N)) by using a Map to track the newest Version for each Id. One pass tracks the newest version for each Id, and the second removes elements which are not the latest.
Map<Integer, Thing> newestById = new HashMap<>();
for (Thing thing : list) {
newestById.merge(thing.id, thing, (a,b) -> a.version > b.version ? a : b);
}
list.removeIf(thing -> thing != newestById.get(thing.id)); }
Depending on your use case, you might even be able to store your data in a Map instead of a List, and check if the version is the latest before adding it to the Map.
As the other answers have discussed this won't work. You have three options as I see them, trading memory for CPU cycles/flexibility. I've used Integer instead of ObjectVO in my examples, but it'll be trivial to swap them.
Option 1 - moderate memory, single-pass of the array
Track the highest ID you've seen and populate an ArrayList with new items as they meet the criteria. When you encounter a new higher ID, throw away the ArrayList and create a new one:
ArrayList<Integer> objectList = getObjectList();
Integer bestId = -1;
ArrayList<Integer> allObjectsMatchingId = new ArrayList<>();
for(Integer currentObject : objectList) {
if(currentObject > bestId) {
bestId = currentObject;
allObjectsMatchingId = new ArrayList<>();
} else if(currentObject == bestId) {
allObjectsMatchingId.add(currentObject);
}
}
return allObjectsMatchingId;
Option 2 - more expensive memory, single-pass of the array, most flexible.
For each ID you see, create an ArrayList and store it against a map. This allows you to easily change the criteria about what ID you want to keep.
ArrayList<Integer> objectList = getObjectList();
Map<Integer, ArrayList<Integer>> objectsById = new HashMap<>();
for(Integer currentObject : objectList) {
ArrayList<Integer> listForId = objectsById.get(currentObject);
if(listForId == null) {
listForId = new ArrayList<Integer>();
}
listForId.add(currentObject);
objectsById.put(currentObject, listForId);
}
Integer bestId = -1;
for(Integer i : objectsById.keySet()) {
if(i > bestId) {
bestId = i;
}
}
return objectsById.get(bestId);
Option 3 - no additional memory aside from id, two-passes of the array.
Search through the ArrayList for the highest ID, then filter the array to only elements that pass that filter.
This is the closest to your current implementation, the difference being that you do them in separate steps. This reduces complexity from O(N^2) to O(N), and is valid as you aren't modifying the ArrayList while iterating it. You could use a Stream here to filter instead of an iterator if you're Java 8 compatible. See Java: Efficient ArrayList filtering?
ArrayList<Integer> objectList = getObjectList();
Integer bestId = -1;
for(Integer currentObject : objectList) {
if(currentObject > bestId) {
bestId = currentObject;
}
}
Iterator<Integer> iter = objectList.iterator();
while(iter.hasNext()) {
if(iter.next() != bestId) {
iter.remove();
}
}
Why not use Java Streams to solve this:
Collection<ObjectVO> result = objectList.stream()
.collect(Collectors.toMap(ObjectVO::getID, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(ObjectVO::getVersion))))
.values();
This creates a map which contains the max version for each id. Then you can just use Map.values() to get the object list.
If you need a List or an ArrayList you can just use new ArrayList<>(result).

How do I use streams/filters/lambdas from an iterator and for loop?

I'm not too familiar with streams, filters, and lambdas as I'm used to writing older Java code. I wanted to try using new things in newer versions of Java. I can't seem to get anything started without getting compiling problems. Any help is appreciated, thank you.
List<Obj> a = getListForA(); // ['werdsegs', 'wsghnmrst', 'vaasdfdas', 'iujhgfds']
List<Obj> b = getListForB(); // ['aalaksdjf', 'erftghjuk', 'werdsejfksd', 'asdklgjaklgj', 'poijgndf']
a.forEach((item) -> {
String foo = item.substring(0,5);
for(Iterator<Obj> i = b.iterator(); i.hasNext();) {
Obj o = i.next();
if(foo.equals(o.substring(0,5)) {
i.remove();
}
}
});
I expect for any item that contains the substring, remove it from the list for b.
After the code runs, list b should show the list below with werdsejfksd missing from
['aalaksdjf', 'erftghjuk', 'asdklgjaklgj', 'poijgndf']
Your code runs fine if you replace Obj with String and fix the compilation errors:
List<String> a = Arrays.asList("werdsegs", "wsghnmrst", "vaasdfdas", "iujhgfds");
List<String> b = new ArrayList<>(Arrays.asList("aalaksdjf", "erftghjuk", "werdsejfksd", "asdklgjaklgj", "poijgndf"));
a.forEach((item) -> {
String foo = item.substring(0,5);
for (Iterator<String> i = b.iterator(); i.hasNext(); ) {
String o = i.next();
if (foo.equals(o.substring(0,5))) {
i.remove();
}
}
});
System.out.println(b); // prints: [aalaksdjf, erftghjuk, asdklgjaklgj, poijgndf]
As suggested by jonathan Heindl in another answer, you can simplify the code by using removeIf(...). I'd build a Set first, though, for better performance:
List<String> a = Arrays.asList("werdsegs", "wsghnmrst", "vaasdfdas", "iujhgfds");
List<String> b = new ArrayList<>(Arrays.asList("aalaksdjf", "erftghjuk", "werdsejfksd", "asdklgjaklgj", "poijgndf"));
Set<String> set = a.stream().map(s -> s.substring(0,5)).collect(Collectors.toSet());
b.removeIf(s -> set.contains(s.substring(0,5)));
System.out.println(b); // prints: [aalaksdjf, erftghjuk, asdklgjaklgj, poijgndf]
If you want a new list, instead of modifying b, you can use the filter(...) method of Stream:
List<String> a = Arrays.asList("werdsegs", "wsghnmrst", "vaasdfdas", "iujhgfds");
List<String> b = Arrays.asList("aalaksdjf", "erftghjuk", "werdsejfksd", "asdklgjaklgj", "poijgndf");
Set<String> set = a.stream().map(s -> s.substring(0,5)).collect(Collectors.toSet());
List<String> c = b.stream().filter(s -> ! set.contains(s.substring(0,5))).collect(Collectors.toList());
System.out.println(c); // prints: [aalaksdjf, erftghjuk, asdklgjaklgj, poijgndf]
I guess like this
List<String> a = getListForA(); // ['werdsegs', 'wsghnmrst', 'vaasdfdas', 'iujhgfds']
List<String> b = getListForB(); // ['aalaksdjf', 'erftghjuk', 'werdsejfksd', 'asdklgjaklgj', 'poijgndf']
b.removeIf(bObj->a.stream().anyMatch(aObj->aObj.substring(0,5).equals(bObj.substring(0,5))));

Assert number of matching elements in collection via Hamcrest

I have a collection of matchers like List<Matcher<?>> filters and some collection of elements List<Element> elementsToCheck. I want to create an assertion that checks if there are x elements that match all filters, something like that:
public void checkMatch(List<Matcher<?>> filters, int expectedSize){
MatcherAssert.assertThat(elementsToCheck, ???);
}
I wrote something like that:
final Iterator<Element> iterator = this.elements.iterator();
final List<Element> filtered = new ArrayList<>();
while (iterator.hasNext()) {
final Element element = iterator.next();
boolean allMatches = true;
for (final Matcher<?> matcher : this.filters) {
if (!matcher.matches(element)) {
allMatches = false;
break;
}
}
if (allMatches) {
filtered.add(element);
}
}
MatcherAssert.assertThat(filtered,
Matchers.hasSize(this.expectedSize));
Is there any better solution?
org.hamcrest.CoreMatchers.allOf matcher can be used instead of the list of matchers. Then a test may look as follows (I test list of Strings as an example):
// given
Matcher<String> allOfMatcher = allOf(containsString("a"), containsString("b"));
long expectedNumberOfMatches = 2L;
// when
List<String> elementsToCheck = Arrays.asList("aa", "ab", "ba", "bb"); // substitute with some actual method call
// then
assertThat(elementsToCheck, notNullValue());
assertThat(elementsToCheck.stream().filter(allOfMatcher::matches).collect(Collectors.counting()), equalTo(expectedNumberOfMatches));
You can even pass the already existing list of matchers to overloaded version of allOf that takes Iterable as an argument.
private Matcher<Element> getAllOfMatcher(List<Matcher<? super Element>> matchers) {
return CoreMatchers.allOf(matchers);
}
If I understodd task right:
List elementsToCheck- collection of elements
this.filters - collection of Matchers
elementsToCheck.stream()
.filter(element ->!this.filters.stream()
.filter(matcher -> matcher.matches(element))
.collect(Collectors.toList()).size() == this.filters.size())
.collect(Collectors.toList()).size() == this.expectedSize

how to get duplicated and non duplicated element of arrayList?

I have an object as Riziv with three variables as id, cnk and product. Then I search in a databank for this object and add it to a ArrayList as ArrayList<Riziv> list.
Now I should checkout if all object in his array are the same cnk then return true otherwise I should return all objects which are not the same cnk with error message.
public class Riziv{ String id, cnk, product; }
ArrayList<Riziv> list = getArrayListFromDatabank(id);
public void getDuplicatedWhichHasTheSameCnk(){
}
}
Using standard JVM structures (MultiMap is provided by guava), you can do that:
public List<Riviz> getDuplicates(final List<Riviz> l)
{
final HashMap<String, List<Riviz>> m = new HashMap<String, List<Riviz>>();
final List<Riviz> ret = new ArrayList<Riviz>();
String cnk;
for (final Riviz r: l) {
cnk = r.getCnk();
if (!m.contains(cnk))
m.add(cnk, new ArrayList<Riviz>());
m.get(cnk).add(r);
}
List<Riviz> tmp;
for (final Map.Entry<String, List<Riviz>> entry: m.entrySet()) {
tmp = entry.getValue();
if (tmp.size() == 1) // no dups
continue;
ret.addAll(tmp);
}
return ret;
}
ret will contain the duplicates. You can change that function to return a Map<String, Riviz> instead, and filter out entries where the list size is only one. You'll then get a map with the conflicting cnks as keys and a list of dups as values.
I am not clear exactly what you want however I suspect you want something like this.
MultiMap<Key, Riziv> multiMap =
List<Riziv> list =
for(Riziv r: list)
multiMap.put(r.getCnk(), r);
for(Key cnk: multiMap.keySet()) {
Collection<Riziv> sameCnk = multiMap.get(cnk);
// check size and compare entries
}
The multi-map will have the list of Riziv objects for each Cnk.
One way to do it is write a comparator to sort the list by cnk String and then compare each consecutive cnk String to the next, if you find a duplicate, they will be right next to eachother.
1.) Sort the list using a comparator by sorting on the cnk variable.
2.) Compare each element in the list to the next for duplicates.
There's probably many other ways to solve this, this is just the first that came to mind.
I did not test this so you have been forewarned lol:
ArrayList<Riziv> rizArray = new ArrayList<Riziv>();
//Sort the array by the CNK variable.
Collections.sort(rizArray, new Comparator<Riziv>(){
#Override
public int compare(Riziv arg0, Riziv arg1) {
//Return the comparison of the Strings.
//Use .compareToIgnoreCase if you want to ignore upper/lower case.
return arg0.getCnk().compareTo(arg1.getCnk());
}
});
//List should be in alphabetical order at this point.
List<Riziv> duplicates = new ArrayList<Riziv>();
Riziv rizPrevious = null;
for(Riziv riz: rizArray){
if(rizPrevious == null){
rizPrevious = riz;
continue;
}
if(riz.getCnk().compareTo(rizPrevious.getCnk()) == 0){
duplicates.add(riz);
}
rizPrevious = riz;
}

Categories

Resources