Is there a list implementation that would allow gaps? - java

I'm looking for a collection that would be some sort of a list that allows gaps. The objectives are:
every element has some index in the collection that is meaningful.
the collection is to be sparse and not continuous; its size should return the number of proper elements, hence the workaround of initializing with null wouldn't work.
subList method is desirable to access sublists according to index intervals
Sample use case:
List<Integer> list = /* ? */;
list.add(0,5);
list.add(1,4);
list.add(5,3);
for( Integer i : list )
{
System.out.print( i + " " );
}
/* desired output : "5 4 3 "*/

Use a Map<Integer,Integer>. The key would be your index and the value the value of the list.
For your subList requirement, perhaps TreeMap<Integer,Integer> would work, as it keeps the keys sorted and makes it easy to iterate over a sub-list.
Of course this means you can't use the List interface. If you must use the List interface, you can make your own List implementation backed by a TreeMap (for example, list.add(5,3) would call map.put(5,3)).

You may use a Map and only set the keys you need.
You can keep the insertion order if you want, take a look: Java Class that implements Map and keeps insertion order

Related

In an array of arraylists, get the largest arraylist

I have an array with multiple arraylists of different sizes.
I want to find the index of the largest arraylist in the array. I tried this:
Collections.max(disc);
disc being the name of the array of arraylists.
ArrayList I believe doesn't implement the Comparable interface so I can't do it like this. Is there a way to make a custom comparable for sizes of ArrayLists?
If you want to know the index of the largest sub-ArrayList then you're basically looking for an information relative to where the largest ArrayList is stored. This research is not based solely on the intrinsic characteristics of your biggest ArrayList but also on where it is located.
Using Collections.max won't help you, even if you redefine its natural ordering providing a new Comparator, because you would only get the largest ArrayList not the index where it is stored. If you want to find the index you have to manually loop the outer ArrayList and whenever you find a new largest sub-ArrayList save its index.
Of course, all of what I said is based on the sole condition that you're interested in retrieving the index and not the biggest sub-ArrayList itself. If you're simply looking for the biggest sub-ArrayList then Collections.max with a Comparator implementation is what you need.
You can't add interfaces to an existing class. If you want ArrayList to implement Comparable, you'll need to write an adaptor class that contains an ArrayList and implements the interface.
But, in your case, you don't need that. Comparable has a sister interface called Comparator which is an external object specifying how to compare some type. It can be used to provide alternative sorting mechanisms (descending rather than ascending, for instance) or, as in your case, to add comparison capabilities to an existing class that lacks them. And Collections.max has an overload that takes a comparator.
Collections.max(disc, (a, b) -> a.size() - b.size())
Note that if you're on a really old Java version, you'll need to explicitly create a Comparator instance, rather than using SAM conversion like I do above.
I want to find the index of the largest arraylist in the array.
Collections.max does not take an array nor does it return an index. It returns the large element in a Collection.
Without knowing your exact data structure I am using a list of lists.
List<List<Integer>> ss =
List.of(
List.of(1, 2, 3),
List.of(1, 2, 3, 4),
List.of(1, 2)
);
This just compares the sizes and returns the index that targets the maximum list
int maxIndex = IntStream.range(0, ss.size()).boxed().collect(Collectors
.maxBy(Comparator.comparing(i -> ss.get(i).size()))).get();
prints
1
The maxBy Collector returns an Optional of which I immediately took the value. You may want to assign it to an Optional and then process appropriately for an empty collection.
A simpler way of doing it is with a loop. Just find the max size and associate the index with it.
maxIndex = 0;
int maxSize = 0;
for (int i = 0; i < ss.size(); i++) {
int size = ss.get(i).size();
if (size> maxSize) {
maxIndex = i;
maxSize = size;
}
}
System.out.println(maxIndex);
prints
1
I prefer the more clever solution by WJS, but here is another solution as well. This one here is not as brief but might be easier to comprehend on first reading.
You said:
I have an array with multiple arraylists of different sizes. I want to find the index of the largest arraylist in the array.
As they discussed, Collections.max returns you a reference to the largest object, not the the index of that object within the array.
So we need two steps:
Determine the largest element.
Get the index of that largest object.
First, some example data.
List[] arrayOfListsOfIntegers =
List.of(
List.of( 1 ) ,
List.of( 1001 , 1002 , 1003 , 1004 ) ,
List.of( 101 , 102 , 103 ) ,
List.of( 11 , 12 )
)
.toArray( List[] :: new );
Determine the largest element
Use Arrays.asList to make a view onto that array that appears as a List. This way we can call convenient List methods.
Then make a stream of that list. Sort the elements of the stream by getting each one’s size. Make a new list from the sorted elements. We know the last element of that sorted list has the biggest element.
List sorted =
Arrays
.asList( arrayOfListsOfIntegers )
.stream()
.sorted( Comparator.comparingInt( List :: size ) )
.toList();
Get a reference to that biggest element, the last element of our sorted list of lists.
Object target = sorted.get( sorted.size() - 1 );
Get the index of that largest object
That target object is what we want to locate within our original list. We can locate by calling List#indexOf. Here again we use Arrays.asList to mask our array as a List.
int index = Arrays.asList( arrayOfListsOfIntegers ).indexOf( target );

