Is there a fixed sized queue which removes excessive elements? - java

I need a queue with a fixed size. When I add an element and the queue is full, it should automatically remove the oldest element.
Is there an existing implementation for this in Java?

Actually the LinkedHashMap does exactly what you want. You need to override the removeEldestEntry method.
Example for a queue with max 10 elements:
queue = new LinkedHashMap<Integer, String>()
{
#Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
If the "removeEldestEntry" returns true, the eldest entry is removed from the map.

Yes, Two
From my own duplicate question with this correct answer, I learned of two:
EvictingQueue in Google Guava
CircularFifoQueue in Apache Commons
I made productive use of the Guava EvictingQueue, worked well.
To instantiate an EvictingQueue call the static factory method create and specify your maximum size.
EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ; // Set maximum size to 100.

I just implemented a fixed size queue this way:
public class LimitedSizeQueue<K> extends ArrayList<K> {
private int maxSize;
public LimitedSizeQueue(int size){
this.maxSize = size;
}
public boolean add(K k){
boolean r = super.add(k);
if (size() > maxSize){
removeRange(0, size() - maxSize);
}
return r;
}
public K getYoungest() {
return get(size() - 1);
}
public K getOldest() {
return get(0);
}
}

There is no existing implementation in the Java Language and Runtime. All Queues extend AbstractQueue, and its doc clearly states that adding an element to a full queue always ends with an exception. It would be best ( and quite simple ) to wrap a Queue into a class of your own for having the functionality you need.
Once again, because all queues are children of AbstractQueue, simply use that as your internal data type and you should have a flexible implementation running in virtually no time :-)
UPDATE:
As outlined below, there are two open implementations available (this answer is quite old, folks!), see this answer for details.

This is what I did with Queue wrapped with LinkedList, It is fixed sized which I give in here is 2;
public static Queue<String> pageQueue;
pageQueue = new LinkedList<String>(){
private static final long serialVersionUID = -6707803882461262867L;
public boolean add(String object) {
boolean result;
if(this.size() < 2)
result = super.add(object);
else
{
super.removeFirst();
result = super.add(object);
}
return result;
}
};
....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....

public class CircularQueue<E> extends LinkedList<E> {
private final int capacity;
public CircularQueue(int capacity){
this.capacity = capacity;
}
#Override
public boolean add(E e) {
if(size() >= capacity)
removeFirst();
return super.add(e);
}
}
Usage and test result:
public static void main(String[] args) {
CircularQueue<String> queue = new CircularQueue<>(3);
queue.add("a");
queue.add("b");
queue.add("c");
System.out.println(queue.toString()); //[a, b, c]
String first = queue.pollFirst(); //a
System.out.println(queue.toString()); //[b,c]
queue.add("d");
queue.add("e");
queue.add("f");
System.out.println(queue.toString()); //[d, e, f]
}

I think what you're describing is a circular queue. Here is an example and here is a better one

