At first I write:
private ArrayList<Integer> getDataList() {
ArrayList<Integer> dataList = new ArrayList<>(LEN);
for (int i = 0; i < LEN; i++)
dataList.add(i);
Collections.shuffle(dataList);
return dataList;
}
Later I decide to use generic:
private <E> ArrayList<E> getDataList() {
ArrayList<E> dataList = new ArrayList<>(LEN);
for (int i = 0; i < LEN; i++)
dataList.add(/* a procedure that generates E instance from index i*/);
Collections.shuffle(dataList);
return dataList;
}
Static methods in interface are not override-able, so can't call static method on E to generate instance.
How to refactor this to use generics? Thanks.
You need to supply something that does the creation as a method parameter:
private <E> ArrayList<E> getDataList(IntFunction<? extends E> fn) {
Then:
dataList.add(fn.apply(i));
And invoke like:
List<Integer> integerList = getDataList(Integer::valueOf); // Same output as non-generic code.
List<String> stringList = getDataList(i -> "Item " + i);
Related
There is my problem, I've a function listToSublists I absolutely want to be generic so i've wrote this :
public static List<List<? extends Object>> listToSubLists(List<? extends Object> elements, int sublistsLength) {
....
}
And i want to call that function that way :
List<LigneMaj> datas = ...;
List<List<LigneMaj>> datasPaquets = BatchUtils.listToSubLists(datas, 100);
It appears that it is impossible to compile and I'm struggling to understand why i'm getting this error :
Type mismatch: cannot convert from List<List<? extends Object>> to List<List<LigneMaj>>
Could someone explain what I am missing please?
public static <T> List<List<T>> listToSubLists(List<T> elements, int sublistsLength) {
if (!elements.isEmpty()) {
List<List<T>> result = new ArrayList<>();
final int sublists = ((elements.size() - 1) / sublistsLength) + 1;
for (int i = 0; i < sublists; i++) {
result.add(new ArrayList<>());
}
for (int currentIndex = 0; currentIndex < elements.size(); currentIndex++) {
final T elem = elements.get(currentIndex);
result.get(currentIndex / sublistsLength).add(elem);
}
return result;
} else {
return Collections.emptyList();
}
}
this is my first question here. I have played around a little and created a class named MyIterator which implements the interface Iterator. Everything works great when I use it with the following array :
String u [] = {"Hello", "Whats", "Up"};
MyIterator <String> strins = new MyIterator <> (u);
while(strins.hasNext()) {
System.out.print(strins.next() + " ");
}
It works perfectly but it just doesn't work when I use an Integer array..
Integer array[] [] = new Integer [3] [3];
for (int i=0; i<3; i++) {
for (int t=0; t<3; t++) {
array[i] [t] = 3;
}
}
MyIterator <Integer> it = new MyIterator <> (array);
while(it.hasNext()) {
System.out.println(it.next());
}
Here is my MyIterator class guys :
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyIterator <E> implements Iterator <E> {
private int i;
private E a[];
public MyIterator(E a[]) {
i=0;
this.a=a;
}
public boolean hasNext() {
return i<a.length;
}
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
} else {
return a[i++];
}
}
}
It says : Cannot infer type arguments for MyIterator<>
I would be very happy if people could help me since learning java is harder when the university is closed :(
If you guys see other things that could be improved, please tell me :)
The Problem is that you are not creating a normal Integer array, but instead a 2D Integer array Integer array[][]
So if you want to iterate over that 2D array you have to Iterate over the outer array or Integer[]:
MyIterator <Integer[]> it = new MyIterator <> (array);
If you want to also iterate the inner Integer array you will need to use 2 nested MyIterator objects:
final MyIterator<Integer[]> it = new MyIterator<>(array);
while (it.hasNext()) {
final MyIterator<Integer> itInner = new MyIterator<>(it.next());
while (itInner.hasNext()) {
System.out.println(itInner.next());
}
}
I have the following:
public class RandomList {
private List<Integer> list;
public List<Integer> getList() {
return list;
}
public RandomList (int n) {
list = new ArrayList<Integer>();
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
for (int i=0; i < n; i++)
{
Integer r = rand.nextInt();
list.add(r);
}
}
}
which gives me a list filled with random Integer values. I would like to generalize this, to also get a list of random Character values or perhaps lists of other types' random values.
So what I want is a generic type version, class RandomList<T>. I can replace everywhere "Integer" by "T", but am stuck at the line Integer r = rand.nextInt(); which would read different for different types.
I am thinking of doing the following:
pass in the class of the generic type to RandomList
using instanceof check the passed in class against the desired types (Integer, Character...) and depending on the check return the proper random value
Does this make sense? Is there another/better way to achieve what I want?
First method (inferior)
In Java you can't check for the generic type, at least not without reflection. You're on the money with the generic type, so you'd do something like this:
public class RandomList<T> {
private List<T> list;
private Class<T> clazz;
public List<T> getList() {
return list;
}
public RandomList (Class<T> clazz, int n) {
this.clazz = clazz;
list = new ArrayList<T>();
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
if (clazz.isAssignableFrom(Integer.class)) {
for (int i = 0; i < n; i++) {
Integer r = rand.nextInt();
list.add(r);
}
}
else {
throw new IllegalArgumentException("Unsupported class: " + clazz.getName());
}
}
}
Second method (superior)
Alternatively, you could generalise this even further and add a Function to produce the randomised results. Note that this requires Java 8. If you're not on Java 8, you could just define an interface and construct that anonymously.
public class RandomList<T> {
private List<T> list;
public List<T> getList() {
return list;
}
public RandomList (Function<Random, T> creator, int n) {
list = new ArrayList<T>();
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
for (int i = 0; i < n; i++) {
list.add(creator.apply(rand));
}
}
}
Construct a new instance using:
RandomList<Integer> list = new RandomList<>(rand -> rand.nextInt(), 10);
Third method (cleaner)
Edit: This occurred to me later, but you seem to be using Java 8, so you could just use streams:
List<Integer> list = Stream.generate(() -> rand.nextInt()).limit(10).collect(Collectors.toList())
What is the best way to write a method that given a type T and integer n, returns a list of n newly created objects of type T. Is it possible to pass a constructor as argument or will I have to accomplish this in some other way?
Was thinking something like this
public <T> ArrayList<Object> generate(T type, int amount){
ArrayList<Object> objects = new ArrayList();
for (int i = 0; i < amount; i ++){
objects.add(new bla bla)...
}
Use a generic method.
public <T> List<T> getList(Class<T> clazz, int size) throws InstantiationException, IllegalAccessException{
List<T> list = new ArrayList<T>();
for(int x = 0; x < size; x++){
list.add(clazz.newInstance());
}
return list;
}
NOTE: This will only work for objects with a default constructor. If you want to create a List of objects that do not contain a default constructor you must do so using reflection to pick the appropriate constructor.
public static <T> List<T> generate(Class<T> clazz, int amount) {
ArrayList<Object> objects = new ArrayList();
for (int i = 0; i < amount; i ++){
objects.add(clazz.newInstance());
}
return list;
}
The code above actually tries to use default constructor. You can pass a reflection reference to an appropriate constructor of your choice if you want. A list of constructors can be obtained by calling Class.getConstructors().
Then your code would look like this:
public static <T> List<T> generate(Constructor<T> constructor, int amount) {
ArrayList<Object> objects = new ArrayList();
for (int i = 0; i < amount; i ++){
objects.add(constructor.newInstance(param1, param2, ...)); // fill the arguments
}
return list;
}
I have about 10+ classes, and each one has a LUMP_INDEX and SIZE static constant.
I want an array of each of these classes, where the size of the array is calculated using those two constants.
At the moment i have a function for each class to create the array, something along the lines of:
private Plane[] readPlanes()
{
int count = header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE;
Plane[] planes = new Plane[count];
for(int i = 0; i < count; i++)
planes[i] = new Plane();
return planes;
}
private Node[] readNodes()
{
int count = header.lumps[Node.LUMP_INDEX].filelen / Node.SIZE;
Node[] nodes = new Node[count];
for(int i = 0; i < count; i++)
nodes[i] = new Node();
return nodes;
}
private Leaf[] readLeaves()
{
int count = header.lumps[Leaf.LUMP_INDEX].filelen / Leaf.SIZE;
Leaf[] leaves = new Leaf[count];
for(int i = 0; i < count; i++)
leaves[i] = new Leaf();
return leaves;
}
etc.
There are 10 of these functions, and the only differences is the class type, so as you can see, there's a ton of duplication.
Does any one have any ideas on how to avoid this duplication?
Thanks.
(I asked a similar question before, but i guess the way i asked it was a bit off)
Use Java generics. That way, you can just write one generic method and specify a type parameter each time you use it.
Bala's solution is close. You can't access constants from the generic type though, so I'd create a getCount() (or whatever you want to name it) and have each subtype implement it with the appropriate constants.
interface LumpySize<L extends LumpySize> {
int getCount(); // subtypes return the appropriate header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE;
T[] initializeArray();
abstract <T extends LumpySize> static class Base implements LumpySize<T> {
protected T[] initializeArray(Class<T> cls) {
int count = getCount();
T[] lumps = (T[]) Array.newInstance(cls, count);
for(int i = 0; i < count; i++) {
try {
lumps[i] = cls.newInstance();
} catch (Exception e) { // obviously this isn't good practice.
throw new RuntimeException(e);
}
}
return lumps;
}
}
}
class Plane extends LumpySize.Base<Plane> {
public int getCount() {
return header.lumps[Plane.LUMP_INDEX].filelen / Plane.SIZE; // assuming header is available somewhere
}
public Plane[] initializeArray() { return initializeArray(Plane.class); }
}
Okey doke ... I've tested this to make sure, and I believe it does what you're looking for.
You need an interface:
public interface MyInterface
{
public int getSize();
public int getLumpIndex();
}
Your classes implement that interface:
public class Plane implements MyInterface
{
...
public int getSize()
{
return SIZE;
}
public int getLumpIndex()
{
return LUMP_INDEX;
}
}
In the class that header is an instance of, you have ...
public <E extends MyInterface> E[]
getArray(Class<E> c, MyInterface foo)
{
int count = lumps[foo.getLumpIndex()].filelen / foo.getSize();
E[] myArray = (E[]) Array.newInstance(c, count);
for(int i = 0; i < count; i++)
myArray[i] = c.newInstance();
return myArray;
}
You could call it from say, your Plane class as:
Plane[] p = header.getArray(Plane.class, this);
I think? :) Can someone look at this and see if I'm off?
(EDIT: Becasue I've tested it now - That works)
On an additional note, you could eliminate the getters in each class by making getArray() take the size and index as arguments:
public <E extends MyInterface> E[]
getArray(Class<E> c, int size, int index)
{
int count = lumps[index].filelen / size;
E[] myArray = (E[]) Array.newInstance(c, count);
for(int i = 0; i < count; i++)
myArray[i] = c.newInstance();
return myArray;
}
And call it as:
Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);
from inside your classes. The interface just becomes empty to provide the generic type and you don't have to define the getter methods.
OR (last edit I promise, but this does give you choices and explains a bit about generics)
Ditch the interface. What this removes is some sanity checking because the method doesn't care what type of object you give it:
public <E> E[]
getArray(Class<E> c, int size, int index)
{
...
Now you don't have to define the interface or implement it, you just call:
Plane p[] = header.getArray(Plane.class, SIZE, LUMP_INDEX);
Use generics, but you'll need to pass in some sort of factory object to construct instances to put in your collection, eg:
public class MyClass {
public <E> E[] getArray(IObjectFactory builder, int index, int size){
ArrayList<E> arrayList = new ArrayList<E>();
int count = header.lumps[index].filelen / size;//wasn'tsure where header was coming from...
for(int i = 0; i< count; i++){
E newInstance = builder.getNewInstance();
arrayList.add(newInstance);
}
return (E[]) arrayList.toArray();
}
}
interface IObjectFactory {
<E> E getNewInstance();
}