Dynamiczna generyczna tablica

0

Witam
Jestem samoukiem i obecnie przepracowuję temat dotyczący klas generycznych w Javie.
Mam problem ze stworzyłęm klasy "DynamicArray<E>", która będzie dynamicznie powiększała i pomniejszała swój rozmiar.
Domyślny rozmiar ma być 10; Klasa ma posiadać metodę add()(dodaje element) oraz remove()(usuwa ostatnio dodany element).

Czy ktoś z doświadczonych tutaj użytkowników mógłby spojrzeć na kod i doradzić czy dobrze wykonałem zadanie??
A jeśli nie to proszę o wskazanie gdzie leży błąd
Z góry dziękuję

import java.util.Arrays;

public class DynamicArray<E> {

    private int array[];
    private int size;
    private int capacity;

    public DynamicArray() {
        array = new int[2];
        size = 0;
        capacity = 2;
    }

    public void addElement(int element){
        if (size == capacity){
            ensureCapacity(2);
        }
        array[size] = element;
        size++;
    }

    public void addElement(int index, int element) {
        if (size == capacity) {
            ensureCapacity(10);
        }
        for (int i=size-1; i>=index; i--) {
            array[i+1] = array[i];
        }
        array[index] = element;
        size++;
    }

    private void ensureCapacity(int minCapacity) {
        int temp[] = new int[capacity * minCapacity];
        for (int i = 0; i < capacity; i++) {
            temp[i] = array[i];
        }
        array = temp;
        capacity = capacity * minCapacity;
    }

    public int getElement(int index) {
        return array[index];
    }

    public void remove(int index) {
        if (index>=size || index<0) {
            System.out.println("Brak elementu w indeksie");
        }else {
            for (int i = index; i <size-1 ; i++) {
                array[size-1] = 0;
                size--;
            }
        }
        
    }

    public void trimToSize() {
        System.out.println("Przytnij tablice");
        int temp[] = new int[size];
        for (int i = 0; i < size; i++) {
            temp[i] = array[i];
        }
        array = temp;
        capacity = array.length;
    }


    public int size(){
        return size;

    }

    public int capacity(){
        return capacity;
    }

    
    public void printElements(){
        System.out.println("elements in array are :"+ Arrays.toString(array));

    }
}


0

Źle bo twój typ generyczny to masz tylko w sygnaturze a wszędzie indziej widze int :D A co jak ja chce mieć new DynamicArray<String>() a potem robić array.addElement("alamakota");? O tym że twoja klasa nie jest thread-safe to nie wspominam, bo chyba nie było wymagane.
Domyślny rozmiar miał być 10 a u ciebie jest 2.

0

Stworzyłęm kod od początku. Trochę to trwało ale chyba wygląda to lepiej. Proszę wskazać błędy i w miarę możliwości podpowiedzieć ;)


public class DynamicArray2<T> {
    private int positionPointer;
    private int arraySize;
    private T[] dynamicArray;
    private static final int DEFAULT_ARRAY_SIZE=10;

    public DynamicArray2() {
        this(DEFAULT_ARRAY_SIZE);
    }

    public DynamicArray2(int arraySize) {
        this.arraySize=arraySize;
        dynamicArray=(T[]) new Object[arraySize];
    }

    public void addElement(T element) {
        adjustSize();
        dynamicArray[positionPointer] = element;
        positionPointer++;
    }

    public void addElementAtNode(int index, T element) {
        if(index<positionPointer)
        {
            dynamicArray[index] = element;
        }
            else
            {
                addElement(element);
                throw new ArrayIndexOutOfBoundsException("index "+index+" is greater than the size of array "+(positionPointer-1)+" \nElement added to end of array..");
            }
        }
    
        private void adjustSize() {
            if(positionPointer==arraySize)
            {
                increaseSize();
            }
            else if(positionPointer==(arraySize/4-1))
            {
                decreaseSize();
            }
        }
    
        private void increaseSize() {
            T[] tempArray=(T[]) new Object[2*arraySize];
            for(int i=0;i<positionPointer;i++)
            {
                tempArray[i]=dynamicArray[i];
            }
            dynamicArray=tempArray;
            arraySize=2*arraySize;
        }
    
        private void decreaseSize() {
            T[] tempArray=(T[]) new Object[(arraySize/4)];
            for(int i=0;i<positionPointer;i++)
            {
                tempArray[i]=dynamicArray[i];
            }
            dynamicArray=tempArray;
            arraySize=arraySize/4;
        }
        public int searcElement(T element) {
            for (int i = 0; i < positionPointer; ++i) {
                if (Objects.equals(element, dynamicArray[i])) {
                    return i;
                }
            }
    
            return -1; 
    }
    