How to get first or last item from cqengine IndexedCollection with NavigableIndex

I have com.googlecode.cqengine.IndexedCollection object with NavigableIndex configured. I need to get first or last item from the index or iterator of the index in general.
I suppose this should be trivial. I know I can create Query object with queryOptions object, use it to retrieve iterator from IndexedCollection and get first object, but I'm not sure if it's optimal for performance. Surely it's not elegant.
With help of miradham I figured out that I need to remember indexes, since it's hard to pick up the right one if we have more of them. It will only work with NavigableIndex, we can't iterate base class Index
collection = new ConcurrentIndexedCollection<Data>();
index = NavigableIndex.onAttribute(Data.UNIQUE_TIMESTAMP);
collection.addIndex(index);
when I have the index:
try (CloseableIterator<KeyValue<String, Data>> iterator = indexUniqueTimestamp.getKeysAndValuesDescending(null).iterator()) {
if (iterator.hasNext())
return iterator.next().getValue();
}
return null;
One trick to retrieve the min or max (i.e first or last) object according on one of its attributes, is to use an all() query (which matches all objects in the collection), and to request that results should be returned in ascending or descending order of your attribute.
For example, if you had a collection of Car objects, you could use the following code to retrieve the car which has the highest (i.e. the max) price:
try (ResultSet<Car> results = cars.retrieve(
all(Car.class),
queryOptions(
orderBy(descending(Car.PRICE)),
applyThresholds(
threshold(INDEX_ORDERING_SELECTIVITY, 1.0)
)
))) {
results.stream()
.limit(1)
.forEach(System.out::println);
}
You can also change the limit to something other than 1, in case you want the top n most expensive cars to be returned.
The code above will work regardless of whether or not you actually have a NavigableIndex on the price. The bit about INDEX_ORDERING_SELECTIVITY is to actually request CQEngine to leverage the index (more details here).
or iterator of the index in general
You can use getIndexes() API of QueryEngine interface to retrieve set of Indexes.
Example code:
IndexedCollection<Car> indexedCollection = new ConcurrentIndexedCollection<Car>();
indexedCollection.addIndex(HashIndex.onAttribute(Car.CAR_ID), noQueryOptions());
List<Index<Car>> indexes = new ArrayList<Index<Car>>();
for (Index<Car> index : indexedCollection.getIndexes()) {
indexes.add(index);
}
NavigableIndex stores object in element in Map with attribute as key and set of object as value.
NavigableIndex does not maintain insertion order. First element of the index could be anything.
CQEngine is best designed for random access of object in collection not sequential.
Normal collections in java is best suited for sequence access with index.
one elegant way of accessing first element is to create SequentialIndex class and add it to concurrent collection. retrieve element using index as query.

Cull all duplicates in a set

