Home > OS >  Creating a generic ArrayList in Java?
Creating a generic ArrayList in Java?

Time:10-29

How can I create a generic Arraylist in java? How to store different data types? If I want to create an integer arraylist that can contains all the methods, It will work. but using generic <T> It gives me error. How can I fix that?

public class Array<T> {

    int array[];
    
    Array(int size) {
        array = new int[size];
    }
}
Array <String>a = new Array(5);

is any chance to work like that?

CodePudding user response:

How to store different data types?

There are two ways to 'read' that question.

I want to store either primitives like int, OR objects

Java mostly can't do that. You can make an array dynamically and thus be flexible; arrays are themselves objects.

public class MyArrayList {
  private Object store;
  private int length;

  public MyArrayList(Class<?> componentType) {
    store = java.lang.reflect.Array.newInstance(componentType, 10);
  }
}

This can even make int[] arrays. I intentionally am not showing any generics here because generics cannot be primitive. You simply cannot make a List<int> of any sort, period. Project Valhalla may change that for some future java version, but the most recent java version does not have valhalla included and will not for some time.

A better way to do this instead is polymorphism: Make an abstract class AbstractList and then have multiple implementations:

public abstract class AbstractList<T> {
  public abstract T get(int idx);
}

public final class IntList extends AbstractList<T> {
  private int[] store = ...;

  public Integer get(int idx) {
    return store[idx];
  }
}

Note again the awkwardness of Integer vs int.

I want to store some specific reference type, i.e. Strings OR LocalDates.

Arrays are reified and (incorrectly) covariant, generics are invariant and erased. They do not mix and match. This is no problem - you just write the assurance that you don't return the wrong type on your own and add the appropriate ignore warnings code. Object is already everything you want - after all, all types are a subtype of object. Therefore, an Object[] can store any reference type of any stripe. This is in fact how java's already existing ArrayList implementation works:

class MyList<T> {
  private Object[] data; // not T[] - you can't make those.
  private int size;

  public MyList() {
    this.data = new Object[10];
    this.size = 0;
  }

  public void add(T in) { // this ensures only Ts can be added
    ensureCapacity();
    this.data[size  ] = in;
  }

  private void ensureCapacity() {
    if (size == data.length) {
      // Consider data.length*2 overflowing and such
      // I'm keeping it (over-)simplified here

      Object[] a = new Object[data.length * 2];
      System.arraycopy(data, 0, a, 0, data.length);
      data = a;
    }
  }

  @SuppressWarnings("unchecked")
  public T get(int idx) {
    return (T) data[idx];
  }
}

Generics are 'erased', meaning: The compiler uses generics to silently inject casts and do some checking if your code is correct - at runtime, the runtime does nothing and has no idea what generics are, hence a cast to (T) does literally nothing whatsoever: There is no way to check it at runtime as T is gone at runtime. You're just adding that cast because otherwise the compiler will note that you're returning some Object when you're supposed to return a T. The compiler can't do it, but your eyeballs and human brain can: You know that, given that the add() method only takes in Ts, the objects in that array will be objects whose type is T or some subtype of T, even if the compiler can't be sure, you are. So, you add the cast, and tell the compiler to stop whining (add @SuppressWarnings), and all is well.

Again, exactly how java.util.ArrayList does it.

But I really want a T[]

And I want a pony. You can't do that. The only thing you can do is receive a T[] or receive code that can make them, you cannot make them yourself. If you must:

public class SillyArrayList<T> {
  private final IntFunction<T[]> arrayMaker;
  private T[] data;
  private int size;

  public SillyArrayList(IntFunction<T[]> arrayMaker) {
    this.arrayMaker = arrayMaker;
    data = arrayMaker.apply(10);
  }
  
  public void add(T in) {
    ensureCapacity();
    this.data[size  ] = in;
  }

  private void ensureCapacity() {
    if (size == data.length) {
      // Consider data.length*2 overflowing and such
      // I'm keeping it (over-)simplified here

      T[] a = arrayMaker.apply(data.length * 2);
      System.arraycopy(data, 0, a, 0, data.length);
      data = a;
    }
  }

  // no suppress needed
  public T get(int idx) {
    return data[idx];
  }
}

// and to use:

SillyArrayList<String> myList = new SillyArrayList<>(String[]::new);

The caller knows what T is (it's String), so we let the caller pass in a function that will turn an int into an array of the right type as large as that int; String[]::new is shorthand for size -> new String[size] which is shorthand for:

class StringArrMaker implements IntFunction<String[]> {
  @Override public String[] apply(int size) {
    return new String[size];
  }
}
return new StringArrMaker();

In other words, we pass a thing that can make arrays to your arraylist so that the arraylist can use that, as it can't make T[] on its own.

However, this is all a lot of hoopla for no purpose whatsoever - it is trivial to manually ensure the basic arraylist code (that adds an unsafe (T) cast) is in fact 100% safe and cannot possibly go wrong. The only reason to 'reify' your arrays (have them actually be String[] instead of Object[]) is that you can ask at runtime what an array's type actually is, and the runtime will ensure you don't put the wrong things in:

Object[] a = new String[10]; // legal
a[0] = LocalDate.now(); // compiles - but throws ArrayStoreException

System.out.println(a.getClass().getComponentType()); // prints String.

however, given that the data array does not 'leak' out of your arraylist and shouldn't (it is a private implementation detail after all), there is no point to any of this.

That's presumably why ArrayList uses Object[] and not T[] an IntSupplier.

CodePudding user response:

it is so easy simply just create a class that you want forex Products and define fields and constructor and getter & setter when create an object from that class simply in an array list use this List<Product> products list = new ArrayList<Product>(); by using this line you are creating an object in the product and store int ArrayList

class Product{  
    int id;  
    String name;  
    float price;  
    public Product(int id, String name, float price) {  
        this.id = id;  
        this.name = name;  
        this.price = price;  
    }  
}  
public class JavaArrayListExample {  
    public static void main(String[] args) {  
        List<Product> productsList = new ArrayList<Product>();  
        //Adding Products  
        productsList.add(new Product(1,"HP Laptop",25000f));  
        productsList.add(new Product(2,"Dell Laptop",30000f));  
        List<Float> productPriceList = new ArrayList<Float>();  
        for(Product product: productsList){  
              
            // filtering data of list  
            if(product.price<30000){  
                productPriceList.add(product.price);    // adding price to a productPriceList  
            }  
        }  
        System.out.println(productPriceList);   // displaying data  
    }  
}``` 
  • Related