        public T getElementAtIndex(int index) {
            if(index<positionPointer)
            {
                return dynamicArray[index];
            }
            else
            {
                throw new ArrayIndexOutOfBoundsException("index "+index+" is greater than the size of array "+positionPointer);
            }
        }
    
        public void removeElementAtIndex(int index) {
            if(index<positionPointer)
            {
                for(int i=index;i<positionPointer-1;i++)
                {
                    dynamicArray[i]=dynamicArray[i+1];
                }
                }
                dynamicArray[positionPointer-1]=null;
                positionPointer--;
                adjustSize();
            }
        public int size() {
            return positionPointer;
        }
    

0

Panowie nie za bardzo kumam co robię źle. Czy może mi ktoś pomóc w modyfikacji powyższego kodu??

1
MaxMann napisał(a):

Panowie nie za bardzo kumam co robię źle

Co ci nie działa?

Co masz z testów operacji na pustej, jednoelementowe, dwuelementowej tablicy?

Na start

DEFAULT_ARRAY_SIZE=2

Więcej do pierwszych rób nie potrzebujesz. Najpierw operacje na jednoelementowej tablicy, później dwuelementowej, przy 3 elementach powinien być "resize". Itd. Lecisz po kolei: red, green, refactor

Sprawdź, czy na początku est pusta (co gdy usuwasz nieistniejący element albo sięgasz po nieistniejący element albo pod nieistniejący index?)
Z jednym elementem: dodajesz, sprawdzasz czy się dodał, sprawdzasz czy możesz pobrać, usuwasz, sprawdzasz czy usunięty i pusta tablica.
Dwa elementy, jak wyżej.
Trzy, za mała pojemność - resize, czy OK?
Usuwasz wszystko, czy ma być resize w dół czy już do końca świata zostanie utrzymana pojemność (dynamiczna tylko w górę?).
Itd

0

Nie spodziewałem się, że tak ciężko będzie mi z tematem klas generycznych.
Spójrzcie proszę na kod i może ktoś z bardziej doświadczonych userów może go poprawić??

public class Bag<T> {

    private final int INITIAL_CAPACITY = 10;
    private int capacity;
    private T[] data;
    private int count;


    @SuppressWarnings("unchecked")
    public Bag()
    {

        setCapacity(INITIAL_CAPACITY);
        data = (T[])new Object[capacity];
        count = 0;
    }


    public void setCapacity(int newCapacity)
    {
        capacity = newCapacity;
    }

    public int getSize() {
        return count;
    }


    public void add(T element) {
        if (count >= capacity)
        {
            resize();
        }
        else {
            data[count] = element;
            count++;
        }
    }

    private void resize() {
        capacity *= 2;
        data = Arrays.copyOf(data, capacity);
    }

    public void print() {
        System.out.println("The bag has " + count + " elements:");
        if (!isEmpty()) {
            System.out.print(data[0].toString());
            for (int i = 1; i < count; i++) {
                System.out.print(", " + data[i]);
            }
            System.out.println();
        }
    }

    public T remove()
    {
        T target = null;
        if(isEmpty())
        {
            throw new IllegalStateException("Bag - remove()");
        }
        target = data[count - 1];
        data[count - 1] = null;
        count--;

        return target;
    }

    public T remove(T element)
    {
        T target = null;
        Boolean done = false;
        int i = 0;
        while(i < count && !done)
        {
            if (data[i] == element)
            {
                target = data[i];
                data[i] = data[count-1];
                data[count - 1] = null;
                count--;
                done = true;
            }
            i++;
        }
        if(!done)
        {
            throw new NoSuchElementException("Bag - remove(element)");
        }
        return target;
    }

    public boolean contains(T target) {
        Boolean found = false;
        int i = 0;
        while(i < count && !found) {
            if (data[i] == target)
            {
                found = true;
            }
            i++;
        }
        return found;
    }

    public boolean isEmpty() {
        return (count == 0);
    }

    public T get(int index) {
        T target = null;
        if(index < 0 || index >= count)
        {
            throw new IndexOutOfBoundsException("Bag - get(index)");
        }
        target = data[index];
        return target;
    }
}

1

@MaxMann:

Już ci pisałem.
Zaimplementuj najpierw najprostsze operacje: add, get i je przetestuj
Potem remove i go przetestuj
Potem set i go przetestuj

itd

Wtedy nie będzie "tak ciężko"

1

Kilka rzeczy, które widzę

data = (T[])new Object[capacity];

dlaczego tak dziwnie i podejrzewam nieprawidłowo? Widać, ze walczysz z warningami