I'm using Set to isolate the unique values of a List (in this case, I'm getting a set of points):
Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);
This will return a set of unique points, but for every item in listToCull, I'd like to test the following: if there is a duplicate, cull all of the duplicate items. In other words, I want pointSet to represent the set of items in listToCull which are already unique (every item in pointSet had no duplicate in listToCull). Any ideas on how to implement?
EDIT - I think my first question needs more clarification. Below is some code which will execute what I'm asking for, but I'd like to know if there is a faster way. Assuming listToCull is a list of PVectors with duplicates:
Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);
List<PVector> uniqueItemsInListToCull = new ArrayList<PVector>();
for(PVector pt : pointSet){
int counter=0;
for(PVector ptCheck : listToCull){
if(pt==ptCheck){
counter++;
}
}
if(counter<2){
uniqueItemsInListToCull.add(pt);
}
}
uniqueItemsInListToCull will be different from pointSet. I'd like to do this without loops if possible.
You will have to do some programming yourself: Create two empty sets; on will contain the unique elements, the other the duplicates. Then loop through the elements of listToCull. For each element, check whether it is in the duplicate set. If it is, ignore it. Otherwise, check if it is in the unique element set. If it is, remove it there and add to the duplicates set. Otherwise, add it to the unique elements set.
If your PVector class has a good hashCode() method, HashSets are quite efficient, so the performance of this will not be too bad.
Untested:
Set<PVector> uniques = new HashSet<>();
Set<PVector> duplicates = new HashSet<>();
for (PVector p : listToCull) {
if (!duplicates.contains(p)) {
if (uniques.contains(p)) {
uniques.remove(p);
duplicates.add(p);
}
else {
uniques.add(p);
}
}
}
Alternatively, you may use a third-party library which offers a Bag or MultiSet. This allows you to count how many occurrences of each element are in the collection, and then at the end discard all elements where the count is different than 1.
What you are looking for is the intersection:
Assuming that PVector (terrible name by the way) implements hashCode() and equals() correctly a Set will eliminate duplicates.
If you want a intersection of the List and an existing Set create a Set from the List then use Sets.intersection() from Guava to get the ones common to both sets.
public static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)
Returns an unmodifiable view of the intersection of two sets. The returned set contains all
elements that are contained by both backing sets. The iteration order
of the returned set matches that of set1. Results are undefined if
set1 and set2 are sets based on different equivalence relations (as
HashSet, TreeSet, and the keySet of an IdentityHashMap all are).
Note: The returned view performs slightly better when set1 is the
smaller of the two sets. If you have reason to believe one of your
sets will generally be smaller than the other, pass it first.
Unfortunately, since this method sets the generic type of the returned
set based on the type of the first set passed, this could in rare
cases force you to make a cast, for example:
Set aFewBadObjects = ... Set manyBadStrings =
...
// impossible for a non-String to be in the intersection
SuppressWarnings("unchecked") Set badStrings = (Set)
Sets.intersection(
aFewBadObjects, manyBadStrings); This is unfortunate, but should come up only very rarely.
You can also do union, complement, difference and cartesianProduct as well as filtering very easily.
So you want pointSet to hold the items in listToCull which have no duplicates? Is that right?
I would be inclined to create a Map, then iterate twice over the list, the first time putting a value of zero in for each PVector, the second time adding one to the value for each PVector, so at the end you have a map with counts. Now you're interested in the keys of the map for which the value is exactly equal to one.
It's not perfectly efficient - you're operating on list items more times than absolutely necessary - but it's quite clean and simple.
OK, here's the solution I've come up with, I'm sure there are better ones out there but this one's working for me. Thanks to all who gave direction!
To get unique items, you can run a Set, where listToCull is a list of PVectors with duplicates:
List<PVector> culledList = new ArrayList<PVector>();
Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);
culledList.addAll(pointSet);
To go further, suppose you want a list where you've removed all items in listToCull which have a duplicate. You can iterate through the list and test whether it's in the set for each item. This let's us do one loop, rather than a nested loop:
Set<PVector> pointSet = new HashSet<PVector>(listToCull);
Set<PVector> removalList = new HashSet<PVector>();//list to remove
for (PVector pt : listToCull) {
if (pointSet.contains(pt)) {
removalList.add(pt);
}
else{
pointSet.add(pt);
}
}
pointSet.removeAll(removalList);
List<PVector> onlyUniquePts = new ArrayList<PVector>();
onlyUniquePts.addAll(pointSet);

