I have an ArrayList filled with objects with attributes name and time. I would like to remove duplicates based on the name and keep only records with the latest time. So I have overriden equals and hashcode for name in my object and used code like this.
private List<ChangedRecentlyTO> groupRecords(List<ChangedRecentlyTO> toList) {
changedRecentlyList.clear(); //static list
for(ChangedRecentlyTO to : toList) {
if(!changedRecentlyList.contains(to)) {
changedRecentlyList.add(to);
} else {
if(changedRecentlyList.get(changedRecentlyList.lastIndexOf(to)).getTimeChanged().before(to.getTimeChanged())) {
changedRecentlyList.remove(to);
changedRecentlyList.add(to);
}
}
}
return changedRecentlyList;
}
But I am wondering, is there a better solution?I was thinking about using Set but I am not able to figure out how should I put there the time criterion.
You have to me two ways, one which requires understanding how the set work, and one which is more understandable for people who have littler understanding of Java Collections:
If you want to make it simple, you can simply read in the detail the Javadoc of Set, http://docs.oracle.com/javase/6/docs/api/java/util/Set.html#add(E). It clearly states that if an element is already inside, it won't be added again.
You implement your equals and hashcode using only the name
You sort the items by time and then you add them to the Set.
In such a way, the first time you will add the item to Set, you will be adding the elements with the latest times. When you'll add the others, they will be ignored because they are already contained.
If someone else who does not know exactly the contract of java.util.Set behaves, you might want to extend Set to make your intention clearer. However, since a Set is not supposed to be accessed to "get back an element after removal", you will need to back your set with an HashMap:
interface TimeChangeable {
long getTimeChanged();
}
public class TimeChangeableSet<E extends TimeCheangeable> implements Set<E> {
private final HashMap<Integer,E> hashMap = new HashMap<Integer,E>();
#Override
public boolean add(E e) {
E existingValue = hashMap.remove(e.hashCode());
if(existingValue==null){
hashMap.put(e.hashCode(),e);
return true;
}
else{
E toAdd = e.getTimeChanged() > existingValue.getTimeChanged() ? e : existingValue;
boolean newAdded = e.getTimeChanged() > existingValue.getTimeChanged() ? true : false;
hashMap.put(e.hashCode(),e);
return newAdded;
}
}
#Override
public int size() {
return hashMap.size();
}
#Override
public boolean isEmpty() {
return hashMap.isEmpty();
}
#Override
public boolean contains(Object o) {
return hashMap.containsKey(o.hashCode());
}
#Override
public Iterator<E> iterator() {
return hashMap.values().iterator();
}
#Override
public Object[] toArray() {
return hashMap.values().toArray();
}
#Override
public <T> T[] toArray(T[] a) {
return hashMap.values().toArray(a);
}
#Override
public boolean remove(Object o) {
return removeAndGet(o)!=null ? true : false;
}
public E removeAndGet (Object o) {
return hashMap.remove(o.hashCode());
}
#Override
public boolean containsAll(Collection<?> c) {
boolean containsAll = true;
for(Object object:c){
E objectInMap = removeAndGet(object);
if(objectInMap==null || !objectInMap.equals(object))
containsAll=false;
}
return containsAll;
}
#Override
public boolean addAll(Collection<? extends E> c) {
boolean addAll=true;
for(E e:c){
if(!add(e)) addAll=false;
}
return addAll;
}
#Override
public boolean retainAll(Collection<?> c) {
boolean setChanged=false;
for(E e: hashMap.values()){
if(!c.contains(e)){
hashMap.remove(e.hashCode());
setChanged=true;
}
}
return setChanged;
}
#Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("Please do not use type-unsafe methods in 2012");
}
#Override
public void clear() {
hashMap.clear();
}
}
Extend HashMap and override put method to put only if new object is more recent than the existing one.
Or, you can create your own dedicated container which will be backed by a HashMap, just like some implementations of Stack are backed by LinkedList
This is a mock code:
import java.util.HashMap;
import java.util.Map;
public class TimeMap<K, V> {
private Map<K, V> timeMap;
public TimeMap() {
this.timeMap = new HashMap<K, V>();
}
public void put(K key, V value) {
if (isNewer(key, value)) {
this.timeMap.put(key, value);
}
}
}
Why you dont use a Set and later:
new ArrayList(set);
A very quick implementation of what I had in mind.
Assumed the ChangedRecentlyTO object had a name property.
private List<ChangedRecentlyTO> groupRecords(List<ChangedRecentlyTO> toList) {
Map<String, ChangedRecentlyTO> uniqueMap = new HashMap<String, ChangedRecentlyTO>();
for(ChangedRecentlyTO to : toList) {
if (uniqueMap.containsKey(to.getName())) {
if (uniqueMap.get(to.getName()).getTimeChanged().before(to.getTimeChanged())) {
uniqueMap.put(to.getName(), to);
}
} else {
uniqueMap.put(to.getName(), to);
}
}
return (List<ChangedRecentlyTO>) uniqueMap.values();
}
After all of that, it doesn't seem to different to your original implementation with the exception that there is no need override hashcode and equals.
You could let your class implement the Comparable interface and make compare check the timestamps you are interested in. If you then sort it (e.g. put all the elements in a TreeSet) and then get them out one by one, only if they don't already exist. Something like this:
public void removeDuplicates(List<MyObject> list){
SortedSet<MyObject> sortedSet = new TreeSet<MyObject>();
sortedSet.addAll(list);
//Now clear the list, and start adding them again
list.clear();
for(MyObject obj : sortedSet){
if(!list.contains(obj) {
list.add(obj);
}
}
return list;
}
This, however, will only work if two objects with different timestamps are not equal! (in the equals() sense of the word
What I would suggest , Make your class Comparable by implementing Comparable interface.Then in comparetTo() based on name and time compare them if object time is recent return 1 else 0(if equal) or -1 .Once you got this functionality you can extend HashMap class and override the put method like.
o1.compareTo(o2) > 0 then simply overwrite the object with latest one.
Adding logic to #Lopina code like
public class MyHashMap extends HashMap<String, MyClass>{
private Map<String, MyClass> timeMap;
public MyHashMap() {
this.timeMap = new HashMap<String, MyClass>();
}
public MyClass put(String key, MyClass value) {
MyClass obj;
if (isNewer(key, value)) {
System.out.println("count");
obj=this.timeMap.put(key, value);
}else{
obj=value;
}
return obj;
}
private boolean isNewer(String key, MyClass value) {
if(this.timeMap.get(key)==null ||( key.equals(value.getName()))&& (this.timeMap.get(key).compareTo(value))<0)
return true;
else
return false;
}
#Override
public int size() {
return this.timeMap.size();
}
#Override
public MyClass get(Object key) {
return this.timeMap.get(key);
}
}
In MyClass implement comparable interface and override compareTo method like below.
#Override
public int compareTo(MyClass o) {
return this.getTime().compareTo(o.getTime());
}
I wrote a UniqueList class that extends an ArrayList to back its data and utilises a HashSet to efficiently reject duplicates. This gives O(1) Random Access Time and many other speed improvements to manually sweeping the dataset.
https://gist.github.com/hopesenddreams/80730eaafdfe816ddbb1
public class UniqueList<T> extends ArrayList<T> implements Set<T>
{
HashMap<T,Integer> hash; // T -> int
public UniqueList()
{
hash = new HashMap<>();
}
/*
* O(n)
* */
#Override
public void add(int location, T object)
{
super.add(location, object);
for( int i = location ; i < size() ; i++ )
{
hash.put(get(i),i);
}
}
/*
* O(1) amortized.
* */
#Override
public boolean add(T object) {
if( hash.containsKey(object) ) return false;
hash.put(object, size());
super.add(object);
return true;
}
/*
* O(MAX(collection.size(),n)) because of the hash-value-shift afterwards.
* */
#Override
public boolean addAll(int location, Collection<? extends T> collection) {
boolean bChanged = false;
for( T t : collection)
{
if( ! hash.containsKey( t ) )
{
hash.put(t, size());
super.add(t);
bChanged = true;
}
}
for( int i = location + collection.size() ; i < size() ; i ++ )
{
hash.put( get(i) , i );
}
return bChanged;
}
/*
* O(collection.size())
* */
#Override
public boolean addAll(Collection<? extends T> collection) {
boolean bChanged = false;
for( T t : collection)
{
if( ! hash.containsKey( t ) )
{
hash.put( t , size() );
super.add(t);
bChanged = true;
}
}
return bChanged;
}
/*
* O(n)
* */
#Override
public void clear() {
hash.clear();
super.clear();
}
/*
* O(1)
* */
#Override
public boolean contains(Object object) {
return hash.containsKey(object);
}
/*
* O(collection.size())
* */
#Override
public boolean containsAll(Collection<?> collection) {
boolean bContainsAll = true;
for( Object c : collection ) bContainsAll &= hash.containsKey(c);
return bContainsAll;
}
/*
* O(1)
* */
#Override
public int indexOf(Object object) {
//noinspection SuspiciousMethodCalls
Integer index = hash.get(object);
return index!=null?index:-1;
}
/*
* O(1)
* */
#Override
public int lastIndexOf(Object object)
{
return hash.get(object);
}
/*
* O(n) because of the ArrayList.remove and hash adjustment
* */
#Override
public T remove(int location) {
T t = super.remove(location);
hash.remove( t );
for( int i = size() - 1 ; i >= location ; i -- )
{
hash.put( get(i) , i );
}
return t;
}
/*
* O(n) because of the ArrayList.remove and hash adjustment
* */
#Override
public boolean remove(Object object) {
Integer i = hash.get( object );
if( i == null ) return false;
remove( i.intValue() );
return true;
}
/*
* O( MAX( collection.size() , ArrayList.removeAll(collection) ) )
* */
#Override
public boolean removeAll(#NonNull Collection<?> collection) {
for( Object c : collection )
{
hash.remove( c );
}
return super.removeAll( collection );
}
}
Related
I have a simple class that wraps an unmodifiable list (it is used as a context for a query):
public class Context<T extends Node> implements Iterable<T> {
private final List<T> m_nodes;
Context(List<T> nodes) {
m_nodes = Collections.unmodifiableList(nodes);
}
#Override
public Iterator<T> iterator() {
return m_nodes.iterator();
}
public int indexOf(T node) {
return m_nodes.indexOf(node);
}
public boolean isEmpty() {
return m_nodes.isEmpty();
}
// context are always sorted by pre value
public boolean containsAll(FDMContext<T> other) {
Iterator<T> oit = other.iterator();
Iterator<T> sit = iterator();
int c = 0;
while( oit.hasNext()) {
T o = oit.next();
while( sit.hasNext()) {
T s = sit.next();
if( s.getPre() == o.getPre()) {
c++;
break;
}
}
}
return c == other.size();
}
public int size() {
return m_nodes.size();
}
public List<T> getNodes() {
return m_nodes;
}
#Override
public String toString() {
return m_nodes.toString();
}
}
The Node class has a Value subclass. I don't think the details of the Node and Value class are important, except that they share a common getPre() method defining a unique node identifier.
If there is a method that expects an Context<Value> parameter but I have an Context<Node> instance as a result of a query where I am certain it only contains Value instances, is it safe to suppress warnings?
e.g.
static Entry makeEntry(Event event, Group head, Group tail, Context<Value> values) {
/* code */
}
...
Context<Node> ctx = query(anotherCtx,somePath); // the result ctx contains only Value objects. The query method returns a Context(Node) object
#SuppressWarnings({ "rawtypes", "unchecked" })
Entry ret = makeEntry( this, headGrp, tailGrp, (Context)tail);
I could create a new context of type Context<Value>, but I'd rather avoid the copy if possible.
I'm creating a class-wide project with my school mates, I'm supposed to create pull requests for some functions, but I'm having some problems just creating the class and overriding the methods in a way that it would just compile (I don't need to write the methods at this moment, that's the project, I just need it to compile). I've found most methods are working (or at least the compiler is not complaining), but I'm confused about a few things:
Compiler complaining about methods that are optional (like set, and addAll)
The method addAll, although it was added, complains that it wasn't overridden, although it was, and when I add the other addAll method for it, then I get an erasure error as well.
I've read a bunch about it, but I couldn't find a proper conclusion on how to solve it. I'm just using Atom to write my code, and the Terminal, no fancy IDE (perhaps I should learn one).
In case it isn't clear, I'm just looking to have the stubs of the methods available, not an all around answer for every single method, since this is the project with the class.
// https://docs.oracle.com/javase/8/docs/api/java/util/List.html
import java.util.*;
import java.lang.reflect.*;
public class SkipList<E> implements List<E>
{
// compiler complaining, then added, although optional
public E set(int index, E element)
{
throw new IndexOutOfBoundsException();
}
// compiler complaining, then added, although optional
public boolean addAll(Collection <? extends E> c)
{
return true;
}
// Group 1
public boolean add(E e)
{
return true;
}
public void add(int index, E e)
{
}
public boolean addAll(Collection c)
{
return true;
}
public int indexOf(Object o)
{
int index = 0;
return index;
}
public int lastIndexOf(Object o)
{
int index = 0;
return index;
}
// Group 2
public boolean contains(Object o)
{
return true;
}
public boolean containsAll(Collection c)
{
return true;
}
public boolean equals(Object o)
{
return true;
}
public List<E> subList(int fromIndex, int toIndex)
{
List<E> sub = new SkipList<>();
return sub;
}
// Group 3
public boolean isEmpty()
{
return true;
}
public int size()
{
int size = 0;
return size;
}
public void clear()
{
}
public E get(int index)
{
throw new IndexOutOfBoundsException();
}
public E getQuantile(double quantile) // e.g. 0 = minimum, 0.5 = median, 1 = max
{
throw new IndexOutOfBoundsException();
}
// Group 4
public Iterator<E> iterator()
{
throw new IndexOutOfBoundsException();
}
public ListIterator<E> listIterator()
{
throw new IndexOutOfBoundsException();
}
public ListIterator<E> listIterator(int index)
{
throw new IndexOutOfBoundsException();
}
// Group 5
public E remove(int index)
{
throw new IndexOutOfBoundsException();
}
public boolean remove(Object o)
{
return true;
}
public boolean removeAll(Collection c)
{
return true;
}
public boolean retainAll(Collection c)
{
return true;
}
// Group 6
public int hashCode()
{
int hashCode = 0;
return hashCode;
}
public Object[] toArray()
{
Object[] arr = new Object[0];
return arr;
}
public <T> T[] toArray(T[] a)
{
return a;
}
}
Turns out that when reading the API, for some reason I glossed over the other parameter for the addAll method, that led me to believe something else was wrong. I changed the method with the correct parameters and it compiled.
public boolean addAll(int index, Collection <? extends E> c)
{
return true;
}
HOW TO IMPLEMENT LAST METHOD? I have implemented most of the beginning parts of this polymorphic binary search tree but can't figure out how to check that two trees have the same keys. Keys could be in any order but the two trees need to have identical size and identical keys (values don't matter). This method haveSameKeys returns a boolean (method at very bottom) if this has the same keys as otherTree. I first check if the trees have the same size but don't know anything further than this. I cannot use any arrays or other Java Library classes but I may add helper methods (probably recursive). Suggestions?
#SuppressWarnings("unchecked")
public class NonEmptyTree<K extends Comparable<K>, V> implements Tree<K, V> {
public K key;
public V value;
public Tree<K,V> leftTree;
public Tree<K,V> rightTree;
public NonEmptyTree(K key, V value, Tree<K,V> leftTree,
Tree<K,V> rightTree) {
this.key=key;
this.value=value;
this.leftTree=leftTree;
this.rightTree=rightTree;
}
public NonEmptyTree<K, V> addKeyWithValue(K keyToAdd, V valueToAdd) {
if(keyToAdd.compareTo(key)==0) {
value = valueToAdd;
}
if(keyToAdd.compareTo(key)>0) {
rightTree = rightTree.addKeyWithValue(keyToAdd, valueToAdd);
}
if(keyToAdd.compareTo(key)<0) {
leftTree = leftTree.addKeyWithValue(keyToAdd, valueToAdd);
}
return this;
}
public int size() {
return 1 + leftTree.size() + rightTree.size();
}
public V lookup(K lookUpKey) {
if(lookUpKey.compareTo(key)>0) {
return this.rightTree.lookup(lookUpKey);
}
if(lookUpKey.compareTo(key)<0) {
return this.leftTree.lookup(lookUpKey);
}
if(lookUpKey.compareTo(key)!=0) {
return null;
}
return this.value;
}
public K max() throws EmptyTreeException {
try{
K temp = this.rightTree.max();
return temp;
}
catch(EmptyTreeException e) {
return key;
}
}
public K min() throws EmptyTreeException {
try{
K temp = this.leftTree.min();
return temp;
}
catch(EmptyTreeException e) {
return key;
}
}
public Tree<K, V> deleteKeyAndValue(K keyToDelete) {
if(keyToDelete.compareTo(key)>0) {
rightTree = rightTree.deleteKeyAndValue(keyToDelete);
}
if(keyToDelete.compareTo(key)<0) {
leftTree = leftTree.deleteKeyAndValue(keyToDelete);
}
if(keyToDelete.compareTo(key)==0) {
try{
value = this.lookup(rightTree.min());
key = rightTree.min();
}
catch(EmptyTreeException e) {
return this.leftTree;
}
}
return this;
}
public boolean haveSameKeys(Tree<K, V> otherTree) {
boolean check = true;
if(this.size()!=otherTree.size()) {
check = false;
}
}
// Tests haveSameKeys() with two empty trees.
#Test public void testPublic9() {
Tree<Byte, Boolean> tree= EmptyTree.getInstance();
Tree<Byte, Boolean> tree2= EmptyTree.getInstance();
assertTrue(tree.haveSameKeys(tree2));
assertTrue(tree2.haveSameKeys(tree));
}
// Tests haveSameKeys() with an empty tree and a nonempty tree
#Test public void testPublic10() {
Tree<String, Integer> tree= EmptyTree.getInstance();
Tree<String, Integer> tree2= TestCode.exampleTree1();
assertFalse(tree.haveSameKeys(tree2));
assertFalse(tree2.haveSameKeys(tree));
}
// Tests haveSameKeys() with two nonempty trees that have the same keys.
#Test public void testPublic11() {
Tree<String, Integer> tree= TestCode.exampleTree1();
Tree<String, Integer> tree2= TestCode.exampleTree1();
assertTrue(tree.haveSameKeys(tree2));
assertTrue(tree2.haveSameKeys(tree));
}
You can use recursion to check if every key in the tree is contained in the other tree, since lookup returns null if no such key exists.
For this, I'm making some assumptions:
EmptyTree.haveSameKeys returns otherTree.size() == 0
EmptyTree.hasSameKeysalways returns true
EmptyTree.lookup always returns null.
I CAN add public methods to the Tree interface that Empty/NonEmpty trees implement though if i please...
Thus, you'll need to add hasSameKeys to the Tree interface:
// Checks if every key in 'this' tree is contained in 'otherTree'
public boolean hasSameKeys(Tree<K, V> otherTree) {
if (otherTree.lookup(this.key) == null) { // if key does not exist
return false;
}
return (leftTree.hasSameKeys(otherTree) && rightTree.hasSameKeys(otherTree));
}
public boolean haveSameKeys(Tree<K, V> otherTree) { // Both trees should have the same keyset
return hasSameKeys(otherTree) && otherTree.hasSameKeys(this);
}
What this returns:
EmptyTree and EmptyTree; true && true. => Empty trees have the same keyset
NonEmptyTree and EmptyTree; false && true. => Or the other way around. Always.
NonEmptyTree and NonEmptyTree; true => If both have the exact same key set.
There is a method in the Collections class.
Set<E> Collections.newSetFromMap(<backing map>)
What does it mean by the backing map and the set backed by a map?
Perhaps it would be illuminating to look at the implementation:
private static class SetFromMap<E> extends AbstractSet<E>
implements Set<E>, Serializable
{
private final Map<E, Boolean> m; // The backing map
private transient Set<E> s; // Its keySet
SetFromMap(Map<E, Boolean> map) {
if (!map.isEmpty())
throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
public void clear() { m.clear(); }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public boolean remove(Object o) { return m.remove(o) != null; }
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
public Iterator<E> iterator() { return s.iterator(); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
public String toString() { return s.toString(); }
public int hashCode() { return s.hashCode(); }
public boolean equals(Object o) { return o == this || s.equals(o); }
public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
public boolean removeAll(Collection<?> c) {return s.removeAll(c);}
public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
// addAll is the only inherited implementation
private static final long serialVersionUID = 2454657854757543876L;
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
s = m.keySet();
}
}
Edit - added explanation:
The map that you provide is used as the m field in this object.
When you add an element e to the set, it adds an entry e -> true to the map.
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
So this class turns your Map into an object that behaves like a Set by simply ignoring the values that things are mapped to, and just using the keys.
I just made an example code for you
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
Set<String> set = Collections.newSetFromMap(map);
System.out.println(set);
for (int i = 0; i < 10; i++)
map.put("" + i, i % 2 == 0);
System.out.println(map);
System.out.println(set);
and the output
[]
{3=false, 2=true, 1=false, 0=true, 7=false, 6=true, 5=false, 4=true, 9=false, 8=true}
[3, 2, 1, 0, 7, 6, 5, 4, 9, 8]
Simply put, Collections.newSetFromMap uses the provided Map<E> implementation to store the Set<E> elements.
The Set internally uses Map to store the values. Here the backing map refers to the set map which is internally used by the set.
For more information.
http://www.jusfortechies.com/java/core-java/inside-set.php
Is there a way to iterate over Java SparseArray (for Android) ? I used sparsearray to easily get values by index. I could not find one.
Seems I found the solution. I hadn't properly noticed the keyAt(index) function.
So I'll go with something like this:
for(int i = 0; i < sparseArray.size(); i++) {
int key = sparseArray.keyAt(i);
// get the object by the key.
Object obj = sparseArray.get(key);
}
If you don't care about the keys, then valueAt(int) can be used to while iterating through the sparse array to access the values directly.
for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
Object obj = sparseArray.valueAt(i);
}
Ooor you just create your own ListIterator:
public final class SparseArrayIterator<E> implements ListIterator<E> {
private final SparseArray<E> array;
private int cursor;
private boolean cursorNowhere;
/**
* #param array
* to iterate over.
* #return A ListIterator on the elements of the SparseArray. The elements
* are iterated in the same order as they occur in the SparseArray.
* {#link #nextIndex()} and {#link #previousIndex()} return a
* SparseArray key, not an index! To get the index, call
* {#link android.util.SparseArray#indexOfKey(int)}.
*/
public static <E> ListIterator<E> iterate(SparseArray<E> array) {
return iterateAt(array, -1);
}
/**
* #param array
* to iterate over.
* #param key
* to start the iteration at. {#link android.util.SparseArray#indexOfKey(int)}
* < 0 results in the same call as {#link #iterate(android.util.SparseArray)}.
* #return A ListIterator on the elements of the SparseArray. The elements
* are iterated in the same order as they occur in the SparseArray.
* {#link #nextIndex()} and {#link #previousIndex()} return a
* SparseArray key, not an index! To get the index, call
* {#link android.util.SparseArray#indexOfKey(int)}.
*/
public static <E> ListIterator<E> iterateAtKey(SparseArray<E> array, int key) {
return iterateAt(array, array.indexOfKey(key));
}
/**
* #param array
* to iterate over.
* #param location
* to start the iteration at. Value < 0 results in the same call
* as {#link #iterate(android.util.SparseArray)}. Value >
* {#link android.util.SparseArray#size()} set to that size.
* #return A ListIterator on the elements of the SparseArray. The elements
* are iterated in the same order as they occur in the SparseArray.
* {#link #nextIndex()} and {#link #previousIndex()} return a
* SparseArray key, not an index! To get the index, call
* {#link android.util.SparseArray#indexOfKey(int)}.
*/
public static <E> ListIterator<E> iterateAt(SparseArray<E> array, int location) {
return new SparseArrayIterator<E>(array, location);
}
private SparseArrayIterator(SparseArray<E> array, int location) {
this.array = array;
if (location < 0) {
cursor = -1;
cursorNowhere = true;
} else if (location < array.size()) {
cursor = location;
cursorNowhere = false;
} else {
cursor = array.size() - 1;
cursorNowhere = true;
}
}
#Override
public boolean hasNext() {
return cursor < array.size() - 1;
}
#Override
public boolean hasPrevious() {
return cursorNowhere && cursor >= 0 || cursor > 0;
}
#Override
public int nextIndex() {
if (hasNext()) {
return array.keyAt(cursor + 1);
} else {
throw new NoSuchElementException();
}
}
#Override
public int previousIndex() {
if (hasPrevious()) {
if (cursorNowhere) {
return array.keyAt(cursor);
} else {
return array.keyAt(cursor - 1);
}
} else {
throw new NoSuchElementException();
}
}
#Override
public E next() {
if (hasNext()) {
if (cursorNowhere) {
cursorNowhere = false;
}
cursor++;
return array.valueAt(cursor);
} else {
throw new NoSuchElementException();
}
}
#Override
public E previous() {
if (hasPrevious()) {
if (cursorNowhere) {
cursorNowhere = false;
} else {
cursor--;
}
return array.valueAt(cursor);
} else {
throw new NoSuchElementException();
}
}
#Override
public void add(E object) {
throw new UnsupportedOperationException();
}
#Override
public void remove() {
if (!cursorNowhere) {
array.remove(array.keyAt(cursor));
cursorNowhere = true;
cursor--;
} else {
throw new IllegalStateException();
}
}
#Override
public void set(E object) {
if (!cursorNowhere) {
array.setValueAt(cursor, object);
} else {
throw new IllegalStateException();
}
}
}
For whoever is using Kotlin, honestly the by far easiest way to iterate over a SparseArray is: Use the Kotlin extension from Anko or Android KTX! (credit to Yazazzello for pointing out Android KTX)
Simply call forEach { i, item -> }
Simple as Pie. Just make sure you fetch array size before actually performing the loop.
for(int i = 0, arraySize= mySparseArray.size(); i < arraySize; i++) {
Object obj = mySparseArray.get(/* int key = */ mySparseArray.keyAt(i));
}
Hope this helps.
For removing all the elements from SparseArray using the above looping leads to Exception.
To avoid this Follow the below code to remove all the elements from SparseArray using normal loops
private void getValues(){
for(int i=0; i<sparseArray.size(); i++){
int key = sparseArray.keyAt(i);
Log.d("Element at "+key, " is "+sparseArray.get(key));
sparseArray.remove(key);
i=-1;
}
}
Here is simple Iterator<T> and Iterable<T> implementations for SparseArray<T>:
public class SparseArrayIterator<T> implements Iterator<T> {
private final SparseArray<T> array;
private int index;
public SparseArrayIterator(SparseArray<T> array) {
this.array = array;
}
#Override
public boolean hasNext() {
return array.size() > index;
}
#Override
public T next() {
return array.valueAt(index++);
}
#Override
public void remove() {
array.removeAt(index);
}
}
public class SparseArrayIterable<T> implements Iterable<T> {
private final SparseArray<T> sparseArray;
public SparseArrayIterable(SparseArray<T> sparseArray) {
this.sparseArray = sparseArray;
}
#Override
public Iterator<T> iterator() {
return new SparseArrayIterator<>(sparseArray);
}
}
If you want to iterate not only a value but also a key:
public class SparseKeyValue<T> {
private final int key;
private final T value;
public SparseKeyValue(int key, T value) {
this.key = key;
this.value = value;
}
public int getKey() {
return key;
}
public T getValue() {
return value;
}
}
public class SparseArrayKeyValueIterator<T> implements Iterator<SparseKeyValue<T>> {
private final SparseArray<T> array;
private int index;
public SparseArrayKeyValueIterator(SparseArray<T> array) {
this.array = array;
}
#Override
public boolean hasNext() {
return array.size() > index;
}
#Override
public SparseKeyValue<T> next() {
SparseKeyValue<T> keyValue = new SparseKeyValue<>(array.keyAt(index), array.valueAt(index));
index++;
return keyValue;
}
#Override
public void remove() {
array.removeAt(index);
}
}
public class SparseArrayKeyValueIterable<T> implements Iterable<SparseKeyValue<T>> {
private final SparseArray<T> sparseArray;
public SparseArrayKeyValueIterable(SparseArray<T> sparseArray) {
this.sparseArray = sparseArray;
}
#Override
public Iterator<SparseKeyValue<T>> iterator() {
return new SparseArrayKeyValueIterator<T>(sparseArray);
}
}
It's useful to create utility methods that return Iterable<T> and Iterable<SparseKeyValue<T>>:
public abstract class SparseArrayUtils {
public static <T> Iterable<SparseKeyValue<T>> keyValueIterable(SparseArray<T> sparseArray) {
return new SparseArrayKeyValueIterable<>(sparseArray);
}
public static <T> Iterable<T> iterable(SparseArray<T> sparseArray) {
return new SparseArrayIterable<>(sparseArray);
}
}
Now you can iterate SparseArray<T>:
SparseArray<String> a = ...;
for (String s: SparseArrayUtils.iterable(a)) {
// ...
}
for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
// ...
}
If you use Kotlin, you can use extension functions as such, for example:
fun <T> LongSparseArray<T>.valuesIterator(): Iterator<T> {
val nSize = this.size()
return object : Iterator<T> {
var i = 0
override fun hasNext(): Boolean = i < nSize
override fun next(): T = valueAt(i++)
}
}
fun <T> LongSparseArray<T>.keysIterator(): Iterator<Long> {
val nSize = this.size()
return object : Iterator<Long> {
var i = 0
override fun hasNext(): Boolean = i < nSize
override fun next(): Long = keyAt(i++)
}
}
fun <T> LongSparseArray<T>.entriesIterator(): Iterator<Pair<Long, T>> {
val nSize = this.size()
return object : Iterator<Pair<Long, T>> {
var i = 0
override fun hasNext(): Boolean = i < nSize
override fun next() = Pair(keyAt(i), valueAt(i++))
}
}
You can also convert to a list, if you wish. Example:
sparseArray.keysIterator().asSequence().toList()
I think it might even be safe to delete items using remove on the LongSparseArray itself (not on the iterator), as it is in ascending order.
EDIT: Seems there is even an easier way, by using collection-ktx (example here) . It's implemented in a very similar way to what I wrote, actally.
Gradle requires this:
implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'
Here's the usage for LongSparseArray :
val sparse= LongSparseArray<String>()
for (key in sparse.keyIterator()) {
}
for (value in sparse.valueIterator()) {
}
sparse.forEach { key, value ->
}
And for those that use Java, you can use LongSparseArrayKt.keyIterator , LongSparseArrayKt.valueIterator and LongSparseArrayKt.forEach , for example. Same for the other cases.
The answer is no because SparseArray doesn't provide it. As pst put it, this thing doesn't provide any interfaces.
You could loop from 0 - size() and skip values that return null, but that is about it.
As I state in my comment, if you need to iterate use a Map instead of a SparseArray. For example, use a TreeMap which iterates in order by the key.
TreeMap<Integer, MyType>
The accepted answer has some holes in it. The beauty of the SparseArray is that it allows gaps in the indeces. So, we could have two maps like so, in a SparseArray...
(0,true)
(250,true)
Notice the size here would be 2. If we iterate over size, we will only get values for the values mapped to index 0 and index 1. So the mapping with a key of 250 is not accessed.
for(int i = 0; i < sparseArray.size(); i++) {
int key = sparseArray.keyAt(i);
// get the object by the key.
Object obj = sparseArray.get(key);
}
The best way to do this is to iterate over the size of your data set, then check those indeces with a get() on the array. Here is an example with an adapter where I am allowing batch delete of items.
for (int index = 0; index < mAdapter.getItemCount(); index++) {
if (toDelete.get(index) == true) {
long idOfItemToDelete = (allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);
}
}
I think ideally the SparseArray family would have a getKeys() method, but alas it does not.