  data = new T[capacity];

Inne:

Boolean found = false;

to jest obiektowy typ logiczny (opakowanie nad boolean ), ten kod jest w rzeczywistości rozwijany przez tzw autoboxning do mniej więcej
Boolean found = new Boolean (false);
tego się czasem używa z pewnych powodów, ale tu nie jest potrzebny, użyj typu prostego boolean

boolean found = false;

teraz

public T remove()

ja bym zmienił na

public T removeLast()

choć Twoja szkoła też jest obecna.

public void setCapacity(int newCapacity)
    {
        capacity = newCapacity;
    }

Prawdopodobnie nie chcesz, aby ktoś zdewastował cały kontener takim publicznym seterem. Wyeliminować lub zabezpieczyć.
Za to możesz zrobić konstruktor z podobnym argumentem, bardzo pozytywne.

Za to chyba bym zrobił publiczną stałą, może się komuś do czegoś ??? przydać, a pisze się to w Javie ze static.

public static final int INITIAL_CAPACITY = 10;
0
AnyKtokolwiek napisał(a):
data = (T[])new Object[capacity];

dlaczego tak dziwnie i podejrzewam nieprawidłowo?

"Uroki" prehisto(gene)rycznej epoki javy-łupanej
https://www.baeldung.com/java-generic-array

Czasy AFAIK: J2SE 5.0 - September 30, 2004

PS
Jeszcze inne zaszłości:
AFAIR tablice to runtime time, kolekcje compile time
https://www.informit.com/articles/article.aspx?p=2861454&seqNum=3
https://wiki.c2.com/?JavaArraysBreakTypeSafety
https://www.baeldung.com/java-arraystoreexception

0

Panowie uczę się z tutorialy znalezionych w sieci i probuję przyswoić materiał typów generycznych ale jak sami widzicie idzie mi to opornie.
Widzę że rozumiecie te zadanie i co w moim kodzie nie funkcjonuje. Jeśli macie wolną chwilę to prosiłbym o poprawienie kodu, z góry dzięki

0

@MaxMann:

Do trzech razy sztuka, może za trzecim razem?

Zrób sobie przykład z Box< T > i zaimplementuj tylko add(T t) i get(int idx)
Jak to zrobisz dla jednego elementu, dwóch, trzech, to się czegoś nauczysz i wrócisz do tej tablicy.

0
MaxMann napisał(a):

Panowie uczę się z tutorialy znalezionych w sieci i probuję przyswoić materiał typów generycznych ale jak sami widzicie idzie mi to opornie.

Widzę że rozumiecie te zadanie i co w moim kodzie nie funkcjonuje. Jeśli macie wolną chwilę to prosiłbym o poprawienie kodu, z góry dzięki

Chyba przeceniasz generyki i cię to przeraża. Nie jest to wcale różniące się od kontenera na konkretny typ Pomarańcza, przynajmniej pod względem koncepcyjnym i algorytmicznym.
Tj algorytmy na generykach pisze sie dokładnie tak samo, jak na konkretnych.
Oprócz pierwszej wersji, nie masz już potknięć generycznych, ale algorytmiczne

Generyczność to <zaledwie> dostarczenie zmiennego typu. (wiem, mistrzowie javy znajdą kontr-przykłady na ambitnym poziomie, ale nie o tym mówimy).

0
MaxMann napisał(a):

Panowie uczę się z tutorialy znalezionych w sieci i probuję przyswoić materiał typów generycznych ale jak sami widzicie idzie mi to opornie.

Widzę że rozumiecie te zadanie i co w moim kodzie nie funkcjonuje. Jeśli macie wolną chwilę to prosiłbym o poprawienie kodu, z góry dzięki

Ponawiam pytanie, przeczytałeś podlinkowany kod?

1

@AnyKtokolwiek:

Tablice i generics to zła mieszanka, nie tylko na początek nauki.

public class Animal {}
public class Cat extends Animal {}
        Cat[] cats = new Cat[7];
        Animal[] animals = cats;

        cats[0] = new Cat();
        animals[1] = new Animal();

Albo zwrócimy uwagę, że SonarLint pokazuje warning: (tylko żółty warning a nie czerwony error!)
Storing element of type 'com.myapp.Animal' to array of 'com.myapp.Cat' elements will produce 'ArrayStoreException'

Albo dopiero błąd poleci dopiero przy uruchomieniu, bo