java why does an enhanced for loop iterate over null results in a HashMap with more than 16 or more items?

The code I used originally, which returned a NullPointerException in some places for any HashMap containing 16 or more elements:
for(Entry<Integer, String> e : myHashMap.entrySet()){
System.out.println(e.getKey() + ": "+e.getValue());
}
The code I am now using, which works on the same HashMap, regardless of size:
int i = 0; //variable to show the index
int c = 0; //variable to count the number items found
while(c < myHashMap.size()){
if(myHashMap.containsKey(i)){ //if the HashMap contains the key i
System.out.println(i + ": "+myHashMap.get(i)); //Print found item
c++; //increment up to count the number of objects found
}
i++; //increment to iterate to the next key
}
What is the difference between the two? Why does the first one iterate over null values? And, more importantly, why does the first one iterate out of order if there are 16 or more items? (ie: 12,13,17,15,16,19,18 instead of the neat 12,13,14,15,16,17,18,19 in the second)
I think I am just starting to scratch the surface of java, so I would like to understand why it was designed this way. Any book recommendations on this kind of thing are welcome.
You should read the documentation of a class and try to understand its purpose before starting to use it. HashMap provides an efficient storage but no guaranteed order. It’s just a coincident that you didn't discover it with smaller HashMap sizes because the default capacity is 16 and the hash codes of contiguous Integer objects are contiguous too. But that is not a property you can rely on. You always have to assume no guaranteed order for a HashMap.
If you need the insertion order you can use a LinkedHashMap, if you need ascending order of the keys you can use a TreeMap. If you have a contiguous range of Integer keys and want ascending order you can simply use an array as well.
The foreach loop for(Entry<Integer, String> e : myHashMap.entrySet()) does not “iterate over null values”. It iterates over the values contained in the HashMap which are the values you have added before. There can be at most one null key contained in the map, if you added it. You might see null values in the debugger when looking at the internal array of a HashMap which are unused slots as a HashMap has a capacity which can be larger than its size.

How To Prevent Duplicates In Object ArrayList

I have a custom object array list, the object must be in an array list however i have some duplicates in the list and i want to preform a check before i do an add to the list. How can this be achieved. The victimSocialSecurityNumber is unique. Under is my code:
CODE
while (rs.next()){
Citizens victims = new Citizens();
victims.setSocialSecurityNumber(rs.getInt("victimSocialSecurityNumber"));
victims.setfName(rs.getString("victimFName"));
victims.setlName(rs.getString("victimLName"));
victims.setPhoto(rs.getString("victimPhoto"));
victims.setName(rs.getString("victimFName") +" "+ rs.getString("victimLName"));
crime.getVictims().add(victims);
you can convert arraylist to set and back to get rid of the duplicates or use directly structure which allows only sorted unique elements : LinkedHashSet
Assuming Citizens overrides equals, you can do it like this
if (!crime.getVictims().contains(victims)) {
crime.getVictims().add(victims);
}
though generally when duplicates are not allowed the solution is Set
If you have doubts how to override equals / hashCode read http://javarevisited.blogspot.com/2011/10/override-hashcode-in-java-example.html
You can use a hash set to add the objects and convert it to an Arraylist. This can help you to check whether the victim is unique.
CODE
Set hashset = new HashSet();
while (rs.next()){
Citizens victims = new Citizens();
victims.setSocialSecurityNumber(rs.getInt("victimSocialSecurityNumber"));
victims.setfName(rs.getString("victimFName"));
victims.setlName(rs.getString("victimLName"));
victims.setPhoto(rs.getString("victimPhoto"));
victims.setName(rs.getString("victimFName") +" "+ rs.getString("victimLName"));
hashset.add(victims);
}
List list = new ArrayList(hashset);
I could be completely wrong here, but wouldn't a for loop solve your problem? You could just compare what you are about to add to all the elements in the arraylist, and if there are no matches add it, and if there is don't?

Categories