Przekroczenie zakresu tablicy

0

Witam,

tworzę program w Javie w środowisku NetBeans. Mam dwa wektory, jeden globalny, drugi lokalny. Wywołuję funkcję sortowania (jako identyfikator podaję jej pierwszy element z globalnego wektora), która szuka identyfikatorów w tym globalnym wektorze, spełniających pewne kryteria. Jak już ich znajdzie, dodaje ich numery do lokalnego wektora. Na końcu, przeszukuje wszystkie elementy dodane do lokalnego wektora, wywołując rekurencyjnie znów funkcję sortowania, kolejno dla elementów lokalnego wektora. Niestety, wystepuje odwołanie do nieistniejącego elementu.

Tak w uproszczeniu wygląda funkcja sortowania:

	public void sortowanie(int identyfikator, int argument1, int argument2)
	{
		try
		{
			//tu tworzę wewnętrzny wektor
			final Vector<Integer> mojWektor = new Vector<Integer>();

			//Tutaj jest dużo linijek kodu, w których wyszukuję innych identyfikatorów
			//i dodaję je do mojWektor.

			if (mojWektor.size() > 0)
			{
				for (int i = 0; i < mojWektor.size(); i++)
				{ sortowanie(mojWektor.get(i),0,0); }
			}
		}
		catch (Exception e)
		{
			JOptionPane.showMessageDialog(null,
					"Wyjątek: " + e,
					"Błąd funkcji",
					JOptionPane.ERROR_MESSAGE);
		}
	}

I błąd, który wyrzuca "catch":

Exception: java.lang.ArrayIndexOutOfBoundsException: -1

Jednak dodałem parę System.out.println(mojWektor.size() + " " + i) i wygląda mi z nich tuż przed błędem:

mojWektor.size() = 9
i = 6
mojWektor.get(i) = 4

to oznacza, że wcale nie chciałem się odwołać do nieistniejącego elementu. Na co zatem innego może wskazywać ten błąd z "catch"?

Całe sortowanie znajduje się w klasie:
public class MojaBazaDanych extends Vector

Pomyślałem, że to może chodzi o zasięg tego wektora lokalnego (że wcale nie jest on taki, jak tego chciałem) i jakiś słów kluczowych może brakuje lub są użyte niewłaściwe, odnośnie zakresu zmiennej. Proszę o sugestie.

Pozdrawiam!

1
  1. Debugger twoim przyjacielem. Znalazł byś powód błędu w ciągu 30 sek...
  2. Przecież w stacktrace masz informacje o tym w której linijce poleciał ci wyjątek...
  3. Zapewne błąd jest w tych linijkach ktorych nie pokazałeś...
0

Dzięki!

Jak użyć tego Stack Trace'a? Tutaj (http://wiki.netbeans.org/AnalyzeStackTrace) znajduję informację, że jest to w Window => Other => Analyze Stacktrace. Włączam go, ale w momencie błędu nie wyświetla żadnych informacji.

Bardziej mnie interesuje stan zmiennych tuż przed błędem (choć i konkretna linijka, która powoduje błąd też), jednak być może z powodu zasięgu nie widzę żadnej zawartości tego lokalnego wektora. A to co widzę dzięki error output wskazuje, że zakres nie został przekroczony.

Mam ciągle tylko w message dialog:

Exception: java.lang.ArrayIndexOutOfBoundsException: -1

i żadnych błędów w zakładce "Output", poza tym, co wrzucam samemu do testowania zmiennych na standard error (poprzez System.out.println).

Pozdrawiam!

1

obsłuż porządnie wyjątek. Z tego co kojarze to nie powinno się rzucać Exception w bloku catch.
Może ArrayIndexOutOfBoundsException byłoby lepsze

1

Obsłuż porządnie ..., tzn. chwilowo zrób tak:

                catch (Exception e)
                {
                        e.printStackTrace();
                        JOptionPane.showMessageDialog(null,
                                        "Wyjątek: " + e,
                                        "Błąd funkcji",
                                        JOptionPane.ERROR_MESSAGE);
                }
0

Dzięki!

Jestem w trakcie debuggowania i taka mnie wątpliwość naszła. Otóż miałem warunek

if (PierwszaZmienna == DrugaZmienna)

jednak IDE zasugerowało mi jego zmianę na:

if (PierwszaZmienna == null ? DrugaZmienna == null : PierwszaZmienna.equals(DrugaZmienna))

gdzie obie zmienne są typu String. Czy IDE słusznie zasugerował zmianę? (Czy logika warunku jest taka sama)?

Pozdrawiam!

2

