2014-05-05 14 views
5

Ich brauche eine Java-Datenstruktur, die ich effizient hinzufügen, löschen und auf ein zufälliges Objekt zugreifen kann.Java-Datenstruktur, die effizient hinzufügen, löschen und zufällig

Dies ist, was nicht funktioniert:

Arraylist effiziente add (konstante Zeit) und Random Access (nur „get“ mit einer Zufallszahl), aber Löschungen lineare Zeit in Anspruch nehmen können, weil es möglicherweise muss Suche die ganze Liste danach.

TreeSet oder HashSet hat effiziente hinzufügen und löschen, aber ich kann nicht herausfinden, wie man ein zufälliges Objekt erhalten.

Irgendwelche Ideen?

In der Theorie würde ein B-Baum funktionieren, wenn ich den Baum selbst mit zufälligen Lefts oder Rechten durchqueren könnte, aber ich glaube nicht, dass eine Standard-Java-Klasse mir diese Fähigkeit gibt.

Ich bin bereit, eine Drittanbieter-Bibliothek zu verwenden, wenn nichts in den Standard-Java-Klassen funktionieren wird.

Ich brauche weder Duplikate oder Nullen, noch muss es threadsicher sein.

Danke.

+0

'ArrayList.remove (int index)' läuft in der amortisierten konstanten Zeit. Soweit ich das beurteilen kann, bedeutet dies, dass einzelne Anrufe lineare Zeit sind, aber die durchschnittliche Zeit über eine Reihe von Anrufen nähert sich konstanter Zeit. – Aarowaim

+0

Danke für das Aarowaim, aber ich würde nicht den Index des Objekts zu entfernen wissen. Ich nehme an, ich könnte diese Informationen separat in einer HashMap oder ähnlichem speichern, aber sobald ich ein Objekt aus der ArrayList entfernte, würden die anderen Objekte in der Liste die Indizes ändern, was bedeuten würde, dass einige der Werte in der HashMap falsch wären. und es würde mich linear brauchen, sie zu reparieren. – Magmatic

+0

@Magmatic Müssen Sie doppelte Elemente zulassen? – Boann

Antwort

4

Sie können dies mit einem ArrayList/HashMap-Paar erhalten (wobei die Karte den Index der Objekte in der Liste speichert), wenn Sie die Listenentfernung ungeordnet vornehmen. Beim Entfernen verschieben Sie nicht alle nachfolgenden Elemente in der Liste um eine Position nach unten, sondern verschieben einfach das letzte Element, um das Feld des entfernten Elements zu füllen. Dann gibt es höchstens zwei verschiedene Elemente, die beim Entfernen berührt werden müssen. Ein kurzes Beispiel (die ich nicht getestet haben, und hoffe, dass ich bungle nicht):

class SpecialSet<E> extends AbstractSet<E> implements Set<E> { 
    private final List<E> list = new ArrayList<>(); 
    private final Map<E,Integer> indexMap = new HashMap<>(); 

    @Override 
    public boolean add(E e) { 
     if (contains(e)) return false; 
     indexMap.put(e, list.size()); 
     list.add(e); 
     return true; 
    } 

    @Override 
    public boolean remove(Object o) { 
     Integer indexBoxed = indexMap.remove(o); 
     if (indexBoxed == null) return false; 
     int index = indexBoxed; 
     int last = list.size() - 1; 
     E element = list.remove(last); 
     if (index != last) { 
      indexMap.put(element, index); 
      list.set(index, element); 
     } 
     return true; 
    } 

    public E removeRandom() { 
     E element = list.get((int)(Math.random() * size())); 
     remove(element); 
     return element; 
    } 

    @Override 
    public boolean contains(Object o) { 
     return indexMap.containsKey(o); 
    } 

    @Override 
    public Iterator<E> iterator() { 
     return list.iterator(); 
    } 

    @Override 
    public int size() { 
     return list.size(); 
    } 
} 

Je nachdem, welche Objektgleichheit Testverhalten Sie möchten, könnten Sie vielleicht die HashMap für eine IdentityHashMap für ein wenig besser auslagern Geschwindigkeit.

+0

Wow, danke für den Code! Ich habe es einfach in mein Projekt geworfen. (Ich habe nur "removeRandom" in "getRandom" geändert und die Zeile entfernt, die es entfernt). Es hat perfekt funktioniert! Ich hatte eine ArrayList mit der ineffizienten Entfernung verwendet, aber als ich zu diesem Code wechselte, fiel die Verarbeitungszeit von 1900 ms auf 628 ms. Wow wow wow! Danke danke danke! – Magmatic

+0

Ich sehe hier nicht die Bedeutung von 'HashMap'. Der wichtigste Trick ist hier: Entfernen entfernt das Element nicht direkt, es entfernt das letzte Element und legt das letzte Element an die Position, die entfernt werden muss. In diesem Fall hilft nur eine einzige 'ArrayList' als interne Struktur. –

+0

@AdrianShum Sie müssen ein Element finden, bevor Sie es entfernen können; Das ist der Teil, den die HashMap schnell macht. – Boann

0

Ich würde vorschlagen, dass Sie eine Karte mit einem Integer-Schlüssel verwenden. Auf diese Weise können Sie eine Zufallszahl für das Entfernen generieren.

Problem für den Leser übrig: bei nachfolgenden Entfernen (oder jeder Operation) wird es Lücken geben.

+0

Aber wie können Sie einen Wert entfernen, wenn Sie seinen Schlüssel nicht kennen? Sie müssen immer noch eine ineffiziente lineare Suche durchführen, um sie zu finden. – Magmatic

1

Beachten Sie, dass ein einfaches Array die Aufgabe erledigt, wenn Sie die maximale Größe vordefinieren, die Sie benötigen. Behalten Sie einen "oberen" Indexwert und fügen Sie ein, indem Sie "top" inkrementieren und in diesem Array-Element speichern. Wenn Sie löschen, kopieren Sie den Wert "top" in das gelöschte Element und dekrementieren Sie "top".

Wenn Sie Ihre maximale Größe nicht kennen, können Sie immer noch ein Array von Arrays zuweisen und denselben Basisalgorithmus verwenden. Sie müssen jedoch zuerst die Indexwerte des Haupt- und Nebenarrays berechnen, bevor Sie auf ein Element zugreifen.

Verwandte Themen