Evenly distribute lists into sublists in Java - java

I want to evenly distribute a list into a given number of sublists.
For example, I have a list with elements 1 to 10 and I want 3 lists. These should look like:
SL1 -> {1, 2, 3, 4}
SL2 -> {5, 6, 7}
SL3 -> {8, 9, 10}
Important: What each list contains is not relevant, i.e. SL1 could have {1, 5, 7, 10}. The most important thing is that there are 2 lists with size 3 and 1 list with size 4.
I have tried several things, including the Iterables.partition but that won't help.
The only thing I've come up with that works is:
public Iterable<List<Integer>> distributeEvenlyQueryListIntoLists(final LinkedList<Integer> bigList, final Integer numberOfSublists) {
List<List<Integer>> result = new ArrayList<>();
// Creates as many lists as needed
for (int i = 0; i < numberOfSublists; i++) {
result.add(new ArrayList<>());
}
while (bigList.iterator().hasNext()) {
for (int i = 0; i < numberOfSublists; i++) {
if (!bigList.iterator().hasNext()) {
break;
}
result.get(i).add(bigList.poll());
}
}
return result;
}
The passed bigList does not have to be a LinkedList, it can be any Iterable.
I especially hate the first loop where I create the sublists.
Thanks!

Just distribute them in a round-robin pattern:
public <T> List<List<T>> partition(Iterable<T> iterable, int partitions){
List<List<T>> result = new ArrayList<>(partitions);
for(int i = 0; i < partitions; i++)
result.add(new ArrayList<>());
Iterator<T> iterator = iterable.iterator()
for(int i = 0; iterator.hasNext(); i++)
result.get(i % partitions).add(iterator.next());
return result;
}
A sample run with this code:
List<String> l = Stream.iterate(0, i->i + 1).limit(25).map(i->Integer.toString(i)).collect(Collectors.toList());
System.out.println(partition(l, 4).toString());
Produces
[[0, 4, 8, 12, 16, 20, 24], [1, 5, 9, 13, 17, 21], [2, 6, 10, 14, 18, 22], [3, 7, 11, 15, 19, 23]]
The basic idea is to add a single element to each list in the result set roundwise. This way it's guaranteed that the difference in the number of elements between two lists never exceeds 1.
As an alternative you could use guavas implementation of Iterables.partition, which takes a slightly different approach.

If you hate creating sublists, that implies you're looking for a fast solution. If you have the original List, and you plan on not altering the original List, consider List.subList().
int subSize = bigList.length() / numSubs;
int numBigSubs = 0; // # of subs that need to be one bigger
if (bigList.length() % numSubs > 0) {
subSize++;
numBigSubs = bigList.length() % numSubs;
}
int from = 0;
int to = subSize;
List<List<Integer>> subList = new ArrayList<List<Integer>>(numSubs);
for (int i = 0; i < numSubs; i++) {
List<Integer> newSub = bigList.subList(from, to);
subList.add (newSub);
from = to;
to += subSize;
if (i >= numBigSubs && numBigSubs > 0) to--;
}
Note: I wrote this without testing - if it fails, I apologize, and hope someone will edit it to work.
Again, the big upside to this is that it should be wicked fast - all the sublists are simply views into the larger one. The downside is that if you change the list, all bets are off.

You can use org.apache.commons.collections4.ListUtils to create equal size sublists.
List<String> bigList = ...
int batchSize = 1000;
List<List<String>> smallerLists = ListUtils.partition(bigList, batchSize);

Related

How to find cartesian product of an arbitrary number of ArrayLists - Java

