import java.util.ArrayList; /** * Alihan Hüyük * Vector class using ArrayLists */ public class Vector { private ArrayList array; //default constructor public Vector() { array = new ArrayList(); } public Vector(ArrayList array) { /** * There is two possible ways to implement this constructor: * * 1: initializing the array list with a set capacity of elements * avoids realocating memory every time we add new elements to the list this.array = new ArrayList(array.size()); * 2: initializing the array list as an empty list * realocates memory regularly when we add new elements to the list this.array = new ArrayList(); */ this.array = new ArrayList(); for(int element : array) this.array.add(element); /** * I have done some tests with each method to see if there is a performance difference between them * Actually there was but 1st method was not the faster one as I expected * * Here are the run times for each method in nanoseconds * (10.000.000 iterations, intel i7 2600) * 1: 6209311796 * 2: 3755491160 * * You can see the measuring method I used in the main() method * I have done some research, why I was getting opposite results from what I expected * It turns out that when I allocate the memory at once, Garbage Collector does more iterations * * --for more details about the Garbage Collector, see this stack overflow thread-- * http://stackoverflow.com/questions/32302791/why-is-this-slower-when-giving-arraylist-an-initial-capacity */ } public static void main(String[] args) { //creating an sample array list ArrayList array = new ArrayList(); for(int i = 0; i < 10_000_000; i++) array.add(i); //initializing a vector object long oldTime = System.nanoTime(); Vector vector = new Vector(array); long time = System.nanoTime(); System.out.println(time - oldTime); } //this constructor enables the class to be initialized like this: //Vector v = new Vector(1, 2, 3); public Vector(int... array) { this.array = new ArrayList(); for(int element : array) this.array.add(element); } //cloning method and constructor public Vector(Vector other) { this.array = new ArrayList(); for(int element : other.array) this.array.add(element); } public Vector clone() { //a possible use of "this" other than differentiating variables return new Vector(this); } public String toString() { String string = "["; for(int element : array) string += element + ", "; //removing the excess ", " left from the last iteration of the for loop string = string.substring(0, string.length() - 2); string += "]"; return string; } public boolean equals(Vector other) { boolean result = this.array.size() == other.array.size(); /** * There is yet again two possible implementations: * * 1: using a regular for loop for(int i = 0; i < array.size() && result; i++) result = this.array.get(i) == other.array.get(i); * 2: using iterators like advanced for loop uses Iterator i = this.array.iterator(); Iterator j = other.array.iterator(); while(i.hasNext() && result) result = i.next() == j.next(); //note: java.util.Iterator needs to be imported */ for(int i = 0; i < array.size() && result; i++) result = this.array.get(i) == other.array.get(i); /** * I have done some tests again to see which approach was more efficient * As I expected there was not any noticable difference, * since accessing an element of an array does not require any complex operations * Using iterators might have increased the performance radically in a linked list instead of an array list * * Here are the run times for each method in nanoseconds * (10.000.000 iterations, intel i7 2600) * 1: 28066 * 2: 23973 * * regular for loops were prefered in the rest of the implementation for simplicity */ return result; } //carrying over standart array list methods public void add(int element) { array.add(element); } public void remove(int index) { array.remove(index); } public int size() { return array.size(); } /** * From now on, non-existing elements are assumed to be 0 * to be able to do arithmetic operations between vectors with different sizes */ // "advanced" get method // returns 0 for non-existing elements public int get(int index) { int result = 0; if(index < array.size()) result = array.get(index); return result; } // "advanced" set method // adds new elements to the list if it is necessary public void set(int index, int element) { if(index < array.size()) //index exists in the array, safe to use set method of array lists array.set(index, element); else { //zeros needs to be added until the desired index number is reached for(int i = array.size(); i < index; i++) array.add(0); //desired index number is reached, adding the new element array.add(element); } } /** * Arithmetic operations are quite self-explanatory * Only thing to keep in mind is that every operation has a dynamic and a static version * Dynamic versions change the object's itself whereas static versions return a new object * * Also the dynamic versions return the object itself to enable chaining * for example: * * instead of writing * v1.add(v2); * v1.mul(2); * * it will be possible to write * v1.add(v2).mul(2); * * "advanced" get and set methods will prevent any possible index out of bounds exceptions */ // adding // for example: [1, 2, 3] + [4, 0, 4] = [5, 2, 7] public Vector add(Vector other) { for(int i = 0; i < Math.max(this.size(), other.size()); i++) this.set(i, this.get(i) + other.get(i)); //a possible use of "this" other than differentiating variables return this; } public static Vector add(Vector v1, Vector v2) { Vector result = new Vector(); for(int i = 0; i < Math.max(v1.size(), v2.size()); i++) result.add(v1.get(i) + v2.get(i)); return result; } // multiplying with a constant // for example: [1, 2, 3] * -2 = [-2, -4, -6] public Vector mul(int scale) { for(int i = 0; i < size(); i++) set(i, get(i) * scale); return this; } public static Vector mul(Vector vector, int scale) { Vector result = new Vector(); for(int i = 0; i < vector.size(); i++) result.add(vector.get(i) * scale); return result; } // subtracting // for example: [1, 2, 3] - [4, 0, 4] = [-3, 2, -1] public Vector sub(Vector other) { for(int i = 0; i < Math.max(this.size(), other.size()); i++) this.set(i, this.get(i) - other.get(i)); return this; } public static Vector sub(Vector v1, Vector v2) { Vector result = new Vector(); for(int i = 0; i < Math.max(v1.size(), v2.size()); i++) result.add(v1.get(i) - v2.get(i)); return result; } // dot product (or scalar product) of two vectors // for example: [1, 2, 3] * [4, 0, 4] = 1*4 + 2*0 + 3*4 = 4 + 0 + 12 = 16 public static int dot(Vector v1, Vector v2) { int result = 0; for(int i = 0; i < Math.min(v1.size(), v2.size()); i++) result += v1.get(i) * v2.get(i); return result; } }