Operator == porównuje zawartość tylko dla typów prymitywnych, tzn int, char, double, float, short. Dla wszystkiego co dziedziczy po Object (i nie podlega autoboxingowi) operator == porównuje referencje, tzn sprawdza czy referencje wskazują na ten sam obszar pamięci, nie porównując zawartości tego obszaru.

Widać, że masz nikłą wiedzę z Javy, proponuję zaopatrzyć się w jakąś książkę do Javy.

1

ale odpowiadając na pytanie johnyjj2-to zdaje się, że źle Ci IDE podpowiedziało. Teoria tu: http://pl.wikipedia.org/wiki/Operator_warunkowy czyli on zmienił twój warunek na:
if (PierwszaZmienna == null{
DrugaZmienna == null}
else{
PierwszaZmienna.equals(DrugaZmienna)
}
Trochę bez sensu.

Jak wibowit pisze: Stringi porównuje się metodą equals, jeśli porównasz operatorem == to porównasz tylko, czy te zmienne są trzymane w tym samym miejscu w pamięci.
Twój warunek to:
if (PierwszaZmienna.equals(DrugaZmienna))

2

Jeżeli już to warunek raczej powinien wyglądać tak:

if (PierwszaZmienna == null || DrugaZmienna == null ? false : PierwszaZmienna.equals(DrugaZmienna))

EDIT: Warunek podpowiedziany przez IDE jest poprawny, jeżeli chcesz aby blok w if się wykonał gdy oba stringi są null, ale myślę, że o to raczej Tobie nie chodziło.

0

Witam,

zmieniłem deklarację myVector:

public class NazwaMojejKlasy extends Vector {

    private Vector vector; //pierwszy wektor
    private Vector<Integer> myVector; //drugi wektor; wcześniej nie deklarowałem go tutaj

    public FamilyDatabase() {
        vector = new Vector();
        myVector = new Vector(); //tu go tworzę; wcześniej tworzyłem go w funkcji, nie konstruktorze, jako final Vector<Integer> myVector = new Vector<Integer>();
    }

oraz sposób odnoszenia się do niego:

myVector.addElement(new Integer(i)); //wcześniej: myVector.add(i);
//...
int zmiennaInt =  myVector.elementAt(i); //wcześniej: int actualID = myVector.get(i);

Wcześniej działało "poprawnie" (to znaczy, dodawało elementy w sposób taki, jakiego się można było spodziewać). Zdecydowałem się jednak, aby nie był tworzony przy każdym wywołaniu funkcji rekurencyjnej nowy wektor, ale aby był jeden, globalny wektor. Niestety, teraz dodaje elementy za pierwszym i drugim razem, później zaś wcale nie dodaje elementów. Innych fragmentów kodu nie zmieniałem, więc błąd się powinien kryć w którejś z powyższych.

Pozdrawiam!

0

Witam,

zmieniłem kod w następujący sposób

deklaracja
private List<Integer> values = new ArrayList<>();

dodanie wartości
values.add(Integer.valueOf(random.nextInt(100)));

zwrócenie wartości
int value = values.get(index);

jednak linijka

values = new ArrayList<>();

powoduje błąd:

diamond operator is not supported in -source 1.6
(use -source 7 or higher to enable diamond operator)

cannot infer type arguments for java.util.ArrayList<>;
reason: no instance(s) of type variable(s) E exist so that java.util.ArrayList<E> conforms to java.util.Vecotr<java.lang.Integer>

W jaki sposób mogę uniknąć używania "diamond operator"?

Pozdrawiam!

2

Napisz po staremu, czyli List<Integer> lista = new ArrayList<Integer>(); Z reguły musisz dawać identyczną parametryzację z obu stron przypisania (no chyba, że parametr jest kowariantny albo kontrawariantny, czyli ? extends Coś, albo ? super Coś, ale to jest rzadko używane i możesz to na początku olać).
Diamond operator to po prostu częściowa inferencja typu, po to aby nie musieć explicite parametryzować klas po obu stronach równości. Jest dostępne od Javy 7, a ty piszesz pod Javę 6 (choć akurat ten ficzer powinien latać pod wersjami niższymi niż 7, bo nie zmienia wynikowego kodu przecież).

1

Nowe wersje Javy działają wstecz ze starym kodem. Wcześniej pisali List arr = new ArrayList(); i było ok.
(Jednakże ja bym tego nie stosował bo w pewnych sytuacjach jest to niebezpieczne)

0

Dzięki za odpowiedzi,

zamieniłem dotychczasowe

import java.awt.*;
import java.util.*;

public class NazwaMojejKlasy extends Vector {

    private Vector<Integer> mojWektor;

    public NazwaMojejKlasy() {
        mojWektor = new ArrayList<>();

na taki kod

import java.awt.*;
import java.util.*;

public class NazwaMojejKlasy extends Vector {

    private List<Integer> mojWektor;

    public NazwaMojejKlasy() {
        mojWektor = new ArrayList<Integer>();

i ta linijka

private List<Integer> myVector;

powoduje błąd

reference to List is ambiguous, both interface java.util.List in java.util and class java.awt.List in java.awt match

Korzystam zarówno z java.awt, jak i java.util w swoim kodzie, więc pewnie w deklaracji jakoś muszę zaznaczyć, o które List mi chodzi, tylko jak?

Pozdrawiam!

1

Ja stosuję zasadę, w której nie korzystam z wildcardów przy importach. W NetBeansie mam Crtl+Shift+I do uzupełniania importów (Eclipse też ma jakiś tam skrót). Jeśli jednak tak bardzo chcesz importować masowo, to użyj zapisu java.util.List<Integer> myVector;

0

Dzięki!

To teraz stosuję taki kod:

import java.awt.*;
import java.util.*;

public class NazwaMojejKlasy extends Vector {

    private java.util.List<Integer> mojWektor;

    public NazwaMojejKlasy() {
        mojWektor = new ArrayList<Integer>();
        //...
    }

    public void JakasFunkcja() {
        mojWektor.addElement(new Integer(i));
        int pobranaWartosc = mojWector.get(i);

i linijka

mojWektor.addElement(new Integer(i));

powoduje błąd

cannot find symbol
symbol: method addElement(java.lang.Integer)
location: variable mojWektor of type java.util.List<java.lang.Integer>

Szukam tutaj (http://docs.oracle.com/javase/6/docs/api/java/util/List.html) i znajduję:

void add(int index, E element)

Zmienilem addElement na add i nie wykrywa błędów kompilacji. Zaraz się przekonam, czy wykonuje się poprawnie.

Pozdrawiam!

0

Dzięki!

Teraz debugguję kod i przy wystąpieniach

mojWektor.add(new Integer(i));

mojWektor.get(i)

w pasku stanu otrzymuję błąd:

Thread AWT-EventQueue-0 stopped at NazwaMojejKlasy.java

oraz w zakładce debugging:

'AWT-EventQueue-0' at field breakpoint NazwaMojejKlasy.mojWektor

Za chwilę dokładniej się przyjrzę temu, choć na razie nie mam pomysłu, co dokładnie ten błąd powoduje.

Pozdrawiam!

1

Nie rzuca żadnego wyjątku? Może odwołujesz się do nieistniejącego indeksu i.
Swoją drogą, możesz pisać:

mojWektor.add(i);
// zamiast
mojWektor.add(new Integer(i));

Autoboxing sam zadziała.

0

Dzięki!

Właściwie to pierwszy raz zatrzymuje się debugger na samym :

mojWektor = new ArrayList<Integer>();

z informacją:

'main' at field breakpoint NazwaMojejKlasy.mojWektor
NazwaMojejKlasy.<init>:18
NazwaGlownegoPliku.<init>:38
NazwaGlownegoPliku.main:69

ponieważ w NazwaGlownegoPliku.java w "public static void main(String[] args) {" wywołuję "new NazwaGlownegoPliku();", to zaś uruchamia konstruktor "public NazwaGlownegoPliku() {", a w nim jest "nazwaGlownejZmiennejZBaza = new NazwaMojejKlasy();", to zaś w pliku NazwaMojejKlasy.java wywołuje "public NazwaMojejKlasy() {", a w nim "mojWektor = new ArrayList<Integer>();". (Nie zaznaczyłem powyższych fragmentów w osobnych tagach code, bo odpowiedź byłaby bardzo długa).

Pozdrawiam!

1

Ale powinna być też tam informacja, jaki wyjątek został rzucony. Najlepiej pokaż cały stacktrace.

0

Dzięki!

Pisząc o "stack trace" masz na myśli okienko "Debugging"?

Pojawia się tam

'AWT-Windows' running
'main' at field breakpoint NazwaMojejKlasy.mojWektor
NazwaMojejKlasy.<init>:18
GlownyPlik.<init>:38
GlownyPlik.main:69

Osiemnasta linijka w pliku z klasą to:

mojWektor = new ArrayList<Integer>();

Trzydziesta ósma w głównym pliku

bazadanych = new NazwaMojejKlasy();

Sześćdziesiąta dziewiąta w głównym pliku:

new GlownyPlik();

ale o tym (ze szczegółami) pisałem we wcześniejszym poście.

Pozdrawiam!

1

W takim razie musiałeś sobie po prostu ustawić breakpoint na polu mojVector.

0

Rzeczywiście :p

Nie wiem, gdzie mam zobaczyć ten "stack trace" (co najwyżej w trakcie wykonywania kodu podglądam sobie zmienne najeżdżając na fragmenty kodu albo korzystam z outputa).

Błąd w outpucie jest taki:

Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError

i jest on związany z wywołaniem rekurencyjnym funkcji, w której się znajdują odwołania do Listy.

Pozdrawiam!

Dodano: 2011-11-26 19:09

Przyglądam się dalej kodowi w trybie debuggowania i widzę, że ta moja funkcja jest wiele razy rekurencyjnie wywoływana, ale dopiero na samym końcu wyrzuca błąd, przy ostatnim wywołaniu, powodując błąd w outpucie, jak to wyżej napisałem.

Jak mogę ustawić w debuggerze, aby dopiero przy wyrzuceniu tego błędu zatrzymał mi wykonanie? Chciałbym znać wartości parametrów przekazywanych funkcji, ale wtedy już niczego nie widzę, gdy na nie najeżdżam w kodzie.

1

Hmm, breakpoint na konstruktor SOE? :D Głowy nie dam za to.
Możesz zrobić coś a'la:

    void run() {
        try {
            run();
        } catch (StackOverflowError ex) {
            // tutaj breakpoint
        }
    }

SOE nie jest rzucany przy ostatnim wywołaniu rekurencyjnym (czasem może takiego w ogóle nie być, gdy ma się nieskończoną rekurencję) tylko wtedy, gdy nastąpi przepełnienie stosu (jak sama nazwa błędu mówi). A więc jeśli na stosie jest miejsce na tysiąc zagnieżdżonych wywołań funkcji, a twój algorytm chce zrobić dwa tysiące, to po zrobieniu tysiąca dostaniesz SOE.

0

Witam,

właściwie to teraz wracam do podobnego problemu, dla którego założyłem ten temat, czyli zasięgu zmiennych dla wektorów/list.

GlownyPlik.java:

public class GlownyPlik extends Frame implements WindowListener, ActionListener {
   //...
   private java.util.List<Integer> rozpatrzone;
 
//i później, gdzieś dużo dalej w kodzie
 
   database.mojaFunkcja(identyfikator, arg1, arg2, rozpatrzone);

DrugiPlik.java

        try {
            //nie przeszukuj już przeszukanych danych
//            if (rozpatrzone.isEmpty() == false)
//            {
                if (rozpatrzone.contains(identyfikator))
                { return; }
//            }
            rozpatrzone.add(identyfikator);
 
//...
 
   mojaFunkcja(actualID, actualCol, actualRow, rozpatrzone);

Błąd wykonania:

Błąd funkcji. Wyjątek: java.lang.NullPointerException

dla linijki

if (rozpatrzone.contains(identyfikator))

Ja to rozumiem tak, że nie widzi

private java.util.List<Integer> rozpatrzone;

zadeklarowanego w GlownyPlik.java.

Próbowałem sprawdzać, czy "rozpatrzone.isEmpty() == false", a także zamienić "private" na "public", co niestety nie pomogło.

Pozdrawiam!

1

Źle rozumiesz, jeżeli ten kod

(rozpatrzone.contains(identyfikator))

rzuca NPE, to albo rozpatrzone, albo identyfikator jest nullem.

1

A zainicjowałeś zmienną rozpatrzone? Domyślna wartość zmiennej referencyjnej (tzn wskazującej na obiekt, a nie na prymityw) to null.

0

Dzięki za odpowiedzi,

w kodzie GlownyPlik.java mam taki fragment:

        //dodaj pierwszą wartość do 'rozpatrzone'
        rozpatrzone.add(0); //linijka 68; znajduje się pod koniec konstruktora public GlownyPlik()

który powoduje błąd:

Exception in thread "main" java.lang.NullPointerException
at GlownyPlik.<init>(GlownyPlik.java:68)
at GlownyPlik.main(GlownyPlik.java:75)

Gdzie linijka 75 to:

    public static void main(String[] args) {
        new GlownyPlik(); //linijka 75
    }

Pozdrawiam!

1

Przed "rozpatrzone.add(0)" dodaj "System.out.println("rozpatrzone = " + rozpatrzone)" i wklej to co zostało wypisane.

Kup sobie książkę omawiającą podstawy Javy i poczytaj zamiast co chwilę zadawać banalne pytania. To będzie szybsze i tańsze (bo czas to pieniądz).

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