LinkedHashMap eldest entry giving null key,value - java

While trying to use a LinkedHashMap as a LRU cache, I am facing null pointer exceptions. A similar issue was discussed here, however my scenario is a bit different.
#Override
protected boolean removeEldestEntry(Map.Entry<K, CacheItem<V>> eldest)
{
if(size() >= maxEntriesOnHeap)
{
if (eldest.getValue() != null && eldest.getValue().isExpired(timeToLiveSecs))
{
offheap.put(eldest.getKey(), eldest.getValue());
}
return true;
}
return false;
}
The entry object is a wrapper object. What I found that if I do not provide the null check, it fails intermittently with the 'eldest' entry encountered having null key and null value. Proper synchronizations are in place.
So, is anyone aware of a scenario when an entry can exist with both key,value as null?

In a simple test case, this seems to work. Maybe you can point out the differences between this test and your implementation?
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashCacheTest
{
public static void main(String[] args)
{
Map<String, CacheItem<Integer>> map = create();
map.put("K0", new CacheItem<Integer>(0));
map.put("K1", new CacheItem<Integer>(1));
map.put("K2", new CacheItem<Integer>(2));
map.put("K3", new CacheItem<Integer>(3));
map.put("K4", new CacheItem<Integer>(4));
map.put("K5", new CacheItem<Integer>(5));
}
static class CacheItem<V>
{
V v;
CacheItem(V v)
{
this.v = v;
}
public boolean isExpired(double timeToLiveSecs)
{
return false;
}
#Override
public String toString()
{
return String.valueOf(v);
}
}
static <K, V> Map<K, CacheItem<V>> create()
{
Map<K, CacheItem<V>> map = new LinkedHashMap<K, CacheItem<V>>()
{
#Override
protected boolean removeEldestEntry(Map.Entry<K, CacheItem<V>> eldest)
{
int maxEntriesOnHeap = 5;
double timeToLiveSecs = 2;
if(size() >= maxEntriesOnHeap)
{
System.out.println("Removing : "+eldest.getKey()+", "+eldest.getValue());
if (eldest.getValue().isExpired(timeToLiveSecs))
{
System.out.println("To off-heap: "+eldest.getKey()+", "+eldest.getValue());
//offheap.put(eldest.getKey(), eldest.getValue());
}
return true;
}
return false;
}
};
return map;
}
}
In any case, the question may sound naive, but ... are you sure that there are no null keys used? A statement like
map.put(null, value);
will work in the first place...

Related

java: issue while access value from TreeMap in multithreading

