Given an Arraylist like this:
List<Integer[]> list = new Arraylist<>();
list.add(new Integer[] {13, 1});
list.add(new Integer[] {100, 2});
list.add(new Integer[] {143, 2});
list.add(new Integer[] {185, 3});
list.add(new Integer[] {111, 3});
list.add(new Integer[] {98, 4});
how can I uniquify the list in respect to their second element with lowest value for the first element?
Usually, I would simply turn a list into a set an return it as a list again but how do I handle this case?
Thanks in advance!
I am not sure if that's the best solution but you can still use a set for the found values and do it like that:
Set<Integer> found=new HashSet<>();
list.removeIf(p->!found.add(p[1]));
The idea is that you need to store the found values anyway. You can use a filter with the streams Api but it is harder to keep a state and you will probably still need to maintain another collection with the already added values. So I think it is an easy solution.
Thats if you have it as a given that first value is always 100. Otherwise you might need to rework it a bit ;)
You could define an intermediate object
class Intermediate implements Comparable<Intermediate> {
private Integer[] array;
public Intermediate(Integer[] arr) { array = arr; }
public boolean equals(Object o) {
return (o instanceof Intermediate) &&
array[1] == ((Intermediate)o).array[1];
}
public int compareTo(Intermediate i) {
return Integer.compare(array[0], i.array[0]);
}
public Integer[] toTarget() { return array; }
}
then create a stream from your list which maps to the intermediate
yourList.stream().map(Intermediate::new)
this is now a Stream<Intermediate>. The equals is defined on the second int, so you can use distinct, then map back to your array and collect.
.sorted() // make sure to take the lowest first value
.distinct()
.map(Intermediate::toTarget)
.collect(toList());
or, if you only want the first int from the arrays,
.map(i -> i.toTarget()[0])
.collect(toList());
(which would give you [13, 100, 111, 98] for your sample list).
Related
I have a map from List to String (keys are lists).
the keys are converted from int arrays to lists.
adding a pair example:
int[] arr = { 1, 2, 3, 4, 5 };
my_map.put(Arrays.asList(arr), "12345");
Now when i check if my_map contains some other list, i will always get null, example:
int[] test_arr = { 1, 2, 3, 4, 5 };
if (my_map.get(Arrays.asList(test_arr)) != null) { // always null!
// do something
}
I know what the problem is: it's comparing the addresses of the lists, and NOT the values!
How can i compare those lists values ?
The problem you mentioned is correct, I’ll suggest replacing the key value to be string to list. But if you insist for your reasons you can envelope the list implementation with your own class and override the equal to function to be based on the values and not the address.
Some map implementations allow you to instantiate your map with a Comparator (TreeMap for example).
This way you can provide your own implementation to determine if the Keys are equal.
Your comparator function returns an int with a value that is greater than zero, less than zero, or zero if the two values are equal.
My advice is to change a little bit the storage in order to have the same functionality. Means to check if a predefined list is within the stucture.
You do not need a Map since it's not required to add values, just use the values within List. Further more use Objects and not primitives.
Briefly a List of Lists will fit well in this case. Notice, the order of elements in List matters. If do not care order you could just sort both lists before compare.
public class TestListArr {
public static void main(String[] args) {
List<List<Integer>> list = new ArrayList<>();
Integer[] arr = { 1, 2, 3, 4, 5 };
//just check it
//int[] arr1 = {1,2,3};
//Arrays.asList(arr1).forEach(System.out::println);
//Arrays.asList(arr).forEach(System.out::println);
list.add(Arrays.asList(arr));
Integer[] test_true = { 1, 2, 3, 4, 5 };
System.out.println(check(list,test_true));
Integer[] test_false = { 1, 2, 3, 4 };
System.out.println(check(list,test_false));
//direct test
//System.out.println(Arrays.asList(arr).equals(Arrays.asList(test_true)));
}
public static boolean check(List<List<Integer>> list, Integer[] test)
{
for(List<Integer> lst:list)
{
if(lst.equals(Arrays.asList(test)))
return true;
}
return false;
}
}
Output
true
false
The List objects that you are creating only contain one element, an object of type int[]. int[] uses identity to test equality, not array content. Instead, convert the array content to a list with equivalent content:
static List<Integer> toList(int[] arr) {
return IntStream.of(arr).boxed().collect(Collectors.toList());
}
I have a inner list of object into a list and I want to convert it in a list of Integer, because I know that its elements are Integer.
List<List<Object>> valuesModel = FCSMs.get(0).getValues();
for (List<Object> innerList : valuesModel) {
//CONVERT LIST OF OBJECT TO LIST OF INTEGER
}
How Can I do?
For a start it's a good practice to double check that you are in fact dealing with a list of type Integer. You may know that the only input is of that type, but anyone in the future working with your code will not (because it is not typed with Integer). After that you can simply "cast" it to type Integer. Some pseudo code on how to do that can be found below:
List<List<Integer>> result = new ArrayList<>();
for (List<Object> innerList : valuesModel) {
List<Integer> integerList = new ArrayList<>();
for (Object object : innerList) {
if (object instanceof Integer) {
integerList.add((Integer) object);
}
}
result.add(integerList);
}
You can do it like this. Since you know that all objects are Integers, no checks are done to avoid ClassCastExceptions.
Stream the list of lists to a stream of lists.
Then flatten those lists into a stream of object.
cast the object to an Integer.
and collect into a List
List<List<Object>> valuesModel = List.of(List.of(1,2,3,4), List.of(5,6,7,8));
List<Integer> integers = valuesModel.stream()
.flatMap(Collection::stream)
.map(ob->(Integer)ob)
.collect(Collectors.toList());
System.out.println(integers);
Prints
[1, 2, 3, 4, 5, 6, 7, 8]
I have an array of int:
int[] a = {1, 2, 3};
I need a typed set from it:
Set<Integer> s;
If I do the following:
s = new HashSet(Arrays.asList(a));
it, of course, thinks I mean:
List<int[]>
whereas I meant:
List<Integer>
This is because int is a primitive. If I had used String, all would work:
Set<String> s = new HashSet<String>(
Arrays.asList(new String[] { "1", "2", "3" }));
How to easily, correctly and succinctly go from:
A) int[] a...
to
B) Integer[] a ...
Thanks!
Using Stream:
// int[] nums = {1,2,3,4,5}
Set<Integer> set = Arrays.stream(nums).boxed().collect(Collectors.toSet())
The question asks two separate questions: converting int[] to Integer[] and creating a HashSet<Integer> from an int[]. Both are easy to do with Java 8 streams:
int[] array = ...
Integer[] boxedArray = IntStream.of(array).boxed().toArray(Integer[]::new);
Set<Integer> set = IntStream.of(array).boxed().collect(Collectors.toSet());
//or if you need a HashSet specifically
HashSet<Integer> hashset = IntStream.of(array).boxed()
.collect(Collectors.toCollection(HashSet::new));
Some further explanation. The asList method has this signature
public static <T> List<T> asList(T... a)
So if you do this:
List<Integer> list = Arrays.asList(1, 2, 3, 4)
or this:
List<Integer> list = Arrays.asList(new Integer[] { 1, 2, 3, 4 })
In these cases, I believe java is able to infer that you want a List back, so it fills in the type parameter, which means it expects Integer parameters to the method call. Since it's able to autobox the values from int to Integer, it's fine.
However, this will not work
List<Integer> list = Arrays.asList(new int[] { 1, 2, 3, 4} )
because primitive to wrapper coercion (ie. int[] to Integer[]) is not built into the language (not sure why they didn't do this, but they didn't).
As a result, each primitive type would have to be handled as it's own overloaded method, which is what the commons package does. ie.
public static List<Integer> asList(int i...);
Or you could easly use Guava to convert int[] to List<Integer>:
Ints.asList(int...)
asList
public static List<Integer> asList(int... backingArray)
Returns a fixed-size list backed by the specified array, similar to Arrays.asList(Object[]). The list supports List.set(int, Object), but any attempt to set a value to null will result in a NullPointerException.
The returned list maintains the values, but not the identities, of Integer objects written to or read from it. For example, whether list.get(0) == list.get(0) is true for the returned list is unspecified.
You can use ArrayUtils in Apache Commons:
int[] intArray = { 1, 2, 3 };
Integer[] integerArray = ArrayUtils.toObject(intArray);
Another option would be to use a primitive set from Eclipse Collections. You can easily convert an int[] to a MutableIntSet to a Set<Integer> or Integer[] as shown below, or you can use the MutableIntSet as is which will be much more memory efficient and performant.
int[] a = {1, 2, 3};
MutableIntSet intSet = IntSets.mutable.with(a);
Set<Integer> integerSet = intSet.collect(i -> i); // auto-boxing
Integer[] integerArray = integerSet.toArray(new Integer[]{});
If you want to go directly from the int array to the Integer array and preserve order, then this will work.
Integer[] integers =
IntLists.mutable.with(a).collect(i -> i).toArray(new Integer[]{});
Note: I am a committer for Eclipse Collections
Just add elements from array to Set with the below snippet
public class RemoveDuplicateElements {
public static void main(String args[]){
int array[] = {0,1,2,3,4,5,6,7,8,9,1,2,3,4,5};
Set <Integer> abc = new HashSet <Integer>();
for (Integer t:array){
abc.add(t);
}
System.out.println("sampleSet"+abc);
}
}
No need for looping :
Just you will convert the array to a List
Then converting this List to a hash set.
Ex:
List list = Arrays.asList(your_array);
Set set = new HashSet<>(list);
This worked perfect for me .
Let's say I have a list (EG: LinkedList<SomeObject>that contains elements ordered by a certain attribute (EG: SomeObject.someValue()). This attribute can and usually does repeat often/it isn't unique, BUT is never null.
Is there a convenient way to divide this into multiple Lists, each list containing only its equal in cardinal order? Also, can this be done with only once iteration of the list? For example, the original list:
1, 1, 1, 2, 2, 3, 3, 3
The desired lists from this:
1, 1, 1
2, 2,
3, 3, 3
Not too convenient, but:
start a loop. Store the previous item, and compare it to the current.
if the previous is different from the current (using equals(..), and be careful with null), then create a new List, or use list.subList(groupStart, currentIdx)
You could use Apache CollectionUtils to do this, where "list" is the original list, and "value" is the current value of the objects you want to extract a sublist for:
Collection<SomeObject> selectedObjects = CollectionUtils
.select(list,
new Predicate() {
boolean evaluate(Object input) {
return ((SomeObject) input).someValue().equals(value);
}
});
This approach means using a well known and well tested library (which always is a good thing), but the downside is that you will loop through the list once for each sublist you need.
Pretty sure there isn't a java API method for this. However you can write:
// This assumes your list is sorted according to someValue()
// SomeValueType is the type of SomeObject.someValue()
public Map<SomeValueType, List<SomeObject>> partition(List<SomeObject> list) {
Object currValue = null;
HashMap<SomeValueType, LinkedList<SomeObject>> result = new HashMap<SomeValueType, LinkedList<SomeObject>>();
LinkedList<SomeObject> currList = null;
for (SomeObject obj : list) {
if (!obj.someValue().equals(currValue()) {
currValue = obj.someValue();
currList = new LinkedList<SomeObject>();
result.put(currValue, currList);
}
currList.add(obj);
}
}
This will return you an HashMap of sublists, where the key is the someValue and the value is the partitioned list associated to it. Note, I didn't test this, so don't just copy the code.
EDIT: made this return hashmap instead of arraylist.
If you would use Google Guava-libaries:
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
public class Example {
public static void main(String[] args) {
HashMultiset<Integer> ints = HashMultiset.create();
ints.addAll(Lists.newArrayList(1, 1, 1, 2, 2, 3, 3, 3));
System.out.println(ints);
}
}
Output:
[1 x 3, 2 x 2, 3 x 3]
If you need to count how many elements of x you have use ints.count(x);, if you have value types you do not need to have more then just count.
With Guava, use Multimaps.index(Iterable<V>, Function<? super V, K>).
This should work (untested, but I am pretty sure everything is ok, This also assumes that the contents of the list are sortable):
public static List[] getEquivalentSubLists( List parent )
{
List cloneList = parent.clone();
Collections.sort(cloneList);
ArrayList<List> returnLists;
int end;
while (cloneList.size() > 0)
{
end = cloneList.lastIndexOf(cloneList.get(0));
returnLists.add(cloneList.subList(0, end));
cloneList.removeAll(cloneList.subList(0, end));
}
return returnList.toArray();
}
Question is simple:
I have two List
List<String> columnsOld = DBUtils.GetColumns(db, TableName);
List<String> columnsNew = DBUtils.GetColumns(db, TableName);
And I need to get the intersection of these. Is there a quick way to achieve this?
You can use retainAll method:
columnsOld.retainAll (columnsNew);
Using Google's Guava library:
Sets.intersection(Sets.newHashSet(setA), Sets.newHashSet(setB))
Note: This is much more efficient than naively doing the intersection with two lists: it's O(n+m), versus O(n×m) for the list version. With two million-item lists it's the difference between millions of operations and trillions of operations.
Since retainAll won't touch the argument collection, this would be faster:
List<String> columnsOld = DBUtils.GetColumns(db, TableName);
List<String> columnsNew = DBUtils.GetColumns(db, TableName);
for(int i = columnsNew.size() - 1; i > -1; --i){
String str = columnsNew.get(i);
if(!columnsOld.remove(str))
columnsNew.remove(str);
}
The intersection will be the values left in columnsNew. Removing already compared values fom columnsOld will reduce the number of comparisons needed.
How about
private List<String> intersect(List<String> A, List<String> B) {
List<String> rtnList = new LinkedList<>();
for(String dto : A) {
if(B.contains(dto)) {
rtnList.add(dto);
}
}
return rtnList;
}
There is a nice way with streams which can do this in one line of code and you can two lists which are not from the same type which is not possible with the containsAll method afaik:
columnsOld.stream().filter(c -> columnsNew.contains(c)).collect(Collectors.toList());
An example for lists with different types. If you have a realtion between foo and bar and you can get a bar-object from foo than you can modify your stream:
List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));
fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());
If you put the second list in a set say HashSet. And just iterate over the first list checking for presence on the set and removing if not present, your first list will eventually have the intersection you need.
It will be way faster than retainAll or contains on a list.
The emphasis here is to use a set instead of list. Lookups are O(1).
firstList.retainAll (new HashSet (secondList)) will also work.
using retainAll if don't care occurrences, otherwise using N.intersection
a = N.asList(12, 16, 16, 17, 19);
b = N.asList(16, 19, 107);
a.retainAll(b); // [16, 16, 19]
N.println(a);
a = N.asList(12, 16, 16, 17, 19);
b = N.asList(16, 19, 107);
a = N.intersect(a, b);
N.println(a); // [16, 19]
N is an utility class in abacus-common
use org.apache.commons.collections4.ListUtils#intersection
With Java 8 Stream API (and Java 9 List.of()) you can do following:
List<Integer> list1 = List.of(1, 1, 2, 2);
List<Integer> list2 = List.of(2, 2, 3, 3);
List<Integer> intersection = list1.stream()
.filter(list2::contains)
.distinct()
.collect(Collectors.toList());