This class does the job using composition instead of inheritance (other answers here) which removes the possibility of certain side-effects (as covered by Josh Bloch in Essential Java). Trimming of the underlying LinkedList occurs on the methods add,addAll and offer.
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class LimitedQueue<T> implements Queue<T>, Iterable<T> {
private final int limit;
private final LinkedList<T> list = new LinkedList<T>();
public LimitedQueue(int limit) {
this.limit = limit;
}
private boolean trim() {
boolean changed = list.size() > limit;
while (list.size() > limit) {
list.remove();
}
return changed;
}
#Override
public boolean add(T o) {
boolean changed = list.add(o);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public int size() {
return list.size();
}
#Override
public boolean isEmpty() {
return list.isEmpty();
}
#Override
public boolean contains(Object o) {
return list.contains(o);
}
#Override
public Iterator<T> iterator() {
return list.iterator();
}
#Override
public Object[] toArray() {
return list.toArray();
}
#Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
#Override
public boolean remove(Object o) {
return list.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends T> c) {
boolean changed = list.addAll(c);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
#Override
public void clear() {
list.clear();
}
#Override
public boolean offer(T e) {
boolean changed = list.offer(e);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public T remove() {
return list.remove();
}
#Override
public T poll() {
return list.poll();
}
#Override
public T element() {
return list.element();
}
#Override
public T peek() {
return list.peek();
}
}

Sounds like an ordinary List where the add method contains an extra snippet which truncates the list if it gets too long.
If that is too simple, then you probably need to edit your problem description.

Also see this SO question, or ArrayBlockingQueue (be careful about blocking, this might be unwanted in your case).

It is not quite clear what requirements you have that led you to ask this question. If you need a fixed size data structure, you might also want to look at different caching policies. However, since you have a queue, my best guess is that you're looking for some type of router functionality. In that case, I would go with a ring buffer: an array that has a first and last index. Whenever an element is added, you just increment the last element index, and when an element is removed, increment the first element index. In both cases, addition is performed modulo the array size, and make sure to increment the other index when needed, that is, when the queue is full or empty.
Also, if it is a router-type application, you might also want to experiment with an algorithm such as Random Early Dropping (RED), which drops elements from the queue randomly even before it gets filled up. In some cases, RED has been found to have better overall performance than the simple method of allowing the queue to fill up before dropping.

Actually you can write your own impl based on LinkedList, it is quite straight forward, just override the add method and do the staff.

I think the best matching answer is from this other question.
Apache commons collections 4 has a CircularFifoQueue which is what you are looking for. Quoting the javadoc:
CircularFifoQueue is a first-in first-out queue with a fixed size that replaces its oldest element if full.

A Simple solution, below is a Queue of "String"
LinkedHashMap<Integer, String> queue;
int queueKeysCounter;
queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;
Note that this will not maintain the Order of the items in the Queue, but it will replace the oldest entry.

As it's advised in OOPs that we should prefer Composition over Inheritance
Here my solution keeping that in mind.
package com.choiceview;
import java.util.ArrayDeque;
class Ideone {
public static void main(String[] args) {
LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
q.add(1);
q.add(2);
q.add(3);
System.out.println(q);
q.add(4);
// First entry ie 1 got pushed out
System.out.println(q);
}
}
class LimitedArrayDeque<T> {
private int maxSize;
private ArrayDeque<T> queue;
private LimitedArrayDeque() {
}
public LimitedArrayDeque(int maxSize) {
this.maxSize = maxSize;
queue = new ArrayDeque<T>(maxSize);
}
public void add(T t) {
if (queue.size() == maxSize) {
queue.removeFirst();
}
queue.add(t);
}
public boolean remove(T t) {
return queue.remove(t);
}
public boolean contains(T t) {
return queue.contains(t);
}
#Override
public String toString() {
return queue.toString();
}
}

Ok, I'll throw out my version too. :-) This is build to be very performant - for when that matters. It's not based on LinkedList - and is thread safe (should be at least). FIFO
static class FixedSizeCircularReference<T> {
T[] entries
FixedSizeCircularReference(int size) {
this.entries = new Object[size] as T[]
this.size = size
}
int cur = 0
int size
synchronized void add(T entry) {
entries[cur++] = entry
if (cur >= size) {
cur = 0
}
}
List<T> asList() {
int c = cur
int s = size
T[] e = entries.collect() as T[]
List<T> list = new ArrayList<>()
int oldest = (c == s - 1) ? 0 : c
for (int i = 0; i < e.length; i++) {
def entry = e[oldest + i < s ? oldest + i : oldest + i - s]
if (entry) list.add(entry)
}
return list
}
}

Related

TreeSet CompareTo not giving desirable result

I am trying to create a set of all letters in all the words in a dictionary.
I am using a TreeSet for that as I have to do lot's of compare operations.
public class main {
public static void main(String[] args) {
Set<String> lines = new TreeSet<>();
lines.add("ba");
DictAwareSolver myGuesser = new DictAwareSolver(lines);
myGuesser.makeGuess();
}
}
This is my class which is operating on the set
package solver;
import sun.reflect.generics.tree.Tree;
import java.util.*;
import java.lang.System;
public class DictAwareSolver extends HangmanSolver
{
private Set<String> dict;
TreeSet<Node> myTree = new TreeSet<>();
//getters
public Set<String> getDict() {
return dict;
}
// methods
public DictAwareSolver(Set<String> dictionary) {
this.dict = dictionary;
// Implement me!
} // end of DictAwareSolver()
#Override
public void newGame(int[] wordLengths, int maxIncorrectGuesses)
{
// Implement me!
} // end of newGame()
#Override
public char makeGuess() {
Set<String> guessDict = getDict();
Iterator dictItr = guessDict.iterator();
while (dictItr.hasNext())
{
String word = (String) dictItr.next();
for (int i = 0; i<word.length(); i++)
{
Node temp = new Node(word.charAt(i));
myTree.add(temp);
}
}
Iterator treeItr = myTree.iterator();
while (treeItr.hasNext())
{
Node n = (Node) treeItr.next();
System.out.println(n.getLetter() + "-->"+n.getFrequency());
}
// TODO: This is a placeholder, replace with appropriate return value.
return '\0';
} // end of makeGuess()
#Override
public void guessFeedback(char c, Boolean bGuess, ArrayList< ArrayList<Integer> > lPositions)
{
// Implement me!
} // end of guessFeedback()
} // end of class DictAwareSolver
class Node implements Comparable<Node>{
private char letter;
private int frequency;
public Node(char letter)
{
this.letter = letter;
this.frequency = 1;
}
public void countIncrementer()
{
int newCount = getFrequency()+1;
setFrequency(newCount);
}
// getters
public char getLetter() {
return letter;
}
public int getFrequency() {
return frequency;
}
// setters
public void setFrequency(int frequency) {
this.frequency = frequency;
}
#Override
public int compareTo(Node o) {
if (getLetter() == o.letter)
{
o.countIncrementer();
return 0;
}
else if (getLetter() > o.getLetter())
{
return 1;
}
else
{
return -1;
}
}
}
When I am running this whatever I am adding 1st is giving a count of 2. As in this case output is
a-->1
b-->2
but I am expecting
a-->1
b-->1
It will be really helpful if you can point out what is the problem. From what I can think of it should be something in my o.countIncrementer(); in my compareTo method. I am new to java.
The code is making the assumption that the TreeSet will only call the comparator against an equal element if one already exists in the set, and if it does such a comparison, it will only do it exactly once. However, this is not how TreeSet is implemented. Looking at the API documentation for TreeSet, there are no guarantees as to how the comparisons will occur or with what frequency. Since this is not a documented part of the API, the authors of TreeSet are free to implement this functionality in any reasonable manner they wish, so long as it meets the documented API. In fact, they are also allowed to change how it's implemented between versions (e.g. Java 6 & Java 7), or between different implementations (e.g. Oracle vs. IBM).
In short, if the documentation does not guarantee a behavior, your code should not rely on that behavior.
To go into the specific behavior you're seeing, the first element added to a TreeSet (in the versions of Java you're using) is compared against itself. While this is perhaps surprising, it is not disallowed by the API. There may or may not be a good reason for this (I believe the check was added in Java 7 to force a NullPointerException to be thrown when a null is added as the first element to a TreeSet that disallows nulls per this bug). However, in the end, the reason for the check shouldn't matter to users of the API, since it's not disallowed in the API.
public static void main(String[] args) {
System.out.printf("Java vendor & version: %s %s\n", System.getProperty("java.vendor"), Runtime.version());
TreeSet<Character> set = new TreeSet<>(new LoggingComparator<>());
set.add('a');
}
private static class LoggingComparator<T extends Comparable<? super T>> implements Comparator<T> {
#Override
public int compare(T o1, T o2) {
System.out.println(o1 + " <=> " + o2);
return o1.compareTo(o2);
}
}
Java vendor & version: Oracle Corporation 11.0.4+10-LTS
a <=> a

Java - Add element to empty set (immutable)

I've been working on trying to implement an immutable set from scratch, so I'm not using HashSet or java.util.Set
I have this method in my Empty class to add an element to an empty set:
public Set<T> add(T x) {
return new Element<T>(x, new Empty<T>());
}
And in another class called Element, I have the following constructor:
public Element(T element, Empty<T> empty) {
assert(element != null);
assert(empty != null);
this.element = element;
this.set = empty;
}
EDIT: here is my other Element constructor used for adding an element to a set.
public Element(T x, Set<T> set) {
this.element = x;
this.set = set;
}
But when I try to add an element it fails and the set is still empty.
I've used a similar code when creating an immutable Binary Search Tree and it worked fine so I assumed that I could do the same but for an immutable Set.
I was just wondering if the problem was with my add method or my constructor
Thank you
The size method:
for the Empty class
/**
* returns number of elements in the set
* #return size - number of elements in the set
*/
public int size(){
return -1;
}
for the element class:
#Override
public int size() {
if (set.isEmpty() == true) {
return -1;
} else {
return set.toList().size();
}
}
the toList() method:
#Override
public List<T> toList() {
List<T> list = new ArrayList<T>();
int i;
for(i = 0; i < set.size(); i++){
list.set(i, element);
}
return list;
}
reading over this part I realise that the problem with returning the size may be from the toList method I wrote, but I don't think that should have an effect on adding an element to the set?
toString - Element Class:
#Override
public String toString() {
return "Set = [" + set + "]";
}
toString - Empty class:
public String toString() {
return "";
}
And the JUnit Test for Add:
EDIT: realised that the set was immutable and so tried to make a new set that was equal to the empty set with the added value - to store the change but kept getting the same NullPointerException error.
#Test
public final void testAdd() {
Set<Integer> set1;
set1 = set.add(1);
int i = 20;
set.add(i);
assertSame("Last element should be the newly added name object", i, set.toList().get(set.size()-1));
assertEquals("Set size should be two", 2, set.size());
}
The assertSame gives a NullPointerException (so I'm guessing this means that the add didn't work and the set is still empty); and if I comment it out to test the next line the assertEquals says that set.size() is -1 (empty)
Almost everything in your existing code is flawed. Your Element's ctor does not make sense, size() and toList() are implemented in weird way, even the unit test is flawed in basic Java.
Some pseudo code
interface Set<T> {
Set<T> add(T v);
int size();
boolean contains(T v);
}
class Element<T> extends Set<T> {
T value;
Set<T> next;
public Element<T>(T element, Set<T> next) {...}
public Set<T> add(T value) {
if contains(value) { // already in set
return this;
}
return new Element(value, this);
}
public int size() {
return next.size() + 1;
}
public boolean contains(T value) {
return (this.value.equals(value) || next.contains(value));
}
}
public class Empty<T> extends Set<T> {
public Set<T> add(T value) {
return new Element(value, this);
}
public int size() {
return 0; // come on! 0 means empty, not -1!
}
public boolean contains(T value) {
return false;
}
}
Having reviewed my code, I realised where I was making the mistake.
Aside from the messy code in other areas, the add(T x) method was not working as expected due to the fact that I did not import the Empty or Element class to the Demo set or the JUnit test and, as mentioned by #shmosel, my toString method wasn't working properly because I did not include the element field and so was not going to output anything.
The following is my Demo code showing how I've added the import line. Furthermore, the constructor for Element works fine too. Again, I just needed to import the class for an Empty set for it to work.
package immutable.set;
import immutable.set.Empty;
public class DemoSet {
public static void main(String[] args) {
Set<Integer> set, set1, set2;
set = new Empty<Integer>();
System.out.println(set.isEmpty());
set1 = set.add(1).add(2);
set2 = set.add(3);
System.out.println(set1.toString());
System.out.println(set1.isEmpty());
System.out.println(set2.toString());
System.out.println(set2.isEmpty());
}
}
And it does print out the expected outcome.
Thank you for you help everyone.

Limit size of an ObservableList

I'm forced to use an ObservableList in a JavaFx Application, and my List must contains a maximum of 1000 items.
So I was thinking of using a FIFO system, but I only see this solution :
observableListData.add(...) //1001 item
if (observableListData.size()>1000){
observableListData.remove(0); // 1000 item
}
I add a lot of items in this list, so I don't think this is the best way to limit the size of my ObservableList, is there another way to do it ?
EDIT :
To answer some of your question :
I need an ObservableList because it's link to a ListView.
I must use FIFO because I display items of this list
You can extend ModifiableObservableListBase, which is the base for collection, returned by FXCollections.observableList, in such a way:
public class ObservableLimitedList<T> extends ModifiableObservableListBase<T> {
private LinkedList<T> list;
private int maxSize;
public ObservableLimitedList(int maxSize) {
this.maxSize = maxSize;
list = new LinkedList<>();
}
#Override
public boolean add(T element) {
boolean result = super.add(element);
if (size() > maxSize) {
remove(0);
}
return result;
}
// delegate overrides:
#Override
public T get(int index) {
return list.get(index);
}
#Override
public int size() {
return list.size();
}
#Override
protected void doAdd(int index, T element) {
list.add(index, element);
}
#Override
protected T doSet(int index, T element) {
return list.set(index, element);
}
#Override
protected T doRemove(int index) {
return list.remove(index);
}
}
Here's the test for this implementation: https://ideone.com/MMhnS6
There is no way to tell the ObservableList itself to limit the size of it.
Here is the link to details of the class and all methodes:
https://docs.oracle.com/javase/8/javafx/api/javafx/collections/ObservableList.html
Can you please tell us what are you trying to make, and why do you need to limit the size? And maybe someone will tell you the better solution.
What about sub classing it and overriding the add method. Use your if statement inside the overridden method.

How to obtain index of a given LinkedHashSet element without iteration?

Is it even possible?
Say you have
private Set<String> names = new LinkedHashSet<String>();
and Strings are "Mike", "John", "Karen".
Is it possible to get "1" in return to "what's the index of "John" without iteration?
The following works fine .. with this question i wonder if there is a better way
for (String s : names) {
++i;
if (s.equals(someRandomInputString)) {
break;
}
}
The Set interface doesn't have something like as an indexOf() method. You'd really need to iterate over it or to use the List interface instead which offers an indexOf() method.
If you would like to, converting Set to List is pretty trivial, it should be a matter of passing the Set through the constructor of the List implementation. E.g.
List<String> nameList = new ArrayList<String>(nameSet);
// ...
I don't believe so, but you could create a LinkedHashSetWithIndex wrapper class that would do the iteration for you, or keep a separate table with the indexes of each entry, if the performance decrease is acceptable for your use case.
It is generally not possible for a Set to return the index because it's not necessarily well defined for the particular Set implementation. For example it says in the HashSet documentation
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So you shouldn't say the type is Set when what you actually expect is a Set implementing som order.
Here is an implementation that does insertions, removals, retainings, backed by an arraylist to achieve o(1) on get(index).
/**
* #Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> extends LinkedHashSet<E> {
private final ArrayList<E> list = new ArrayList<>();
public $IndexLinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public $IndexLinkedHashSet() {
super();
}
public $IndexLinkedHashSet(int initialCapacity) {
super(initialCapacity);
}
public $IndexLinkedHashSet(Collection<? extends E> c) {
super(c);
}
#Override
public synchronized boolean add(E e) {
if ( super.add(e) ) {
return list.add(e);
}
return false;
}
#Override
public synchronized boolean remove(Object o) {
if ( super.remove(o) ) {
return list.remove(o);
}
return false;
}
#Override
public synchronized void clear() {
super.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
#Override
public synchronized boolean removeAll(Collection<?> c) {
if ( super.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
#Override
public synchronized boolean retainAll(Collection<?> c) {
if ( super.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
/**
* Copied from super class
*/
#Override
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
To test it:
public static void main(String[] args) {
$IndexLinkedHashSet<String> abc = new $IndexLinkedHashSet<String>();
abc.add("8");
abc.add("8");
abc.add("8");
abc.add("2");
abc.add("3");
abc.add("4");
abc.add("1");
abc.add("5");
abc.add("8");
System.out.println("Size: " + abc.size());
int i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.remove("8");
abc.remove("5");
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.clear();
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
}
Which outputs:
Size: 6
8
2
3
4
1
5
Size: 4
2
3
4
1
Size: 0
Ofcourse remove, removeAll, retainAll now has the same or worse performance as ArrayList. But I do not use them and so I am ok with that.
Enjoy!
EDIT:
Here is another implementation, which does not extend LinkedHashSet because that's redundant. Instead it uses a HashSet and an ArrayList.
/**
* #Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> implements Set<E> {
private final ArrayList<E> list = new ArrayList<>( );
private final HashSet<E> set = new HashSet<> ( );
public synchronized boolean add(E e) {
if ( set.add(e) ) {
return list.add(e);
}
return false;
}
public synchronized boolean remove(Object o) {
if ( set.remove(o) ) {
return list.remove(o);
}
return false;
}
#Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public synchronized void clear() {
set.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
public synchronized boolean removeAll(Collection<?> c) {
if ( set.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
public synchronized boolean retainAll(Collection<?> c) {
if ( set.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
#Override
public synchronized int size() {
return set.size();
}
#Override
public synchronized boolean isEmpty() {
return set.isEmpty();
}
#Override
public synchronized boolean contains(Object o) {
return set.contains(o);
}
#Override
public synchronized Iterator<E> iterator() {
return list.iterator();
}
#Override
public synchronized Object[] toArray() {
return list.toArray();
}
#Override
public synchronized <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
Now you have two implementations, I would prefer the second one.
Although not as efficient for the machine, this achieves it in one line:
int index = new ArrayList<String>(names).indexOf("John");
A better way there is not, only a single lined one (which makes use of the iterator, too but implicitly):
new ArrayList(names).get(0)
You can convert your Set to List then you can do any indexing operations.
Example: need to crop Set list to 5 items.
Set<String> listAsLinkedHashSet = new LinkedHashSet<>();
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("2");
listAsLinkedHashSet.add("3");
listAsLinkedHashSet.add("4");
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("2");
listAsLinkedHashSet.add("5");
listAsLinkedHashSet.add("7");
listAsLinkedHashSet.add("9");
listAsLinkedHashSet.add("8");
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("10");
listAsLinkedHashSet.add("11");
List<String> listAsArrayList = new ArrayList<>(listAsLinkedHashSet);
//crop list to 5 elements
if (listAsArrayList.size() > 5) {
for (int i = 5; i < listAsArrayList.size(); i++) {
listAsArrayList.remove(i);
i--;
}
}
listAsLinkedHashSet.clear();
listAsLinkedHashSet.addAll(listAsArrayList);

Allocation free game

My code is basically allocation free, however the GC runs every 30 seconds or so when at 60fps. Checking the app with DDMS for allocation shows there is ALOT of SimpleListIterator being allocated. There is also some stuff being allocated because i use Exchanger.
The SimpleListIterator comes from for each loops for (T obj : objs) {}. I was under the impression that the compilator/translator would optimize those to not use iterators for types that support it (I basically only use ArrayList) but that seems to not be the case.
How can I avoid allocating all these SimpleListIterators? One solution would be to switch to regular for loops for (int i = 0; i < size; ++i) {} but I like for each loops :(
Another way would be to extend ArrayList which returns an Iterator that is only allocated once.
A third way I hacked together is using a static helper function which returns a Collection which is reusing an Iterator. I hacked something like this together but the casting feels very hackish and unsafe. It should be thread safe though as I use ThreadLocal? See below:
public class FastIterator {
private static ThreadLocal<Holder> holders = new ThreadLocal<Holder>();
public static <T> Iterable<T> get(ArrayList<T> list) {
Holder cont = holders.get();
if (cont == null) {
cont = new Holder();
cont.collection = new DummyCollection<T>();
cont.it = new Iterator<T>();
holders.set(cont);
}
Iterator<T> it = (Iterator<T>) cont.it;
DummyCollection<T> collection = (DummyCollection<T>) cont.collection;
it.setList(list);
collection.setIterator(it);
return collection;
}
private FastIterator() {}
private static class Holder {
public DummyCollection<?> collection;
public Iterator<?> it;
}
private static class DummyCollection<T> implements Iterable {
private Iterator<?> it;
#Override
public java.util.Iterator<T> iterator() {
return (java.util.Iterator<T>) it;
}
public void setIterator(Iterator<?> it) {
this.it = it;
}
}
private static class Iterator<T> implements java.util.Iterator<T> {
private ArrayList<T> list;
private int size;
private int i;
#Override
public boolean hasNext() {
return i < size;
}
#Override
public T next() {
return list.get(i++);
}
#Override
public void remove() {
}
public void setList(ArrayList<T> list) {
this.list = list;
size = list.size();
i = 0;
}
private Iterator() {}
}
}
You should not use for each in Android games.
I think this official video talks about that too.
Probably the best approach would be to use a Decorator design. Create a class which takes a collection in the constructor and implements the Iterable interface by calling the wrapped class and reusing the iterator returned.
Two additional approaches for avoiding the allocation of iterators.
First is to use a callback idiom:
public interface Handler<T> {
void handle(T element);
}
public interface Handleable<T> {
void handleAll(Handler<T> handler);
}
public class HandleableList<T> extends ArrayList<T> implements Handleable<T> {
public void handleAll(Handler<T> handler) {
for (int i = 0; i < size(); ++i) {
handler.handle(get(i));
}
}
}
This approach still requires an instance of a Handler to receive the callback, but this can definitely reduce allocations when, for example, you are trying to visit the elements of several lists.
Second approach is to use a cursor idiom:
public interface Cursor<T> {
void reset();
boolean next();
T current();
}
public class CursoredList<T> extends ArrayList<T> implements Cursor<T> {
private int _index = -1;
public void reset() {
_index = -1;
}
public boolean next() {
return ++_index >= size();
}
public T current() {
return get(_index);
}
}
Sure, this is the same as implementing Iterable and Iterator on your subtype of ArrayList, but this clearly shows the cursor location as state on the collection itself.

Categories