Is there any efficient way for getting the nth element of a set in Java?
I know 2 ways of doing it:
- By iterating through it until I reach the required element
- By converting it to an ArrayList and getting the elements from that ArrayList
The question is that is there any other way to get the nth element of it quickly. I would mainly need a feature like that for the TreeSets.
EDIT:
For example if I want to select 1000 random elements from a 10 000 000 element long treemap or treeset, very frequently (i.e. every 2-3 seconds), then cloning it to an arraylist all the time is very inefficient, and iterating through so many elements is also inefficient.
If you are sure that you need n elements from random positions in the set (kind of like a statistical sampling), then you may want to consider just iterating through the set once and pick up the samples, by the desired probability, as you iterate through the set. This way is more efficient as you will iterate through the set only once.
The following program demonstrates the idea:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
public class SamplingFromSet {
public static void main(String[] args) {
Set<String> population = new TreeSet<>();
/*
* Populate the set
*/
final int popSize = 17;
for (int i=0; i<popSize; i++) {
population.add(getRandomString());
}
List<String> sample
= sampleFromPopulation(population, 3 /*sampleSize */);
System.out.println("population is");
System.out.println(population.toString());
System.out.println("sample is");
System.out.println(sample.toString());
}
/**
* Pick some samples for a population
* #param population
* #param sampleSize - number of samples
* #return
*/
private static <T>
List<T> sampleFromPopulation(Set<T> population
, int sampleSize) {
float sampleProb = ((float) sampleSize) / population.size();
List<T> sample = new ArrayList<>();
Iterator<T> iter = population.iterator();
while (iter.hasNext()) {
T element = iter.next();
if (random.nextFloat()<sampleProb) {
/*
* Lucky Draw!
*/
sample.add(element);
}
}
return sample;
}
private static Random random = new Random();
private static String getRandomString() {
return String.valueOf(random.nextInt());
}
}
Output of this program:
population is
[-1488564139, -1510380623, -1980218182, -354029751, -564386445, -57285541, -753388655, -775519772, 1538266464, 2006248253, 287039585, 386398836, 435619764, 48109172, 580324150, 64275438, 860615531]
sample is
[-57285541, -753388655, 386398836]
Update
The above program, however, has a caveat -- since the picking up
of samples in that one walk through the set is done by probability,
the returned sample may, depending on your luck of the day,
have fewer or more samples than specified.
This problem, however, can remedied with a slight change of procedure,
which uses a slightly different method signature:
/**
* Pick some samples from a population
* #param population
* #param sampleSize - number of samples
* #param exactSize - a boolean to control whether or not
* the returned sample list must be of the exact size as
* specified.
* #return
*/
private static <T>
List<T> sampleFromPopulation(Set<T> population
, int sampleSize
, boolean exactSize);
Prevention of oversampling
In the one iteration through the population, we over sample a bit,
and then at the end we drop some samples if we do have too many.
Prevention of undersampling
Note also that, even with oversampling, there is a non-zero probability
that, at the end of the one iteration through the population, we still
get less samples than desired. If that happen (unlikely), we will recursively calling
the same method again as a second try. (This recursion has a probability approaching one
to terminate because it is very unlike that, in repeated recursive
call into the method, we consistently get undersampling.)
The following code implements the new sampleFromPopulation() method:
private static <T>
List<T> sampleFromPopulation(Set<T> population
, int sampleSize
, boolean exactSize) {
int popSize = population.size();
double sampleProb = ((double) sampleSize) / popSize;
final double OVER_SAMPLING_MULIT = 1.2;
if (exactSize) {
/*
* Oversampling to enhance of chance of getting enough
* samples (if we then have too many, we will drop them
* later)
*/
sampleProb = sampleProb * OVER_SAMPLING_MULIT;
}
List<T> sample = new LinkedList<>(); // linked list for fast removal
Iterator<T> iter = population.iterator();
while (iter.hasNext()) {
T element = iter.next();
if (random.nextFloat()<sampleProb) {
/*
* Lucky Draw!
*/
sample.add(element);
}
}
int samplesTooMany = sample.size() - sampleSize;
if (!exactSize || samplesTooMany==0) {
return sample;
} else if (samplesTooMany>0) {
Set<Integer> indexesToRemoveAsSet = new HashSet<>();
for (int i=0; i<samplesTooMany; ) {
int candidate = random.nextInt(sample.size());
if (indexesToRemoveAsSet.add(candidate)) {
/*
* add() returns true if candidate was not
* previously in the set
*/
i++; // proceed to draw next index
}
}
List<Integer> indexesToRemoveAsList
= new ArrayList<>(indexesToRemoveAsSet);
Collections.sort(indexesToRemoveAsList
, (i1, i2) -> i2.intValue() - i1.intValue()); // desc order
/*
* Now we drop from the tail of the list
*/
for (Integer index : indexesToRemoveAsList) {
sample.remove((int) index); // remove by index (not by element)
}
return sample;
} else {
/*
* we were unluckly that we oversampling we still
* get less samples than specified, so here we call
* this very same method again recursively
*/
return sampleFromPopulation(population, sampleSize, exactSize);
}
}
If your requirement is to select random elements out of a rather huge set then you should ask yourself whether a set is the best fit for that.
If you want to use the built-in sets there are several challenges you'd face.
TreeSet
A TreeSet is an ordered set and thus would allow you to access the n-th element. However, you'd have to iterate to position n since there is no array that allows random access like an ArrayList would. As the name implies the nodes in a TreeSet form a tree and the nodes most likely are stored anywhere in memory. Because of this to get the n-th element you'd have to start at the first node and hop from node to node until you reach position n - which is similar to how you'd do it in a LinkedList.
If all you want to do is select a random element there are several options:
If the set doesn't change (or not often) you could create a matching array or ArrayList and use random access.
Iterate over the set for a random number of times.
Generate a random key and look up the next higher/lower element, e.g. by using tailSet(randomKey) and getting the first element of that tail set. Of course you'd have to handle random keys that are outside the elements' range. That way a lookup would basically be a binary search.
HashSet
HashSets basically consist of 2 things: an array of buckets and a linked list or tree for collisions, i.e. if 2 elements would be mapped to the same bucket. Getting a random element might then be done by accessing a random bucket (this would be random access) and then iterating over the elements in that bucket for a random number of times.
Related
I'm trying to write a program using Kahn's algorithm, sort of relating to BFS. Since the Queue and the List have exact keys being put in, is there anyways to remove the queue and make the list perform just like a queue and still return values? I was told to keep the preference for the List instead of removing the keys like what a queue does. I'm not sure how to do it though. Any suggestion is appreciated. Here's one part of my program.
private static List<Job> topologicalSortBFS(final List<Job> jobs) //Kahn's
{
final List<Job> sorted = new ArrayList<>(jobs.size());
final Map<Job, Integer> inCount = new HashMap<>(jobs.size());
final Queue<Job> queue = new ArrayDeque<>();
for (final Job j : jobs)
{
/* Associate every node with the amount of nodes it requires. */
final int in = j.inbound.size();
inCount.put(j, in);
/* If the node requires nothing, then add to queue and sorted list. */
if (in == 0)
{
sorted.add(j);
queue.add(j);
}
}
while (!queue.isEmpty())
{
final Job current = queue.poll(); // poll = pop
for (final Job neighbor : current.outbound)
{
/* Remove an outgoing connection without modifying the node. */
final int updatedIncount = inCount.get(neighbor) - 1;
inCount.put(neighbor, updatedIncount);
/* If node is now considered a leaf, its requirements were met. */
if (updatedIncount == 0)
{
sorted.add(neighbor);
queue.add(neighbor);
}
}
}
return sorted;
}
In your given code, only the poll( ) method is not available for the List object. However, poll( ) works in FIFO manner, returning and removing the topmost object from the queue. Alternatively, for a List you can get the first element using the get(index) method with index value 0 and also remove it. But you should consider using a LinkedList as for the remove( ) operation all the elements in ArrayList will be shifted for each removal, which is a costly operation. Also, LinkedList has the poll( ) method as it implements the Queue interface.
NOTE: Queue fits best for the given example, my answer is just a workaround to used a List as per your question.
So I'm doing a Star Trek themed project where I need to take a list of crew members and the planets they've visited and generate a log report for Starfleet.
For example this...
Guinan,Drema IV
Picard,Gamalon V
Barclay,Valo III
Riker,Theydat IV
Pulaski,Alpha Moon
Troi,Tessen III
...
Needs to become this
Acamar III:
B. Crusher 11
Barclay 6
Data 15
Gomez 3
Guinan 4
Lefler 5
O'Brien 12
Ogawa 4
Picard 5
Pulaski 14
Riker 12
Troi 9
W. Crusher 4
Worf 14
Yar 3
...
To do this I need to use a generic structure that automatically sorts the incoming data so I decided to use a Tree Map of 15-element int arrays to store the number of visits each crew member has made to a given planet.
My question is, since I am very new to Java, how would I get a value from an array element inside the tree map and update the value inside a given array element? My problem is that all the examples I can find about working with Tree Map either don't involve arrays inside of them or don't show how to get a value and update values after the first insertion. Below I've given my current psuedocode with my best guess as to how to accomplish this. If anyone knows of a way to do this, or a better method entirely please suggest it.
P.S. I'm going to implement the loop I need after I can get a single iteration written correctly
EDIT: For clarity, each element of the 15-element int array corresponds to a crew member so for example Data would be array[2] and Yar would be array[14]
import java.util.*;
public class TreeMapDemo {
public static void main(String args[]) {
// Create a hash map
TreeMap tm = new TreeMap();
int indexDesired;
int visits;
String planetNameVariable;
String crewMemberName;
//Scan input using Scanner and assign planet name and crew name to
//correct variables (code provided by instructor)
// Put elements to the map
//if(planet doesn't already exist in tm)
tm.put(planetNameVariable, new int[14]);
//Decides which element of the array must be incremented
indexDesired = crewToIndex(crewMemberName);
//Increments visit count of crewMemberName on planetNameVariable
visits = //How do I get the value of the array associated with planetNameVariable at indexDesired?
tm.put(planetNameVariable, int[indexDesired] = visits + 1 //How do I insert into an array element here?);
// Get an iterator
Iterator i = set.iterator();
// Display element
// Code not designed yet
}
}
You can something like this. Here you have to put array in map only once because after that you will get only reference so if you modify that it will be modified in map as well.[shallow copy]
int visits[] = null;
// Increments visit count of crewMemberName on planetNameVariable
visits = tm.get(planetNameVariable);
if (visits == null) {
tm.put(planetNameVariable, new int[14]);
visits = tm.get(planetNameVariable);
}
visits[indexDesired]++;
// Get an iterator
Iterator<String> iterator = tm.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
int[] temp = tm.get(key);
if (temp != null) {
for (int i = 0; i < temp.length; i++) {
System.out.println(key + " " + temp[i]);
}
}
}
I have a method that adds an object to an array but it adds it based on order. So lets say I have an array that has an apples, and cola. If I wanted to add a banana it would go between those two objects. But I keep getting errors when I run my jUnit tests. Here is the code:
/**
* Adds an item to the list. This method assumes that the list is already
* sorted in alphabetical order based on the names of the items in the list.
*
* The new item will be inserted into the list in the appropriate place so
* that the list will remain alphabetized by names.
*
* In order to accomodate the new item, the internal array must be re-sized
* so that it is one unit larger than it was before the call to this method.
*
* #param itemToAdd refers to a Listable item to be added to this list
*/
public void add(Listable itemToAdd) {
int i;
Listable[] newItems = new Listable[items.length+1];
for(i=0; i<items.length;i++){
if(items[i].getName().compareTo(itemToAdd.getName()) < 0){
newItems[i] = items[i];
} else{
break;
}
}
int str=i;
for(i=str+1;i<items.length;i++){
newItems[i+1] = items[i];
}
newItems[str] = itemToAdd;
}
I keep getting an error that states that the test expected <14> but got <0> so now I think that means its my constructor thats the problem:
/**
* This constructor creates an empty list by creating an internal array
* of size 0. (Note that this is NOT the same thing as setting the internal
* instance variable to null.)
*/
public SortedListOfImmutables() {
int i = 0;
items = new Listable[0];
//orders the list
for( i=0;i<items.length;i++){
if(Food.FOOD_OBJECTS[i].getName().compareTo(Food.FOOD_OBJECTS[i+1].getName()) < 0){
items[i] = Food.FOOD_OBJECTS[i];
}else{
items[i+1] = Food.FOOD_OBJECTS[i];
}
}
}
I think atleast one of your problems is with this for loop:
for(i=str+1;i<items.length;i++){
newItems[i+1] = items[i];
}
I'll just use an example list to show you why. Lets say our items list contains {apple, banana, orange, pineapple}. Then we want to insert "grape" which should be inserted between "banana" and "orange". The first for loop does does 3 iterations before breaking (putting "apple" and "banana" into newItems, then breaks on i=2) You assign str = 2 which is the index "grape" should be inserted. But then when inserting the remaining elements, you set i=str+1 which would be 3 in this case, so
newItems[i+1] = items[i];
would put "pineapple" into index 4 of newItems, which is correct but you completely skip putting what is in index 2 in items ("orange") into the newItems at index 3.
To fix this, you either want to do:
for(i=str;i<items.length;i++){
newItems[i+1] = items[i];
}
or:
for(i=str+1;i<items.length;i++){
newItems[i] = items[i-1];
}
I have a problem using java.util.Vector and java.util.ArrayList.
I know the capacity or better how many elements will be saved within the vector. So i initialized a java.util.List using an implementation of it leveraging the constructor new Vector<?>(int capacity).
After the initialisation of the List, I used the method set(index, value) but this call results in an IndexOutOfBoundException. Which is quite confusing because i set the capacity to a given value using the constructor.
The following code snippet shows the problem:
public void calculateSimple(List<Stock> values, int n) {
if (n<=0) {
throw new IllegalArgumentException("N must not be zero or negativ");
}
int max = values.size();
result = new Vector<Double>(max);
System.out.println("Size of result "+result.size());
if (max == 0) {
result.add(0.0);
}
if (max <= n) {
n = max;
}
for (int i = 1; i <= n; i++) {
List<Double> subList = values.subList(max-i-n, max-i);
result.set(result.size()-i, calculateSimpleValue(subList, n));
}
}
I know i can solve this problem using simple arrays. I want to ask if there are any bugs within the code or do i have some wrong imaginations about the capacity constructor, concerning the class Vector or any other List implementation.
UPDATE
The question is:
Is it possible to use any kind of java.util data structur in a hybrid way
(Arrays, Dynamic List (or any other))
If you want to create a list initialied with some values, like 0.0 or nulls, here is a quick way to do it:
ArrayList<Double> list = new ArrayList<>(Collections.nCopies(100, value));
The capacity constructor parameter does not mean that such number of elements will be added to ArrayList automatically. It just means the initial size of internal buffer which will be allocated. It can be used if you can estimate in advance how many elements you will have in the list to improve the performance. But you still need to add actual elements to the list. You can do it using the loop:
for(int i=0; i<max; i++) result.add(0.0);
From the java.util.Vector.set() method i can see
/**
* Replaces the element at the specified position in this Vector with the
* specified element.
*
* #param index index of the element to replace
* #param element element to be stored at the specified position
* #return the element previously at the specified position
* #throws ArrayIndexOutOfBoundsException if the index is out of range
* ({#code index < 0 || index >= size()})
* #since 1.2
*/
public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
so when you are using the set method it is possible that your result.size()-i >= calculateSimpleValue(subList, n) .Where elementCount = elementdata.length in the constructor when you are instantiating the vector.
In the example above , the point where you have used sublist method, if max=n then in that case it will result into a negative value, which can also result into an IndexOutOfBoundException
I am working on a traveling salesman problem here and my p-queue isn't operating it is simply taking the last item added. I was wonder if anyone could help me figure out the error. here is my Node class (nodes which are added to the queue):
import java.util.*;
public class Node implements Comparable< Node >{
//level of node
int level;
//stores path of node
ArrayList< Integer > path = new ArrayList< Integer >();
//bound of node
int bound;
/** Over-rides compareTo for priority queue handling
* #return int desired sorting value
*/
public int compareTo(Node aNode)
{
if (this.bound<aNode.bound)
{
return 1;
}
if (this.bound>aNode.bound)
{
return -1;
}
else
{
return 0;
}
}
}
and here is the p-queue implementation:
PriorityQueue< Node > theQ = new PriorityQueue< Node >();
The algorithm is implemented correctly the p-queue simply is not putting the lowest bound as the head. I even reversed the the returns on the compareTo with no effect on the p-queue output (signifying to me that the queue is not sorting. I have wasted hours trying to figure it out and also asking some classmates (no-one can discern the problem) taking a shot here to see if anyone knows why the queue is acting like this..
Your code works perfectly fine for me.
What I suspect you're doing is changing the the bound value of a single object and repeatedly adding it, giving you a queue full of the same object (lots of references to it) which of course has the single (last) value you set it to.
public static void main(String[] args)
{
PriorityQueue< Node > theQ = new PriorityQueue< Node >();
Node n = new Node();
n.bound = 6;
theQ.add(n);
n = new Node();
n.bound = 9;
theQ.add(n);
n = new Node();
n.bound = 4;
theQ.add(n);
while ((n = theQ.poll()) != null)
System.out.println("Bound = " + n.bound);
}
Output:
Bound = 9
Bound = 6
Bound = 4
Make sure you are iterating through the PriorityQueue by using the methods provided by the Queue interface, ex. remove to pop an element off the top. In pseudo code:
for each element in some other collection
priorityQueue.add(element)
while priorityQueue is not empty
Set node to priorityQueue.remove()
Do stuff with node
If you are trying to iterate through a for-each loop or PriorityQueue.iterator:
The Iterator provided in method
iterator() is not guaranteed to
traverse the elements of the priority
queue in any particular order.
Alternatively, if you don't want to destroy/remove elements from your PriorityQueue to iterate in order, you could use, as the documentation suggests,
Arrays.sort(pq.toArray())