    Animal[] animals = cats;
    animals[1] = new Animal();

skompiluje się bezbłędnie.

A List (jak bóg James Gosling/Brian Goetz przykazał)
(zakomentowana linia) nie skompiluje się

        List<Cat> catList = new ArrayList<>();
//        List<Animal> animalList = catList;

Sonar: Incompatible types. Found: 'java.util.List<com.myapp.Cat>', required: 'java.util.List<com.myapp.Animal>'

0

Kolejny dzień nad kodem ;), starałem się zgłebić problem z którym sobie nie radziłem, Lekko wykończony jestem generykami.
Proszę panowie jeśli możecie spojrzec w kod i doradzić w kwesti rozwiązania zadania


public class DynamicArray2<E> {
        
        private E[] theData;
        private int size = 0;
        private int capacity = 0;
        
        private static final int INIT_CAPACITY = 10;

       
        public DynamicArray2() {
            this(INIT_CAPACITY);
        }

        public DynamicArray2(int initCapacity) {
            capacity = initCapacity;
            theData = (E[]) new Object[capacity];
        }
        
        public boolean add(E e) {
            if(e == null) {
                throw new NullPointerException();
            }

            if(size == capacity) {
                reallocate();
            }

            theData[size] = e;
            size++;

            return true;
        } 

        public void add(int index, E e) {
            if(index < 0 || index > size) {
                throw new ArrayIndexOutOfBoundsException(index);
            }

            if(e == null) {
                throw new NullPointerException();
            }

            if(size == capacity) {
                reallocate();
            }

            for(int i = size; i > index; i--) {
                theData[i] = theData[i - 1];
            }

            theData[index] = e;
            size++;
        }

        public void clear() {
            theData = (E[]) new Object[capacity];
            size = 0;
        } 

        public boolean equals(Object o) {
            if(o == null) {
                return false;
            }

            if(getClass() != o.getClass()) {
                return false;
            }

            DynamicArray2<E> otherO = (DynamicArray2<E>) o;

            if(size != otherO.size) {
                return false;
            }

            for(int i = 0; i < size; i++) {
                if(!theData[i].equals(otherO.theData[i])) {
                    return false;
                }
            }

            return true;
        } 

        public E get(int index) {
            if(index < 0 || index >= size) {
                throw new ArrayIndexOutOfBoundsException(index);
            }
            return theData[index];
        }

        public int indexOf(Object o) {
            if(o == null) {
                throw new NullPointerException();
            }

            for(int i = 0; i < size; i++) {
                if(theData[i].equals(o)) {
                    return i;
                }
            }

            return -1;
        } 

        public boolean isEmpty() {
            return size == 0;
        } // End isEmpty() method

        public E remove(int index) {
            if(index < 0 || index >= size) {
                throw new ArrayIndexOutOfBoundsException(index);
            }

            E temp = theData[index];

            for(int i = index + 1; i < size; i++) {
                theData[i - 1] = theData[i];
            }

            size--;
            return temp;
        } 

        public boolean remove(Object o) {
            int indexOfO = indexOf(o);

            if(indexOfO == -1) {
                return false;
            }

            remove(indexOfO);
            return true;
        } 

        public E set(int index, E e) {
            if(index < 0 || index >= size) {
                throw new ArrayIndexOutOfBoundsException(index);
            }

            if(e == null) {
                throw new NullPointerException();
            }
            E temp = theData[index];
            theData[index] = e;
            return temp;
        } 

        public int size() {
            return size;
        } 

        private void reallocate() {
            capacity *= 2;
            E[] newArraylist = (E[]) new Object[capacity];

            for(int i = 0; i < size; i++) {
                newArraylist[i] = theData[i];
            }

            theData = newArraylist;
        } 
    }
2

@MaxMann: szczerze? Olałbym robienie generycznych tablic, dane trzymałbym w tablicy obiektów

Object[] data; 

a rzutowania miał w metodach. Tak to nawet jest zaimplementowane:


    // Positional Access Operations

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

Niestety w kwesti tabilc i gneryków Java ssie

1

Masz rację @Aleksander32, sam przemyślałem ten temat i wnioski nasuwają się same, ale z drugiej strony nie chcę olewać tematu generyków.
Dzięki za odzew i poświęcony czas Panowie, muszę jednak dalej przysiąść nad tym zadaniem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1