I'm trying to figure out how to output the cartesian product of two or more lists in a java method such that I have a List of List's as the output.
For example, if i have:
a = [1,2] , b = [3,4] , c = [5,6]
Then the output would be:
[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6].....]
I have managed to write the code such that it works for two lists but when it becomes three or more it doesnt work. In my code, i have initially added the cartesian product of the first two lists to a larger list. And from there i tried adding the remaining elements into those such lists, but i am presented with an infinite loop for some reason.
public static List<List<Integer>> cartprod(List<Entry> entries) {
// TODO: implement this
List<List<Integer>> cartprod = new ArrayList<List<Integer>>();
Entry first = entries.get(0);
for (int i =0; i < first.getValues().size(); i++) {
int element = first.getValues().get(i);
for (int j = 1; j < entries.size(); j++) {
Entry entry = entries.get(j);
for (int k =0; k < entry.getValues().size(); k++) {
int entry_element = entry.getValues().get(k);
List<Integer> product = new ArrayList<Integer>();
product.add(element);
product.add(entry_element);
cartprod.add(product);
}
}
}
//HELP WITH THIS CODE BLOCK
// ###################################
// int entries_size = entries.size();
// for (int i=2; i< entries.size(); i++) {
// List<Integer> nextList = entries.get(i).getValues();
// for (int j=0; j< cartprod.size(); j++) {
// List<Integer> element = cartprod.get(j);
// for (int k=0; k < nextList.size(); k++) {
// System.out.println(nextList);
// List<Integer> newList = element;
// int numToAdd = nextList.get(k);
// System.out.println(numToAdd+"\n");
// newList.add(numToAdd);
// cartprod.add(newList);
// }
// }
// }
return cartprod;
}
this question sounds a lot like homework/interview question...but i'll give you a hint anyway.
you are going about it the wrong way, nested loops will not help you in this case, because you want to be able to work with any number of lists.
think of it this way: every element in the cartesian product of lists L1,L2,...Ln is made up of one element from L1, and the rest is the cartesian product of L2...Ln as such:
{L1[0] + cartesian(L2,L3,...Ln)} , {L1[1] + cartesian(L2,L3,...,Ln)} and so on up to the length of L1.
i hope you get where i'm going with this, good luck!
P.S. if this question is indeed not homework, i'll post the java code later.
P.P.S. extra points if you calculate cartesian(Lk,...,Ln) only once for each k where n > k > 1 ;)
It's a lot easier to calculate Cartesian Products using recursion. Here is a recursive code:
public List<List<Integer>> calculateCartesianProduct(List<List<Integer>> inputLists) {
List<List<Integer>> cartesianProducts = new ArrayList<>();
if (inputLists != null && inputLists.size() > 0) {
// separating the list at 0th index
List<Integer> initialList = inputLists.get(0);
// recursive call
List<List<Integer>> remainingLists = calculateCartesianProduct(inputLists.subList(1, inputLists.size()));
// calculating the cartesian product
initialList.forEach(element -> {
remainingLists.forEach(remainingList -> {
ArrayList<Integer> cartesianProduct = new ArrayList<>();
cartesianProduct.add(element);
cartesianProduct.addAll(remainingList);
cartesianProducts.add(cartesianProduct);
});
});
} else {
// Base Condition for Recursion (returning empty List as only element)
cartesianProducts.add(new ArrayList<>());
}
return cartesianProducts;
}
Here he is how I tested it:
List<Integer> a = Arrays.asList(1, 2);
List<Integer> b = Arrays.asList(3, 4);
List<Integer> c = Arrays.asList(5, 6);
List<List<Integer>> inputLists = Arrays.asList(a, b, c);
System.out.println(calculateCartesianProduct(inputLists));
Output:
[[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]

Sort array in a specific order in java

I am trying to sort an array to a specific order. So for example, I have this array
{6,1,1,5,6,1,5,4,6}
and I want them to be sorted to this order
{4,1,6,5}
expected new array would be
{4,1,1,1,6,6,6,6,5}
My idea is this,
public class Sort {
static int[] list = { 6, 1, 1, 5, 6, 1, 6, 4, 6 };
static int[] sorted = { 4, 1, 6, 5 };
static int[] newList = new int[list.length];
static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < sorted.length; i++) {
for (int j = 0; j < list.length; j++) {
if (list[j] != sorted[i])
continue;
else
newList[count++] = sorted[i];
}
}
}
}
It works fine, however, I am not sure if this is the fastest and easier way to do this regarding speed and memory cause the list could have too many numbers.
You can use java built-in sort algorithm with a customized comparator.
public static void main(String[] args) {
Integer[] list = { 6, 1, 1, 5, 6, 1, 6, 4, 6 };
int[] sorted = { 4, 1, 6, 5 };
Map<Integer, Integer> order = new HashMap<>();
for (int i = 0; i < sorted.length; i++)
order.put(sorted[i], i);
Arrays.sort(list, (a ,b) -> order.get(a) - order.get(b));
System.out.println(Arrays.toString(list));
}
The output is [4, 1, 1, 1, 6, 6, 6, 6, 5].
If you
know the possible elements in advance
and they are relatively small numbers
you can simply count them:
int stats[]=new int[7]; // "largest possible element"+1
for(int i=0;i<list.length;i++)
stats[list[i]]++;
and then reconstruct the ordered list:
int idx=0;
for(int i=0;i<sorted.length;i++){
int val=sorted[i];
for(int j=stats[val];j>0;j--)
newlist[idx++]=val;
The two snippets in total have "2*list.length" steps, which is probably faster than your original "sorted.length*list.length" loop-pair.
As you have not described the actual use-case, it is hard to tell more. For example if you have these numbers only, you probably do not need the ordered result to be an actual list. However if these numbers are just part of an object, this build-a-statistics approach is not applicable.

Java: Array of first n integers

Any shortcut to create a Java array of the first n integers without doing an explicit loop?
In R, it would be
intArray = c(1:n)
(and the resulting vector would be 1,2,...,n).
If you're using java-8, you could do:
int[] arr = IntStream.range(1, n).toArray();
This will create an array containing the integers from [0, n). You can use rangeClosed if you want to include n in the resulting array.
If you want to specify a step, you could iterate and then limit the stream to take the first n elements you want.
int[] arr = IntStream.iterate(0, i ->i + 2).limit(10).toArray(); //[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Otherwise I guess the simplest way to do is to use a loop and fill the array. You can create a helper method if you want.
static int[] fillArray(int from, int to, int step){
if(to < from || step <= 0)
throw new IllegalArgumentException("to < from or step <= 0");
int[] array = new int[(to-from)/step+1];
for(int i = 0; i < array.length; i++){
array[i] = from;
from += step;
}
return array;
}
...
int[] arr3 = fillArray(0, 10, 3); //[0, 3, 6, 9]
You can adapt this method as your needs to go per example from an upperbound to a lowerbound with a negative step.

How can I add new integers to replace the old integers to my already-existing Array?

Here is the program task:
Write a method called collapse that accepts an array of integers as a parameter and returns a new array containing the result of replacing each pair of integers with the sum of that pair.
For example, if an array called list stores the values
{7, 2, 8, 9, 4, 13, 7, 1, 9, 10}
then the call of collapse(list) should return a new array containing:
{9, 17, 17, 8, 19}.
The first pair from the original list is collapsed into 9 (7 + 2), the second pair is collapsed into 17 (8 + 9), and so on. If the list stores an odd number of elements, the final element is not collapsed.
For example, if the list had been {1, 2, 3, 4, 5}, then the call would return {3, 7, 5}. Your method should not change the array that is passed as a parameter.
Here is my currently-written program:
public static int[] collapse(int[] a1) {
int newArrayLength = a1.length / 2;
int[] collapsed = new int[newArrayLength];
int firstTwoSums = 0;
for (int i = 0; i < a1.length-1; i++) {
firstTwoSums = a1[i] + a1[i+1];
collapsed[collapsed.length-1] = firstTwoSums;
}
return collapsed;
}
I pass in an array of {7, 2, 8, 9, 4, 13, 7, 1, 9, 10} and I want to replace this array with {9, 17, 17, 8, 19}.
Note:{9, 17, 17, 8, 19} will be obtained through the for-loop that I have written.
Currently, I am having trouble with adding the integers I obtained to my "collapsed" array. It'd be a great help if you could help me or at least give me some guidance on how to do this.
Thanks in advance!
First you have to understand what is going on.
You have an array of certain size where size can either be even or odd. This is important because you are using a1.length/2 to set the size for new array, so you will also have to check for odd and even values to set the size right else it won't work for odd sized arrays. Try a few cases for better understanding.
Here's a way of doing it.
public static int[] collapseThis(int[] array) {
int size = 0;
if(isEven(array.length))
size = array.length/2;
else
size = array.length/2+1;
int[] collapsedArray = new int[size];
for(int i=0, j=0; j<=size-1; i++, j++) {
if(j==size-1 && !isEven(array.length)) {
collapsedArray[j] = array[2*i];
}
else {
collapsedArray[j] = array[2*i]+array[2*i+1];
}
}
return collapsedArray;
}
private static boolean isEven(int num) {
return (num % 2 == 0);
}
Using
collapsed[collapsed.length-1] = firstTwoSums;
The sum of your numbers will be always be put in the same index of the collapsed array, because collapsed.length - 1 is a constant value.
Try creating a new variable starting at zero, that can be incremented each time you add a sum to collapsed. For instance,
int j = 0;
for(...) {
...
collapsed[j++] = firstTwoSums;
}
I think this is a convenient answer.
public static void main(String[] args){
int[] numbers = {1,2,3,4,5};
int[] newList = collapse(numbers);
System.out.println(Arrays.toString(newList));
}
public static int[] collapse(int[] data){
int[] newList = new int[(data.length + 1)/2];
int count = 0;
for (int i = 0; i < (data.length / 2); i++){
newList[i] = data[count] + data[count + 1];
System.out.println(newList[i]);
count = count + 2;
}
if (data.length % 2 == 1){
newList[(data.length / 2)] = data[data.length - 1];
}
return newList;
}
i would combine the cases for the array with either odd or even elements together as below:
public static int[] collapse(int[] a1) {
int[] res = new int[a1.length/2 + a1.length % 2];
for (int i = 0; i < a1.length; i++)
res[i/2] += a1[i];
return res;
}
public static int[] collapse(int[] a1) {
int newArrayLength = a1.length / 2;
int[] collapsed;
if(a1.length%2 == 0)
{
collapsed = new int[newArrayLength];
}
else
{
collapsed = new int[newArrayLength+1];
collapsed[newArrayLength] = a1[a1.length-1];
}
int firstTwoSums = 0;
for (int i = 0; i < newArrayLength; i++) {
firstTwoSums = a1[i*2] + a1[i*2+1];
collapsed[i] = firstTwoSums;
}
return collapsed;
}
I modified your code and you may try it first.

frequency of items in int[] as opposed to List<Integer>?

I'm trying to figure out how to get the frequency of items within a list. When I approach this problem I typically, in the past, did:
int occurrences = Collections.frequency(list, 0);
It works when my list is a List<Integer> list. Is there a way to do this if I'm using int[] list? When I try collections, my list gets converted and then my code breaks. I can convert my code if needed, but was wondering, if there was a way to get the frequency from int[] instead.
You can (1) write your own linear-time frequency method, or (2) convert to an array of boxed int types and use Arrays.asList with Collections.frequency.
int[] arr = {1, 2, 3};
Integer[] boxedArr = new Integer[arr.length];
for(int i = 0; i < arr.length; i++)
boxedArr[i] = arr[i];
System.out.println(Collections.frequency(Arrays.asList(boxedArr), 1));
You could create a List from the int[], but otherwise, you just have to write your own.
int[] l = //your data;
List<Integer> list = new List<Integer>();
for(int i : l)
list.add(i);
int o = Collections.frequency(list, 0);
Or Arrays.asList(l); to make it shorter.
int occurrences = Collections.frequency(Arrays.asList(list), 0);
Or if you are against converting it to a list:
int occurrences = 0;
for (int i = 0; i < list.length; i++)
{
if(list[i] == X) // X being your number to check
occurrences++;
}
You can do this way as well.
List<Integer> intList = Arrays.asList(new Integer [] {
2, 3, 4, 5, 6,
2, 3, 4, 5,
2, 3, 4,
2, 3,
2
});
System.out.println(" count " + Collections.frequency(intList, 6));

Categories