I have a large dataset of length 4 int[] and I want to count the number of times that each particular combination of 4 integers occurs. This is very similar to counting word frequencies in a document.
I want to create a Map<int[], double> that maps each int[] to a running count as the list is iterated over, but Map doesn't take primitive types.
So I made Map<Integer[], Double>.
My data is stored as an ArrayList<int[]>, so my loop should be something like:
ArrayList<int[]> data = ... // load a dataset`
Map<Integer[], Double> frequencies = new HashMap<Integer[], Double>();
for(int[] q : data) {
// **DO SOMETHING TO convert q from int[] to Integer[] so I can put it in the map
if(frequencies.containsKey(q)) {
frequencies.put(q, tfs.get(q) + p);
} else {
frequencies.put(q, p);
}
}
I'm not sure what code I need at the comment to make this work to convert an int[] to an Integer[]. Or maybe I'm fundamentally confused about the right way to do this.
Native Java 8 (one line)
With Java 8, int[] can be converted to Integer[] easily:
int[] data = {1,2,3,4,5,6,7,8,9,10};
// To boxed array
Integer[] what = Arrays.stream( data ).boxed().toArray( Integer[]::new );
Integer[] ever = IntStream.of( data ).boxed().toArray( Integer[]::new );
// To boxed list
List<Integer> you = Arrays.stream( data ).boxed().collect( Collectors.toList() );
List<Integer> like = IntStream.of( data ).boxed().collect( Collectors.toList() );
As others stated, Integer[] is usually not a good map key.
But as far as conversion goes, we now have a relatively clean and native code.
If you want to convert an int[] to an Integer[], there isn't an automated way to do it in the JDK. However, you can do something like this:
int[] oldArray;
... // Here you would assign and fill oldArray
Integer[] newArray = new Integer[oldArray.length];
int i = 0;
for (int value : oldArray) {
newArray[i++] = Integer.valueOf(value);
}
If you have access to the Apache lang library, then you can use the ArrayUtils.toObject(int[]) method like this:
Integer[] newArray = ArrayUtils.toObject(oldArray);
Convert int[] to Integer[]:
import java.util.Arrays;
...
int[] aint = {1,2,3,4,5,6,7,8,9,10};
Integer[] aInt = new Integer[aint.length];
Arrays.setAll(aInt, i -> aint[i]);
Using regular for-loop without external libraries:
Convert int[] to Integer[]:
int[] primitiveArray = {1, 2, 3, 4, 5};
Integer[] objectArray = new Integer[primitiveArray.length];
for(int ctr = 0; ctr < primitiveArray.length; ctr++) {
objectArray[ctr] = Integer.valueOf(primitiveArray[ctr]); // returns Integer value
}
Convert Integer[] to int[]:
Integer[] objectArray = {1, 2, 3, 4, 5};
int[] primitiveArray = new int[objectArray.length];
for(int ctr = 0; ctr < objectArray.length; ctr++) {
primitiveArray[ctr] = objectArray[ctr].intValue(); // returns int value
}
Presumably you want the key to the map to match on the value of the elements instead of the identity of the array. In that case you want some kind of object that defines equals and hashCode as you would expect. Easiest is to convert to a List<Integer>, either an ArrayList or better use Arrays.asList. Better than that you can introduce a class that represents the data (similar to java.awt.Rectangle but I recommend making the variables private final, and the class final too).
The proper solution is to use this class as a key in the map wrapping the actual int[].
public class IntArrayWrapper {
int[] data;
public IntArrayWrapper(int[] data) {
this.data = data;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
IntArrayWrapper that = (IntArrayWrapper) o;
if (!Arrays.equals(data, that.data))
return false;
return true;
}
#Override
public int hashCode() {
return data != null ? Arrays.hashCode(data) : 0;
}
}
And change your code like this:
Map<IntArrayWrapper, Double > freqs = new HashMap<IntArrayWrapper, Double>();
for (int[] data : datas) {
IntArrayWrapper wrapper = new IntArrayWrapper(data);
if (freqs.containsKey(wrapper)) {
freqs.put(wrapper, freqs.get(wrapper) + p);
}
freqs.put(wrapper, p);
}
Convert int[] to Integer[]
public static Integer[] toConvertInteger(int[] ids) {
Integer[] newArray = new Integer[ids.length];
for (int i = 0; i < ids.length; i++) {
newArray[i] = Integer.valueOf(ids[i]);
}
return newArray;
}
Convert Integer[] to int[]
public static int[] toint(Integer[] WrapperArray) {
int[] newArray = new int[WrapperArray.length];
for (int i = 0; i < WrapperArray.length; i++) {
newArray[i] = WrapperArray[i].intValue();
}
return newArray;
}
Rather than write your own code, you can use an IntBuffer to wrap the existing int[] without having to copy the data into an Integer array:
int[] a = {1, 2, 3, 4};
IntBuffer b = IntBuffer.wrap(a);
IntBuffer implements comparable, so you are able to use the code you already have written. Formally, maps compare keys such that a.equals(b) is used to say two keys are equal, so two IntBuffers with array 1,2,3 - even if the arrays are in different memory locations - are said to be equal and so will work for your frequency code.
ArrayList<int[]> data = ... // Load a dataset`
Map<IntBuffer, Double> frequencies = new HashMap<IntBuffer, Double>();
for(int[] a : data) {
IntBuffer q = IntBuffer.wrap(a);
if(frequencies.containsKey(q)) {
frequencies.put(q, tfs.get(q) + p);
} else {
frequencies.put(q, p);
}
}
This worked like a charm!
int[] mInt = new int[10];
Integer[] mInteger = new Integer[mInt.length];
List<Integer> wrapper = new AbstractList<Integer>() {
#Override
public int size() {
return mInt.length;
}
#Override
public Integer get(int i) {
return mInt[i];
}
};
wrapper.toArray(mInteger);
Though the below compiles, it throws a ArrayStoreException at runtime.
Converting an int[], to an Integer[]:
int[] old;
...
Integer[] arr = new Integer[old.length];
System.arraycopy(old, 0, arr, 0, old.length);
I must admit I was a bit surprised that this compiles, given System.arraycopy being lowlevel and everything, but it does. At least in Java 7.
You can convert the other way just as easily.
I am not sure why you need a Double in your map. In terms of what you're trying to do, you have an int[] and you just want counts of how many times each sequence occurs(?). Why would this require a Double anyway?
I would create a wrapper for the int array with a proper .equals and .hashCode methods to account for the fact that int[] object itself doesn't consider the data in its version of these methods.
public class IntArrayWrapper {
private int values[];
public IntArrayWrapper(int[] values) {
super();
this.values = values;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(values);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IntArrayWrapper other = (IntArrayWrapper) obj;
if (!Arrays.equals(values, other.values))
return false;
return true;
}
}
And then use Google Guava's multiset, which is meant exactly for the purpose of counting occurrences, as long as the element type you put in it has proper .equals and .hashCode methods.
List<int[]> list = ...;
HashMultiset<IntArrayWrapper> multiset = HashMultiset.create();
for (int values[] : list) {
multiset.add(new IntArrayWrapper(values));
}
Then, to get the count for any particular combination:
int cnt = multiset.count(new IntArrayWrapper(new int[] { 0, 1, 2, 3 }));
Int is a primitive. Primitives can’t accept null and have default value. Hence, to accept Null you need to use wrapper class Integer.
Option 1:
int[] nos = { 1, 2, 3, 4, 5 };
Integer[] nosWrapped = Arrays.stream(nos)
.boxed()
.toArray(Integer[]::new);
nosWrapped[5] = null // can store null
Option 2:
You can use any data structure that use wrapper class Integer
int[] nos = { 1, 2, 3, 4, 5 };
List<Integer> = Arrays.asList(nos)
You don't need it. int[] is an object and can be used as a key inside a map.
Map<int[], Double> frequencies = new HashMap<int[], Double>();
is the proper definition of the frequencies map.
This was wrong :-). The proper solution is posted too :-).
Related
I have a lot of two-dimensional integer arrays (size 8x8), which each have some numerical double value based on a calculation. I would like to store them in a data structure, so I would be able to look up a two-dimensional array and check what value I have given that array earlier.
I have been trying to use a hashCode of the two-dimensional arrays, that did not work.
I have also tried a hashMap with the array as key and I have tried to create custom objects as keys. But neither worked out for me, so I am doing something wrong. Where do I find the solution?
Since Java arrays do not override hashCode() and euqals() from Object native implementation they are not really suitable for lookups. For example two identical arrays are not equal:
int[][] a1 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int[][] a2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
System.out.println(a1.hashCode()); // 1277181601
System.out.println(a2.hashCode()); // 41903949
System.out.println(a1.equals(a2)); // false
One can however create a wrapper class that internally uses Arrays.deepHashCode() and Arrays.deepEquals() as follows:
public class IntIntArray {
private int[][] values;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
IntIntArray that = (IntIntArray) o;
return Arrays.deepEquals(value, that.value);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(value);
}
}
This class is suitable for value based lookups and can be used as Map key:
IntIntArray i1 = new IntIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
IntIntArray i2 = new IntIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
System.out.println(i1.hashCode()); // 30729379
System.out.println(i2.hashCode()); // 30729379
System.out.println(i1.equals(i2)); // true
If the you're using the SAME INSTANCE of the arrays, you can just use them as the key without doing anything:
#Test
public void test1() {
Integer[][] arr = new Integer[8][];
for (int i = 1; i <= 8; i++) {
arr[i-1] = new Integer[] {545*i,237*i,3513*i,461*i,465*i,20*i,134*i,352*i};
}
Map<Integer[][], Double> map = new HashMap<>();
map.put(arr, 5.7);
Double val = map.get(arr);
assert val == 5.7;
}
You can see here that getting the value from the map, using the same array that was used to put it, will return the wanted value.
This is because the implementation of the array of hash-code and equals is to check of the same instance, which is also good performance, if that is your case.
However, if you have different instances of the same array (key) it won't work:
#Test
public void test2() {
Integer[][] arr1 = new Integer[8][];
Integer[][] arr2 = new Integer[8][];
for (int i = 1; i <= 8; i++) {
arr1[i-1] = new Integer[] {545*i,237*i,3513*i,461*i,465*i,20*i,134*i,352*i};
arr2[i-1] = new Integer[] {545*i,237*i,3513*i,461*i,465*i,20*i,134*i,352*i};
}
Map<Integer[][], Double> map = new HashMap<>();
map.put(arr1, 5.7);
Double val = map.get(arr2);
assert val == null;
}
Instead, you'll need to create a "Key" object and implement equals and hash code for it:
class MyKey {
Integer[][] arr;
public MyKey(Integer[][] arr) {
this.arr = arr;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyKey myKey = (MyKey) o;
return Arrays.deepEquals(arr, myKey.arr);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(arr);
}
}
and use it:
#Test
public void test2() {
Integer[][] arr1 = new Integer[8][];
Integer[][] arr2 = new Integer[8][];
for (int i = 1; i <= 8; i++) {
arr1[i-1] = new Integer[] {545*i,237*i,3513*i,461*i,465*i,20*i,134*i,352*i};
arr2[i-1] = new Integer[] {545*i,237*i,3513*i,461*i,465*i,20*i,134*i,352*i};
}
Map<MyKey, Double> map = new HashMap<>();
map.put(new MyKey(arr1), 5.7);
Double val = map.get(new MyKey(arr2));
assert val == 5.7;
}
Instead of using an array, use a List<List<Integer>> and use a map with hashCode as keys. As per the Java Documentation you should get the same value everytime if the values in the list are same. The hashCode is calculated by the following.
int hashCode = 1;
Iterator<E> i = list.iterator();
while (i.hasNext()) {
E obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
It also mentions that
This ensures that list1.equals(list2) implies that
list1.hashCode()==list2.hashCode() for any two lists, list1 and list2,
as required by the general contract of Object.hashCode().
public static int[] convertListToArray(List<Integer> listResult) {
int[] result = new int[listResult.size()];
int i= 0;
for (int num : listResult) {
result[i++] = num;
}
return result;
}
Is there an efficient way to convert List to array without iterating List explicitly ?
Maybe it is possible by using methods like:
Arrays.copyOf(int [] origin , int newLength );
System.arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
I know that there is a solution described here. However, I particularly interested in an efficient way of converting List<Integer> to int[]
Given the need to convert from Integer to int, I don't think you're going to find something more efficient than what you have, if I assume you're talking about runtime efficiency.
You might find converting to Integer[] first and then looping might be more efficient (below), but you might not, too. You'd have to test it in your specific scenario and see.
Here's that example:
int size = listResult.size();
int[] result = new int[size];
Integer[] temp = listResult.toArray(new Integer[size]);
for (int n = 0; n < size; ++n) {
result[n] = temp[n];
}
If efficiency is your primary concern, I think you can use your solution and make it more efficient by using an indexed for loop on the listResult if it is RandomAccess. However this makes the code much less readable, and you'd have to benchmark it for your use cases to see if it is more efficient.
public static int[] convertListToArray(List<Integer> listResult) {
int size = listResult.size();
int[] result = new int[size];
if (listResult instanceof RandomAccess)
{
for (int i = 0; i < size; i++)
{
result[i] = listResult.get(i);
}
}
else
{
int i = 0;
for (int num : listResult) {
result[i++] = num;
}
}
return result;
}
If you use Java 8 and would like to write less code you can use the Streams library.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int[] array = list.stream().mapToInt(i -> i).toArray();
If you are open to using a third party library, you can Eclipse Collections as follows.
MutableList<Integer> list = Lists.mutable.with(1, 2, 3, 4, 5);
int[] array = list.collectInt(i -> i).toArray();
The following is slightly more code, but is the most efficient solution I could come up with using Eclipse Collections.
MutableList<Integer> list = Lists.mutable.with(1, 2, 3, 4, 5);
int[] array = new int[list.size()];
list.forEachWithIndex((each, index) -> array[index] = each);
If you need to use the java.util.List interface, the ListIterate utility class can be used from Eclipse Collections.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int[] array = new int[list.size()];
ListIterate.forEachWithIndex(list, (each, index) -> array[index] = each);
The ListIterate utility will use different iteration code for RandomAccess lists and non-RandomAccess lists.
The most efficient thing to do would be to change the List<Integer> to a MutableIntList in Eclipse Collections or another library that has support for primitive collections.
Note: I am a committer for Eclipse Collections.
In Java 8:
int[] anArray = list.stream()
.filter(Objects::nonNull)
.mapToInt(Integer::intValue)
.toArray();
There is efficient way you could do this Java. However, this could open room for someone to create the generic function (depend on demand).
Just like this sample i wrote, I suggest you do the same to the specific knowledge of your program.
// Generic function to convert set to list
public static <T> ArrayList<T> convertSetToList(Set<T> set)
{
// create an empty list
ArrayList<T> list = new ArrayList<>();
// push each element in the set into the list
for (T t : set)
list.add(t);
// return the list
return list;
}
I'm using matlabcontrol to control Matlab remotely with Java, is there a way to get a multidimensional numeric array out of the Object array returned to Java from the returningFeval method?
The returningFeval method returns an Object array and my Object[0] is a Matlab 2-D Array of doubles.
For example:
MatlabProxyFactory factory = new MatlabProxyFactory();
MatlabProxy proxy = factory.getProxy();
Object[] myData = proxy.returningFeval("myFunction", 2, "Arg");
double[][] a2DArray = myData[0];
This returns:
ClassCastException: "[D cannot be cast to [[D."
I know I can get the array using the MatlabTypeConverter class and the .getNumericArray method, but then I wouldn't be doing anything with the returned 'myData[0]'.
Any ideas?
Arrays.copyOf() method can be used to convert Object arrays into arrays of other types without ClassCastExceptions if they are really compatible.
Double[][] integerArray = Arrays.copyOf(a, a.length, Double[][].class);
http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#copyOf(U[],%20int,%20java.lang.Class)
Matlab uses one dimensional indexing and returns a double[d1*d2*...*dk] array. In order to convert this array to double[d1][d2]...[dk] the values of d1 ... dk should be known. The following code do the rest:
private static Object unflattenMatrix(boolean columnwise, double[] data, int... dimensions) {
Object res = Array.newInstance(double.class, dimensions);
List<Integer> dims = new LinkedList<>();
for (int i = 0; i < dimensions.length; i++) {
dims.add(dimensions[i]);
}
if (columnwise) {
Collections.reverse(dims);
}
setTraversingColumnwise(res, data, new LinkedList(), dims, columnwise);
return res;
}
static private int counter;
private static void setTraversingColumnwise(Object res, double[] data, List<Integer> indices, List<Integer> dimensions, boolean columnwise) {
if (indices.isEmpty()) {
counter = 0;
}
if (dimensions.isEmpty()) {
System.out.println(Arrays.toString(indices.toArray()));
setInDoubleArray(res, indices, data[counter++], columnwise);
} else {
int d = dimensions.remove(0);
for (int i = 0; i < d; i++) {
indices.add(i);
setTraversingColumnwise(res, data, indices, dimensions, columnwise);
indices.remove(indices.size() - 1);
}
dimensions.add(0,d);
}
}
private static void setInDoubleArray(Object doubleArr, List<Integer> indices, double data, boolean reverse) {
if (reverse) {
indices = new LinkedList<>(indices);
Collections.reverse(indices);
}
Object cur = doubleArr;
Iterator<Integer> iter = indices.iterator();
while (iter.hasNext()) {
int indx = iter.next();
if (iter.hasNext()) {
cur = ((Object[]) cur)[indx];
} else {
((double[]) cur)[indx] = data;
}
}
}
Sample usage :
double[][] res =(double[][]) unflattenMatrix(false,
new double[]{1, 2, 3, 4, 5, 6, 7, 8}, new int[]{2, 4});
The following Java code:
public static void main(String args[]) {
int[] x = new int[] {1, 2, 3};
int[] y = new int[] {1, 2, 3};
LinkedList<int[]> list = new LinkedList<int[]>();
list.add(x);
System.out.println("List contains y: " + list.contains(y));
}
gives the output
List contains y: false
which makes sense as x and y are references to different memory locations, however there is also a sense in which they are equal (they have the the same elements in the same order).
Is there a data structure which would return true to the query list.contains(y) in this example?
I don't believe there is a Java data structure that would return true for contains() as you have described.
The issue, as you probably know, is that for Java arrays, equals() only tests for Object identity and not "equality" as most would define it.
Since contains() relies on equals() in this case (and most of the time), you're stuck with the given behaviour.
You would have to implement a List that specifically overrode contains() to provide your desired behaviour for Java arrays, probably using Arrays.equals().
My suggestion is to instead use a List instead of an array; you'd then have a List<List<Integer>>. contains() should work in this scenario as it'll use equals() on the underyling List implementation.
You need to define a comparator for your arrays. Then when the list looks up the elements, it will use your comparator to see if they're the same:
public static void main(String args[]) {
int[] x = new int[] {1, 2, 3};
int[] y = new int[] {1, 2, 3};
LinkedList<int[]> list = new LinkedList<int[]>(new Comparator<int[]>() {
#Override
public int compare(int[] a1, int[] a2) {
if(a1 == a2) return 0;
if(a1 == null && a2 != null) return -1;
if(a1 != null && a2 == null) return 1;
if(a1.size() < a2.size()) return -1;
if(a1.size() > a2.size()) return 1;
for(int i = 0; i < a1.size(); i++) {
int comp = a1[i] - a2[i];
if(comp < 0) return -1;
if(comp > 0) return 1;
}
return 0;
}
});
list.add(x);
System.out.println("List contains y: " + list.contains(y));
}
It looks like you're really looking for a Set implementation.
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied
by its name, this interface models the mathematical set abstraction.
If you want to store sets of int values, you can use this Tuple class I wrote a while ago for another question on SO.
Set<Tuple> myTuples = new HashSet<Tuple>();
Tuple<Integer> x = Tuple.create(1, 2, 3);
Tuple<Integer> y = Tuple.create(1, 2, 3);
myTuples.add(x);
System.out.println("Set contains y: " + myTuples.contains(y)); // prints true
If order matters, you can use a SortedSet.
LinkedList uses equals to implement contains, so this should work:
public static void main(String args[]) {
static class Ints {
int[] array;
public Ints(int[] array) {
this.array = array;
}
public boolean equals(Object other) {
if (other instanceof Ints) {
return arraysEqual((Ints) other);
}
}
public boolean arraysEqual(Ints other) {
// check that this.array and other.array are same length and
// have same values. Do a null check somewhere too. :)
}
}
Ints x = new Ints(new int[] {1, 2, 3});
Ints y = new Ints(new int[] {1, 2, 3});
LinkedList<Ints> list = new LinkedList<int[]>();
list.add(x);
System.out.println("List contains y: " + list.contains(y));
}
You would probably want to extend LinkedList into your own custom data structure and define a custom equality method if you wanted anything outside of the standard checking that is in place.
If you could use a Set instead of an array it might be easier. Have a look here or here
Suppose the user enter an array, for example:
Array = {France, Spain, France, France, Italy, Spain, Spain, Italy}
which I did know the length of it
the index array would be:
index = {0, 1, 2, 3, 4, 5, 6, 7}
Now, after sorting it using Arrays.sort(Array);
newArray will be like:
newArray = {France, France, France, Italy, Italy, Spain, Spain, Spain}
and the newIndex will be:
newIndex = {0, 2, 3, 4, 7, 1, 5, 6}
The problem is: how can I find the newIndex from the input Array?
Don't sort the array to start with. Sort the index array, passing in a comparator which compares values by using them as indexes into the array. So you end up with newIndex as the result of the sort, and it's trivial to go from there to the sorted array of actual items.
Admittedly that means sorting an array of integers in a custom way - which either means using an Integer[] and the standard Java library, or a 3rd party library which has an "IntComparator" interface which can be used in conjunction with a sort(int[], IntComparator) type of method.
EDIT: Okay, here's an example comparator. For the sake of simplicity I'll assume you only want to sort an "original" array of strings... and I won't bother with nullity testing.
public class ArrayIndexComparator implements Comparator<Integer>
{
private final String[] array;
public ArrayIndexComparator(String[] array)
{
this.array = array;
}
public Integer[] createIndexArray()
{
Integer[] indexes = new Integer[array.length];
for (int i = 0; i < array.length; i++)
{
indexes[i] = i; // Autoboxing
}
return indexes;
}
#Override
public int compare(Integer index1, Integer index2)
{
// Autounbox from Integer to int to use as array indexes
return array[index1].compareTo(array[index2]);
}
}
You'd use it like this:
String[] countries = { "France", "Spain", ... };
ArrayIndexComparator comparator = new ArrayIndexComparator(countries);
Integer[] indexes = comparator.createIndexArray();
Arrays.sort(indexes, comparator);
// Now the indexes are in appropriate order.
Concise way of achieving this with Java 8 Stream API,
final String[] strArr = {"France", "Spain", "France"};
int[] sortedIndices = IntStream.range(0, strArr.length)
.boxed().sorted((i, j) -> strArr[i].compareTo(strArr[j]) )
.mapToInt(ele -> ele).toArray();
TreeMap<String,Int> map = new TreeMap<String,Int>();
for( int i : indexes ) {
map.put( stringarray[i], i );
}
Now iterator over map.values() to retrieve the indexes in sort order, and over map.keySet() to get the strings, or over map.entrySet() to get the String-index-Pairs.
If having a scenario of repeatedly sorting primitive float or int arrays with positive values, then a method like below yields much better (x3~x4) speed compared to using any comparators:
long time = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
float[] array = RandomUtils.randomFloatArray(-1, 1, 3000);
long[] valueKeyPairs = new long[array.length];
for (int j = 0; j < array.length; ++j) {
valueKeyPairs[j] = (((long) Float.floatToIntBits(array[j])) << 32) | (j & 0xffffffffL);
}
Arrays.sort(valueKeyPairs);
/**Then use this to retrieve the original value and index*/
//long l = valueKeyPairs[j];
//float value = Float.intBitsToFloat((int) (l >> 32));
//int index = (int) (l);
}
long millis = System.currentTimeMillis() - time;
I made the following based on #Skeet's code. I think it is a little more OOPie. I dunno.
public static <T extends Comparable<T>> List<Integer> sortIndex(List<T> in) {
ArrayList<Integer> index = new ArrayList<>();
for (int i = 0; i < in.size(); i++) {
index.add(i);
}
Collections.sort(index, new Comparator<Integer>() {
#Override
public int compare(Integer idx1, Integer idx2) {
return in.get(idx1).compareTo(in.get(idx2));
}
});
return index;
}
Instead of the class that implements the sorting and indexing having the Comparator code for the different objects coming in, the objects in the original array must implement the Comparable interface. It seems many objects of interest have a natural ordering and have the Comparable interface implemented already.
public static void main(String[] args) {
List<Integer> a1 = new ArrayList<>(Arrays.asList(2, 3, 9, 4, 1));
// Just pass in the list to have its indexes sorted by the natural ordering
List<Integer> idx = sortIndex(a1);
List<Double> a2 = new ArrayList<>(Arrays.asList(1.0, 5.3, 5.2, -3.1, 0.3));
idx = sortIndex(a2);
List<numBits> a3 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
a3.add(new numBits(i));
}
// If you need to sort the indexes of your own object, you must implement
// the Comparable Interface.
idx = sortIndex(a3);
}
static class numBits implements Comparable<numBits> {
private int a;
public numBits(int i) {
a = i;
}
public String toString() {
return Integer.toString(a);
}
// Sort by the total number of bits in the number.
#Override
public int compareTo(numBits that) {
if (Integer.bitCount(this.a) < Integer.bitCount(that.a))
return -1;
if (Integer.bitCount(this.a) > Integer.bitCount(that.a))
return 1;
return 0;
}
}
One way you could do this is to Wrap the original index and country name into a separate Class. Then sort the Array based on the names. This way, your original indexes will be preserved.
What Comes at first Glance is Map them like that
Map <Integer, String> map = new HashMap<Integer, String>();
map.put(0, "France");
map.put(1, "Spain");
map.put(2, "France");
and then sort them by value like that and then you can know their indexes and values (key, values) just print the map
Iterator mapIterator = map.keySet().iterator();
while (mapIterator .hasNext()) {
String key = mapIterator.next().toString();
String value = map.get(key).toString();
System.out.println(key + " " + value);
}
i found solution.
List<String> a = {b, a, d, c};
List<Integer> b = {2, 1, 4, 3};
and if a sort
private void setsortb() {
List<String> beforeA = new ArrayList<>();
List<Integer> beforeB = new ArrayList<>();
beforeA.addAll(a);
beforeB.addAll(b);
a.sort();//change like this {a, b, c, d}
for(int i = 0; i < beforeA.size(); i++) {
int index = beforeA.indexOf(a.get(i));
b.set(i, beforeB.get(i));
}
}
like this
result
a = {a, b, c, d}
b = {1, 2, 3, 4}