I have use TreeMap to store key value.
For key using custom object.
But once I have faced very strange issue, I am not able to get value which I have set earlier(with same key).
below is my code
public final class TestOptions implements Cloneable {
private Map<StorageSystemOptionKey, Object> options = new TreeMap<StorageSystemOptionKey, Object>();
private static final class StorageSystemOptionKey implements Comparable<StorageSystemOptionKey> {
/** Constant used to create hashcode */
private static final int HASH = 31;
private final Class<? extends StorageRepository> storageRepositoryClass;
/** The option name */
private final String name;
private StorageSystemOptionKey(Class<? extends StorageRepository> storageRepositoryClass, String name) {
this.storageRepositoryClass = storageRepositoryClass;
this.name = name;
}
public int compareTo(StorageSystemOptionKey o) {
int ret = storageRepositoryClass.getName().compareTo(o.storageRepositoryClass.getName());
if (ret != 0) {
return ret;
}
return name.compareTo(o.name);
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final StorageSystemOptionKey that = (StorageSystemOptionKey) o;
if (!storageRepositoryClass.equals(that.storageRepositoryClass)) {
return false;
}
if (!name.equals(that.name)) {
return false;
}
return true;
}
#Override
public int hashCode() {
int result;
result = storageRepositoryClass.hashCode();
result = HASH * result + name.hashCode();
return result;
}
}
void setOption(Class<? extends StorageRepository> fileSystemClass, String name, Object value) {
options.put(new StorageSystemOptionKey(fileSystemClass, name), value);
}
Object getOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.get(key);
}
boolean hasOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.containsKey(key);
}
public int compareTo(TestOptions other) {
if (this == other) {
return 0;
}
int propsSz = options == null ? 0 : options.size();
int propsFkSz = other.options == null ? 0 : other.options.size();
if (propsSz < propsFkSz) {
return -1;
}
if (propsSz > propsFkSz) {
return 1;
}
if (propsSz == 0) {
return 0;
}
int hash = options.hashCode();
int hashFk = other.options.hashCode();
if (hash < hashFk) {
return -1;
}
if (hash > hashFk) {
return 1;
}
return 0;
}
#Override
public Object clone() {
TestOptions clone = new TestOptions();
clone.options = new TreeMap<StorageSystemOptionKey, Object>(options);
return clone;
}
}
calling method to set and get like
public abstract Class<? extends StorageRepository> getStorageRepositoryClass();
public Class<? extends StorageRepository> getStorageRepositoryClass() {
return MyImpl.class;
}
TestOptions opt =new TestOptions(); // shared accross all Threads
Object getProperty(String name) {
return opt.getOption(getStorageRepositoryClass(), name);
}
void setProperty(String name, Object value) {
opt.setOption(getStorageRepositoryClass(), name, value);
}
Using set and get method in multi-threaded application.
queries:
I am calling set/get in multiple time then also I was not able to get value which was set earlier(same key)
Is this due to Treeset implementation is not synchronized
or problem with hashCode, equals or compareTo method implementation?
On a quick glance your compareTo(), equals() and hashCode() look fine. Note that TreeMap will mostly use compareTo() to find elements so that method needs to be correct (your's looks technically correct).
However, TreeMap and TreeSet (as well as other basic collections and maps) are not thread-safe and thus concurrent modifications can cause all kinds of unexpected behavior. We once had a case where 2 threads were trying to add a single element to a hashmap and the threads ended up in an endless loop because the internal list to resolve clashes produced a cycle (due to the concurrent put).
So either use the ConcurrentXxxx maps and collections or synchronize access to yours.
TreeSet is not synchronized. I belive ConcurrentSkipListMap might be better.
Check also your hashCode, equals implementation

A java map where the keys are known however the values should be computed later on as they are expensive

Does a java map implementation exist where the keys are known, however the values should only be computed on the first access as calculating the values is expensive.
The following demonstrates how I would like it to work.
someMap.keySet(); // Returns all keys but no values are computed.
someMap.get(key); // Returns the value for key computing it if needed.
The reason for this is I have something which holds a bunch of data and this Object returns the data as a Map<String, String> this is computationally heavy to compute because computing the values is expensive, the keys are however cheap to compute.
The Map must maintain its type so I can't return a Map<String, Supplier<String>>. The returned Map may be returned as read only.
The map itself could be created by passing in both a Set<String> defining the keys and a Function<String, String> which given a key returns its value.
One solution could be to have a Map that takes a Set of keys and a Function which given a key can compute the value.
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
/**
* Create a Map where we already know the keys but computing the values is expensive and so is delayed as
* much as possible.
*
*/
#AllArgsConstructor
public class MapWithValuesProvidedByFunction implements Map<String, String> {
/**
* All keys that are defined.
*/
private Set<String> keys;
/**
* A function which maps a key to its value.
*/
private Function<String, String> mappingFunction;
/**
* Holds all keys and values we have already computed.
*/
private final Map<String, String> computedValues = new HashMap<>();
#Override
public int size() {
return keys.size();
}
#Override
public boolean isEmpty() {
return keys.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return keys.contains(key);
}
#Override
public boolean containsValue(Object value) {
if(computedValues.size() == keys.size()) return computedValues.containsValue(value);
for(String k : keys) {
String v = get(k);
if(v == value) return true;
if(v != null && v.equals(value)) return true;
}
return false;
}
#Override
public String get(Object key) {
if(keys.contains(key)) {
return computedValues.computeIfAbsent(key.toString(), mappingFunction);
}
return null;
}
#Override
public String put(String key, String value) {
throw new UnsupportedOperationException("Not modifiable");
}
#Override
public String remove(Object key) {
throw new UnsupportedOperationException("Not modifiable");
}
#Override
public void putAll(Map<? extends String, ? extends String> m) {
throw new UnsupportedOperationException("Not modifiable");
}
#Override
public void clear() {
throw new UnsupportedOperationException("Not modifiable");
}
#Override
public Set<String> keySet() {
return Collections.unmodifiableSet(keys);
}
#Override
public Collection<String> values() {
return keys.stream().map(this::get).collect(Collectors.toList());
}
#Override
public Set<java.util.Map.Entry<String, String>> entrySet() {
Set<Entry<String, String>> set = new HashSet<>();
for(String s : keys) {
set.add(new MyEntry(s, this::get));
}
return set;
}
#AllArgsConstructor
#EqualsAndHashCode
public class MyEntry implements Entry<String, String> {
private String key;
private Function<String, String> valueSupplier;
#Override
public String getKey() {
return key;
}
#Override
public String getValue() {
return valueSupplier.apply(key);
}
#Override
public String setValue(String value) {
throw new UnsupportedOperationException("Not modifiable");
}
}
}
An example of this being used might be:
Map<String, String> map = new MapWithValuesProvidedByFunction(
Set.of("a", "b", "c"), // The known keys
k -> "Slow to compute function"); // The function to make the values
Changing this to be generic should be easy enough.
I suspect a better solution exists, however this might be good enough for someone else.
You could do something like this. The map has a key and a Fnc class which holds a function and the argument to the function.
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class MapDemo {
public static Map<String, Object> mymap = new HashMap<>();
public static void main(String[] args) {
MapDemo thisClass = new MapDemo();
// populate the functions
mymap.put("v1", new Fnc<String>(String::toUpperCase));
mymap.put("10Fact", new Fnc<Integer>((Integer a) -> {
int f = 1;
int k = a;
while (k-- > 1) {
f *= k;
}
return f + "";
}));
mymap.put("expensive",
new Fnc<Integer>(thisClass::expensiveFunction));
// access them - first time does the computation
System.out.println(getValue("expensive", 1000));
System.out.println(getValue("10Fact", 10));
System.out.println(getValue("v1", "hello"));
// second time does not.
System.out.println(getValue("expensive"));
System.out.println(getValue("10Fact"));
System.out.println(getValue("v1"));
}
public String expensiveFunction(int q) {
return q * 100 + ""; // example
}
static class Fnc<T> {
Function<T, String> fnc;
public Fnc(Function<T,String> fnc) {
this.fnc = fnc;
}
}
public <T> void addFunction(String key,
Function<String, T> fnc) {
mymap.put(key, fnc);
}
public static String getValue(String key) {
Object ret = mymap.get(key);
if (ret instanceof Fnc) {
return null;
}
return (String)mymap.get(key);
}
public static <T> String getValue(String key, T arg) {
Object ret = mymap.get(key);
if (ret instanceof Fnc) {
System.out.println("Calculating...");
ret = ((Fnc)ret).fnc.apply(arg);
mymap.put(key, ret);
}
return (String) ret;
}
}
First time thru, the function is called and the value is computed, stored, and returned. Subsequent calls return the stored value.
Note that the value replaces the computing function.

How to check if two binary search trees have the same exact keys (ignoring values)?

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.

Create an ArrayList of unique values [duplicate]

This question already has answers here:
Get unique values from ArrayList in Java
(9 answers)
Closed 2 years ago.
I have an ArrayList with values taken from a file (many lines, this is just an extract):
20/03/2013 23:31:46 6870 6810 6800 6720 6860 6670 6700 6650 6750 6830 34864 34272
20/03/2013 23:31:46 6910 6780 6800 6720 6860 6680 6620 6690 6760 6790 35072 34496
Where the first two values per line are strings that contain data and are stored in a single element.
What I want to do is compare the string data elements and delete, for example, the second one and all the elements referred to in that line.
For now, I've used a for loop that compares the string every 13 elements (in order to compare only data strings).
My question: can I implement other better solutions?
This is my code:
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
public static void main(String[] args) throws Exception{
//The input file
Scanner s = new Scanner(new File("prova.txt"));
//Saving each element of the input file in an arraylist
ArrayList<String> list = new ArrayList<String>();
while (s.hasNext()){
list.add(s.next());
}
s.close();
//Arraylist to save modified values
ArrayList<String> ds = new ArrayList<String>();
//
int i;
for(i=0; i<=list.size()-13; i=i+14){
//combining the first to values to obtain data
String str = list.get(i)+" "+list.get(i+1);
ds.add(str);
//add all the other values to arraylist ds
int j;
for(j=2; j<14; j++){
ds.add(list.get(i+j));
}
//comparing data values
int k;
for(k=0; k<=ds.size()-12; k=k+13){
ds.get(k); //first data string element
//Comparing with other strings and delete
//TODO
}
}
}
}
Try checking for duplicates with a .contains() method on the ArrayList, before adding a new element.
It would look something like this
if(!list.contains(data))
list.add(data);
That should prevent duplicates in the list, as well as not mess up the order of elements, like people seem to look for.
Create an Arraylist of unique values
You could use Set.toArray() method.
A collection that contains no duplicate elements. More formally, sets
contain no pair of elements e1 and e2 such that e1.equals(e2), and at
most one null element. As implied by its name, this interface models
the mathematical set abstraction.
http://docs.oracle.com/javase/6/docs/api/java/util/Set.html
HashSet hs = new HashSet();
hs.addAll(arrayList);
arrayList.clear();
arrayList.addAll(hs);
Pretty late to the party, but here's my two cents:
Use a LinkedHashSet
I assume what you need is a collection which:
disallows you to insert duplicates;
retains insertion order.
LinkedHashSet does this. The advantage over using an ArrayList is that LinkedHashSet has a complexity of O(1) for the contains operation, as opposed to ArrayList, which has O(n).
Of course, you need to implement your object's equals and hashCode methods properly.
//Saving each element of the input file in an arraylist
ArrayList<String> list = new ArrayList<String>();
while (s.hasNext()){
list.add(s.next());
}
//That's all you need
list = (ArrayList) list.stream().distinct().collect(Collectors.toList());
If you want to make a list with unique values from an existing list you can use
List myUniqueList = myList.stream().distinct().collect(Collectors.toList());
Use Set
...
Set<String> list = new HashSet<>();
while (s.hasNext()){
list.add(s.next());
}
...
You can easily do this with a Hashmap. You obviously have a key (which is the String data) and some values.
Loop on all your lines and add them to your Map.
Map<String, List<Integer>> map = new HashMap<>();
...
while (s.hasNext()){
String stringData = ...
List<Integer> values = ...
map.put(stringData,values);
}
Note that in this case, you will keep the last occurence of duplicate lines. If you prefer keeping the first occurence and removing the others, you can add a check with Map.containsKey(String stringData); before putting in the map.
You could use a Set. It is a collection which doesn't accept duplicates.
Solution #1: HashSet
A good solution to the immediate problem of reading a file into an ArrayList with a uniqueness constraint is to simply keep a HashSet of seen items. Before processing a line, we check that its key is not already in the set. If it isn't, we add the key to the set to mark it as finished, then add the line data to the result ArrayList.
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
ArrayList<String[]> data = new ArrayList<>();
HashSet<String> seen = new HashSet<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
if (!seen.contains(key)) {
data.add(Arrays.copyOfRange(split, 2, split.length));
seen.add(key);
}
}
}
for (String[] row : data) {
System.out.println(Arrays.toString(row));
}
}
}
Solution #2: LinkedHashMap/LinkedHashSet
Since we have key-value pairs in this particular dataset, we could roll everything into a LinkedHashMap<String, ArrayList<String>> (see docs for LinkedHashMap) which preserves ordering but can't be indexed into (use-case driven decision, but amounts to the same strategy as above. ArrayList<String> or String[] is arbitrary here--it could be any data value). Note that this version makes it easy to preserve the most recently seen key rather than the oldest (remove the !data.containsKey(key) test).
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
LinkedHashMap<String, ArrayList<String>> data = new LinkedHashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
if (!data.containsKey(key)) {
ArrayList<String> val = new ArrayList<>();
String[] sub = Arrays.copyOfRange(split, 2, split.length);
Collections.addAll(val, sub);
data.put(key, val);
}
}
}
for (Map.Entry<String, ArrayList<String>> e : data.entrySet()) {
System.out.println(e.getKey() + " => " + e.getValue());
}
}
}
Solution #3: ArrayListSet
The above examples represent pretty narrow use cases. Here's a sketch for a general ArrayListSet class, which maintains the usual list behavior (add/set/remove etc) while preserving uniqueness.
Basically, the class is an abstraction of solution #1 in this post (HashSet combined with ArrayList), but with a slightly different flavor (the data itself is used to determine uniqueness rather than a key, but it's a truer "ArrayList" structure).
This class solves the problems of efficiency (ArrayList#contains is linear, so we should reject that solution except in trivial cases), lack of ordering (storing everything directly in a HashSet doesn't help us), lack of ArrayList operations (LinkedHashSet is otherwise the best solution but we can't index into it, so it's not a true replacement for an ArrayList).
Using a HashMap<E, index> instead of a HashSet would speed up remove(Object o) and indexOf(Object o) functions (but slow down sort). A linear remove(Object o) is the main drawback over a plain HashSet.
import java.util.*;
public class ArrayListSet<E> implements Iterable<E>, Set<E> {
private ArrayList<E> list;
private HashSet<E> set;
public ArrayListSet() {
list = new ArrayList<>();
set = new HashSet<>();
}
public boolean add(E e) {
return set.add(e) && list.add(e);
}
public boolean add(int i, E e) {
if (!set.add(e)) return false;
list.add(i, e);
return true;
}
public void clear() {
list.clear();
set.clear();
}
public boolean contains(Object o) {
return set.contains(o);
}
public E get(int i) {
return list.get(i);
}
public boolean isEmpty() {
return list.isEmpty();
}
public E remove(int i) {
E e = list.remove(i);
set.remove(e);
return e;
}
public boolean remove(Object o) {
if (set.remove(o)) {
list.remove(o);
return true;
}
return false;
}
public boolean set(int i, E e) {
if (set.contains(e)) return false;
set.add(e);
set.remove(list.set(i, e));
return true;
}
public int size() {
return list.size();
}
public void sort(Comparator<? super E> c) {
Collections.sort(list, c);
}
public Iterator<E> iterator() {
return list.iterator();
}
public boolean addAll(Collection<? extends E> c) {
int before = size();
for (E e : c) add(e);
return size() == before;
}
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public boolean removeAll(Collection<?> c) {
return set.removeAll(c) && list.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return set.retainAll(c) && list.retainAll(c);
}
public Object[] toArray() {
return list.toArray();
}
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
Example usage:
public class ArrayListSetDriver {
public static void main(String[] args) {
ArrayListSet<String> fruit = new ArrayListSet<>();
fruit.add("apple");
fruit.add("banana");
fruit.add("kiwi");
fruit.add("strawberry");
fruit.add("apple");
fruit.add("strawberry");
for (String item : fruit) {
System.out.print(item + " "); // => apple banana kiwi strawberry
}
fruit.remove("kiwi");
fruit.remove(1);
fruit.add(0, "banana");
fruit.set(2, "cranberry");
fruit.set(0, "cranberry");
System.out.println();
for (int i = 0; i < fruit.size(); i++) {
System.out.print(fruit.get(i) + " "); // => banana apple cranberry
}
System.out.println();
}
}
Solution #4: ArrayListMap
This class solves a drawback of ArrayListSet which is that the data we want to store and its associated key may not be the same. This class provides a put method that enforces uniqueness on a different object than the data stored in the underlying ArrayList. This is just what we need to solve the original problem posed in this thread. This gives us the ordering and iteration of an ArrayList but fast lookups and uniqueness properties of a HashMap. The HashMap contains the unique values mapped to their index locations in the ArrayList, which enforces ordering and provides iteration.
This approach solves the scalability problems of using a HashSet in solution #1. That approach works fine for a quick file read, but without an abstraction, we'd have to handle all consistency operations by hand and pass around multiple raw data structures if we needed to enforce that contract across multiple functions and over time.
As with ArrayListSet, this can be considered a proof of concept rather than a full implementation.
import java.util.*;
public class ArrayListMap<K, V> implements Iterable<V>, Map<K, V> {
private ArrayList<V> list;
private HashMap<K, Integer> map;
public ArrayListMap() {
list = new ArrayList<>();
map = new HashMap<>();
}
public void clear() {
list.clear();
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return list.contains(value);
}
public V get(int i) {
return list.get(i);
}
public boolean isEmpty() {
return map.isEmpty();
}
public V get(Object key) {
return list.get(map.get(key));
}
public V put(K key, V value) {
if (map.containsKey(key)) {
int i = map.get(key);
V v = list.get(i);
list.set(i, value);
return v;
}
list.add(value);
map.put(key, list.size() - 1);
return null;
}
public V putIfAbsent(K key, V value) {
if (map.containsKey(key)) {
if (list.get(map.get(key)) == null) {
list.set(map.get(key), value);
return null;
}
return list.get(map.get(key));
}
return put(key, value);
}
public V remove(int i) {
V v = list.remove(i);
for (Map.Entry<K, Integer> entry : map.entrySet()) {
if (entry.getValue() == i) {
map.remove(entry.getKey());
break;
}
}
decrementMapIndices(i);
return v;
}
public V remove(Object key) {
if (map.containsKey(key)) {
int i = map.remove(key);
V v = list.get(i);
list.remove(i);
decrementMapIndices(i);
return v;
}
return null;
}
private void decrementMapIndices(int start) {
for (Map.Entry<K, Integer> entry : map.entrySet()) {
int i = entry.getValue();
if (i > start) {
map.put(entry.getKey(), i - 1);
}
}
}
public int size() {
return list.size();
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> es = new HashSet<>();
for (Map.Entry<K, Integer> entry : map.entrySet()) {
es.add(new AbstractMap.SimpleEntry<>(
entry.getKey(), list.get(entry.getValue())
));
}
return es;
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<V> values() {
return list;
}
public Iterator<V> iterator() {
return list.iterator();
}
public Object[] toArray() {
return list.toArray();
}
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
Here's the class in action on the original problem:
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
ArrayListMap<String, String[]> data = new ArrayListMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
String[] sub = Arrays.copyOfRange(split, 2, split.length);
data.putIfAbsent(key, sub);
}
}
for (Map.Entry<String, String[]> e : data.entrySet()) {
System.out.println(e.getKey() + " => " +
java.util.Arrays.toString(e.getValue()));
}
for (String[] a : data) {
System.out.println(java.util.Arrays.toString(a));
}
}
}
Just Override the boolean equals() method of custom object. Say you have an ArrayList with custom field f1, f2, ... override
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CustomObject)) return false;
CustomObject object = (CustomObject) o;
if (!f1.equals(object.dob)) return false;
if (!f2.equals(object.fullName)) return false;
...
return true;
}
and check using ArrayList instance's contains() method. That's it.
If you need unique values, you should use the implementation of the SET interface
You can read from file to map, where the key is the date and skip if the the whole row if the date is already in map
Map<String, List<String>> map = new HashMap<String, List<String>>();
int i = 0;
String lastData = null;
while (s.hasNext()) {
String str = s.next();
if (i % 13 == 0) {
if (map.containsKey(str)) {
//skip the whole row
lastData = null;
} else {
lastData = str;
map.put(lastData, new ArrayList<String>());
}
} else if (lastData != null) {
map.get(lastData).add(str);
}
i++;
}
I use helper class. Not sure it's good or bad
public class ListHelper<T> {
private final T[] t;
public ListHelper(T[] t) {
this.t = t;
}
public List<T> unique(List<T> list) {
Set<T> set = new HashSet<>(list);
return Arrays.asList(set.toArray(t));
}
}
Usage and test:
import static org.assertj.core.api.Assertions.assertThat;
public class ListHelperTest {
#Test
public void unique() {
List<String> s = Arrays.asList("abc", "cde", "dfg", "abc");
List<String> unique = new ListHelper<>(new String[0]).unique(s);
assertThat(unique).hasSize(3);
}
}
Or Java8 version:
public class ListHelper<T> {
public Function<List<T>, List<T>> unique() {
return l -> l.stream().distinct().collect(Collectors.toList());
}
}
public class ListHelperTest {
#Test
public void unique() {
List<String> s = Arrays.asList("abc", "cde", "dfg", "abc");
assertThat(new ListHelper<String>().unique().apply(s)).hasSize(3);
}
}

Make a unique list of objects Java

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 );
}
}

Categories

Resources