Get an element at a specific index from a NavigableSet - java

I have a NavigableSet and I would like to get its median object.
Being that it's a NavigableSet, I know it's sorted, and thus I know that the median of it is either the middle element, or the arithmetic middle of the two middle elements.
Therefore I would like to access the element at set.size() / 2, but the NavigableSet interface doesn't allow me to.
Is there an easy way to get the specific element without having to iterate through the set manually?

Yes set does not allow you to get an element from a particular index. But I think if you convert it to an array then you will be able to achieve what you need. I tried this sample code, see if it helps:
NavigableSet set = new TreeSet<Integer>();
set.add(new Integer(5));
set.add(new Integer(4));
set.add(new Integer(3));
set.add(new Integer(2));
set.add(new Integer(1));
Integer medianIndex = set.size()/2;
System.out.println(set.toArray()[medianIndex]);
Output: 3

Strings are ordered alphabetically, have a look at this small example:
Edit: Now it really does what you wanted.
public static void main(String[] args) {
NavigableSet<String> set = new TreeSet<String>();
set.add("gamma");
set.add("alpha");
set.add("beta");
System.out.println(Arrays.toString(set.toArray()));
int indexOfGamma = set.headSet("gamma").size();
System.out.println(indexOfGamma);
System.out.println(get(set, set.first(), indexOfGamma));
}
public static String get(NavigableSet<String> set, String e, int index) {
if (index == 0) {
return e;
}
return get(set, set.higher(e), --index);
}
This is the output:
[alpha, beta, gamma]
2
gamma
I didn't do any benchmarks with greater data-sets, but I guess it should perform quite decent. The higher() method should directly point to the next element in the tree.

I haven't been able to find any way other than iterating through the set "index" number of times. However, since we know the size of the set, we can speed that up to at least half the size by using both ascending and descending iteration:
public static <T> T getInNavigableSetByIndex(NavigableSet<T> set, int index) {
Objects.requireNonNull(set);
final int size = set.size();
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
final boolean descend = index >= size / 2;
final Iterator<T> itr = descend ? set.descendingIterator() : set.iterator();
final int stepCount = descend ? size - index : index + 1;
T object = null;
for (int i = 0; i < stepCount && itr.hasNext(); i++) {
object = itr.next();
}
return object;
}

Related

Cannot figure out why it throws ConcurrentModificationException

I've got the ConcurrentModificationException and do not know why. I know that trying to iterate through a list using for loops and deleting elements inside the loop block is bad idea and can throw such exception, but I have no idea how to fix it in my case.
private static final List<Integer> originalList = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
originalList.add(i);
}
final int MAX_GROUP_SIZE = 5;
int partitionSize = 4;
List<List<Integer>> partitions = new LinkedList<>();
for (int i = 0; i < originalList.size(); i += partitionSize) {
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
}
int lastGroupSize = partitions.get(partitions.size() - 1).size();
if (lastGroupSize < partitionSize && partitions.size() > lastGroupSize){
List<Integer> lastGroup = partitions.remove(partitions.size() - 1);
for (int i = 0; i < lastGroupSize; i++) {
partitions.get(i).add(lastGroup.get(i));
}
}
System.out.println("GROUPS: " + partitions.size());
printGroups(new LinkedList<>(partitions));
}
The problem is that your calls to subList() don't create new lists. As the javadoc says it:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
The javadoc also says:
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list.
When you call partitions.get(i).add(...), you're structurally modifying originalList, causing the error.
I don't believe you intended that, so to fix the problem, you just need to make sure your sub-lists are independent of the original list, i.e. copies, which is easy to do:
new ArrayList<>(originalList.subList(...))
Using the ArrayList(Collection) constructor will create a copy of the sub-list.
So, change this statement:
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
to this:
partitions.add(new ArrayList<>(originalList.subList(i,
Math.min(i + partitionSize, originalList.size()))));
You should never iterate the list and perform updating actions while doing so (where updating means adding or removing elements). This is a recipe for disaster.
In order to resolve this there are three possible scenarios to follow:
1) Copy the list, iterate over the copy and remove from the original one.
for (var number : new ArrayList<>(original)) {
if (element > 10) {
original.remove(element);
}
}
2) Use Streams
List<Integer> filtered = original.stream()
.filter(i -> i > 10)
.collect(Collectors.toList());
3) Use an iterator to loop over the list
Iterator<Integer> iterator = original.iterator();
while (iterator.hasNex()) {
Integer number = iterator.next();
if (number > 10) {
iterator.remove();
}
}
Personally I prefer streams.

How do I collect multiple maximum values from a List

How to get max from an ArrayList that has more than one max? For example, if an ArrrayList contains max = 20 stored at index 2, 3 and 6, how do you get all that indicies?
The obvious way is to first get maximum value by Collections.max(), then collect indicies where items are equal to max:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
if (input.isEmpty()) // avoid exception thrown by Collections.max() if input is empty
return Collections.emptyList();
final T max = Collections.max(input);
return IntStream.range(0, input.size())
.filter(i -> input.get(i).compareTo(max) == 0)
.boxed()
.collect(Collectors.toList());
}
Additionally, I'd like to propose another solution where iteration is performed only once. During iteration, you need to check two things for each item: 1) if it is greater than current max, set a new max and reset result list, 2) if it is equal to current max, add its index to result list:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
T max = null;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < input.size(); i++) {
T item = input.get(i);
if (max == null || item.compareTo(max) > 0) { // item > max => reset
res.clear();
max = item;
res.add(i);
} else if (item.compareTo(max) == 0) // item equals current max
res.add(i);
}
return res;
}
This won't give you value of max item itself, but you can get it by any returned index, simply as:
List<Integer> maxInd = maxIndicies(list);
maxValue = maxInd.isEmpty() ? null : list.get(maxInd.get(0));
This sounds like a homework for your programming course. You should do it yourself but anyway here is the solution.
private List<Integer> getAllMaxIndices(List<Integer> aList) {
List<Integer> result = new ArrayList<Integer>();
// check argument
if (aList == null || aList.isEmpty()) {
return result;
}
// initialize the list with the index of the first element
result.add(0);
Integer tmpInt;
Integer tmpFirstIndexOfMaxInt;
Integer tmpMaxInt;
for (int i = 0; i < aList.size(); i++) {
// save the current integer and the currently maximum integer
tmpInt = aList.get(i);
tmpFirstIndexOfMaxInt = result.get(0);
tmpMaxInt = aList.get(tmpFirstIndexOfMaxInt);
// if the current element is greater than the last found
if (tmpInt > tmpMaxInt) {
// empty the result
result.clear();
// start collecting indices again
result.add(i);
}
// if the current element is equal to the last found
else if (tmpInt.intValue() == tmpMaxInt.intValue()) {
// insert the current index in the result
result.add(i);
}
}
return result;
}
I will leave it to you to write the code which tests this function.
Another approach using streams. That solution assumes that you want to know how often the max occurs (not the indices).
public static Map.Entry<Integer, Long> getMaxWithOccurrences(
List<Integer> list) {
return list
.stream()
.collect(
Collectors.groupingBy(i -> i, TreeMap::new,
Collectors.counting())).lastEntry();
}
I'd use a simple and easy to read for loop.
public List<Integer> getMaxIndices(List<Integer> values) {
Integer max = Collections.max(values);
List<Integer> maxIndices = new ArrayList<>();
for (int i = 0; i < values.size(); i++) {
if (values.get(i).equals(max)) {
maxIndices.add(Integer.valueOf(i));
}
}
return maxIndices;
}
Integer maxValue = Collections.max(list);
int numberofMax = Collections.frequency(list, maxValue);
this "numberofMax" will return how many maximum values the "list" has.
usual max finders only store the maximum met value, here you will have to maintain a list of indexes matching the maximum value.
You can do it in following way:
public void findMaxIndices() {
//Your list with numbers
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9));
//Sorted Map which will contain key as numbers and value as list of indices where your 'key' exists in the list
SortedMap<Integer, List<Integer>> indexMapping = new TreeMap<Integer, List<Integer>>();
for(int i = 0; i< list.size(); i++) {
//Get the number at index i
int number = list.get(i);
//Check if any index corresponding to 'number' as index has been added to your map
List<Integer> mapping = indexMapping.get(number);
if(mapping == null) {
//instantiate the list if no index has been added yet
mapping = new ArrayList<Integer>();
//Key as your 'number'
indexMapping.put(number, mapping);
}
//Add the index of the 'number' to the mapping list, which is mapped by key as 'number'
mapping.add(i);
}
//Last key in sorted map will be your highest number in the list, get the value corresponding to it. Following prints: [8,17]
int maxNumber = indexMapping.lastKey(); //Maximum number found
System.out.println(indexMapping.get(maxNumber)); //Indices where maximum number exists
}
This way, you can also find indices with lowest values easily:
indexMapping.get(indexMapping.firstKey());

Convert a range of lists to sub list and store them in an list of type linkedlist

I already have a list type Integer with values in it and I want to test sequentially from index zero if the sum of one range of elements satisfy a particular value then copy this range in an list and store it in a list of linkedlist. Then again test sequentially but now from the following index of the previous range, so if the previous range was index 0 to index 9 then start at index 10, and repeat the process until the last index.
List<Integer> arrayB = new LinkedList<Integer>(); //this is the array with values in it
List<LinkedList> p = new LinkedList<LinkedList>();// this is the array of arrays
List<Integer> arrayA = new LinkedList<Integer>();// this is the range or the sub list of arrayB
public void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
for (int w : arrayB)
{
count = w + count;
arrayA.add(w);
if(count == n)
{
count = 0;
p.add((LinkedList) arrayA);
arrayA.clear();
}
}
}
However, this code fail when I call method clear in arrayA so is there any alternative to code with this logic regardless of the data structure used?
My understanding of the problem is the following:
There exists an array from which you would like to extract a certain range of values given that they satisfy some criteria. In this case, the criterion is that the range evaluates to some sum. Once this has been completed, you would like to repeat the process until all of the values in the original data-structure have been exhausted.
I will assume that your original data-structure is an array of integers, and that your resulting data-structure is a linkedlist of integer arrays.
One way to do it may be to keep a global counter that keeps track of the current index of the original array, such as something like the following:
int[] originalArray = {//list of numbers separated by commas};
LinkedList<Integer[]> resultingList = new LinkedList<>();
int currentIndex = 0;
public static void function(int totalSum) {
int currentSum = 0;
int initialIndex = currentIndex;
while((currentSum != totalSum) && (currentIndex < (originalArray.length - 1))) {
if(currentSum + initialArray[currentIndex] <= totalSum) {
currentSum += initialArray[currentIndex];
currentIndex++;
}
else {
break;
}
}
if(currentSum = totalSum) {
int[] arrayToAdd = new int[currentIndex - initialIndex - 1];
for(int i = 0; i < currentIndex - initialIndex; i++) {
arrayToAdd[i] = originalArray[initialIndex + i];
}
resultingList.add(arrayToAdd);
}
}
You are using the same list reference arrayA every time you add a sub list into p, every list element in p is pointing to the same arrayA . So when you call arrayA.clear(); You clear all the list elements in p.
To correct that, you need to create a new list object when you add a sublist to arrayA:
public static void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
LinkedList<Integer> subList = new LinkedList<>();
for (int w : arrayB) {
count = w + count;
subList.add(w);
if (count == n) {
count = 0;
p.add((LinkedList) subList); // p is adding a new list reference every time
subList = new LinkedList<>(); // create a new list object, subList points to a new list object
}
}
}
The issue is that when you add your linked list into the final storage p, you are assuming the elements of the list are put in there. Only a pointer is referenced, so when you clear it the next line, all the elements are gone.
p.add((LinkedList) arrayA);
arrayA.clear();
One tip is to move arrayA's scope to inside the function. This is because it's temporary, and only a sublist, so it shouldn't be at the instance level. It can be reused by doing a
arrayA = new LinkedList<Integer>();
and when doing so, you haven't lost the old list because p is keeping a reference to it.
Another tip is to name your lists with meaningful names.
originalIntList, groupedIntList, singleGroupIntList help the reader figure out what they could be doing more than a comment stating obvious aspects of the Java object.

How to add an element to an ArrayList

how can i make a user enter a number, which will then shift the array to the right 1. the array cant exceed 50. please help, thanks in advance :)
List<Integer> list = new ArrayList<Integer>(1);
public void add(int value) {
list.add(0, value);
for(int i = 0; i < array.length; i++) {
list.add(index, value); // how to make the elements shift to the right?
if(list.size > 50) {
list.remove(50);
}
}
}
List<Integer> list = new ArrayList<Integer>(50);
public void add(int value) {
if (list.size() == 50)
list.remove(list.size() -1);
list.add(value);
}
ArrayList shifts elements for you, that's why it has index, look at this answer.
When you create the ArrayList: new ArrayList<Integer>(50) 50 dont define size, define capacity of the ArrayList. When created is empty and size is 0.
List<Integer> list = new ArrayList<Integer>(50);
public void add(int value) {
if (list.size <= 50) list.remove(list.size() - 1);
// inserting element at position 0 shifts other elements
list.add(0, value);
}
public class TestList {
public static void main(String[] args) {
ArrayList<Integer> arrlist = new ArrayList<Integer>(4);
// use add() method to add elements in the list
arrlist.add(15);
arrlist.add(4);
arrlist.add(5);
// adding element 25 at third position
arrlist.add(2,25);
for (Integer number : arrlist) {
System.out.println("List Value = " + number);
}
}
}
Inserts the specified element at the specified position in this list. Shifts the element currently at that position (if any) and any subsequent elements to the right (adds one to their indices). From this "http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html"
so you need just to check if the size of your list is no longer than 50, and add the number in the specified index.
List<Integer> list = new ArrayList<Integer>(50);
public void add(int value) {
if (list.size() == 50) // if the size of the array is 50, then remove the last value.
list.remove(list.size() -1);
list.add(int index, E element);// you can even choose where position to insert your value.
}
In the constructor part what you have defined is the capacity. The default minimum capacity is 10. You know your array cant exceed to 50. There is a chance that there must be less element then 50. So first remain that constructor part empty.
List<Integer> list = new ArrayList<Integer>();
public void add(int value) {
if(list.size() <50)
list.add(0,value);
else
{
list.remove(list.size()-1);
list.add(0, value);
}
private List<Integer> list = new ArrayList<Integer>(51);
public void add(int value) {
list.add(0, value); //other elements are shifted right, you need do nothing else
//then limit the list to 50 elements
while(list.size() > 50) list.remove(list.size() - 1);
}
I can't see the rest of the code. I don't know what list length is before add so I'm just guaranteeing it's <= 50 after with a while.
You can specify an initial capacity, if you do, use 51 not 50. It gives the array an initial size that can hold your 50, plus the 51st which is in list for a short period before removal.

Split larger collection (Collections, Arrays, List) into smaller collections in Java and also keep track of last one returned

public Collection<Comment> getCommentCollection() {
commentCollection = movie.getCommentCollection();
return split((List<Comment>) commentCollection, 4);
}
public Collection<Comment> split(List<Comment> list, int size){
int numBatches = (list.size() / size) + 1;
Collection[] batches = new Collection[numBatches];
Collection<Comment> set = commentCollection;
for(int index = 0; index < numBatches; index++) {
int count = index + 1;
int fromIndex = Math.max(((count - 1) * size), 0);
int toIndex = Math.min((count * size), list.size());
batches[index] = list.subList(fromIndex, toIndex);
set = batches[index];
}
return set;
}
I am trying to split a bigger collection into smaller collections, depending on the number of items in the original collection. And then return one of the smaller collections every time the get method is called while keeping track of which smaller collection is returned. How can I achieve this?
Maybe I don't understand the question, but this is part of List:
List<E> subList(int fromIndex, int toIndex)
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.
This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:
list.subList(from, to).clear();
docs.oracle.com/javase/1.5.0/docs/api/java/util/List.html
This is simple: just use Lists.partition() from Guava. If I understand what you want correctly, it's exactly what it does.
private int runs = 0;
public void setRunsOneMore() {
runs++;
}
public void setRunsOneLess() {
runs--;
}
public Collection<Comment> getCommentCollection() {
commentCollection = movie.getCommentCollection();
Collection[] com = split((List<Comment>) commentCollection,4);
try{
return com[runs];
} catch(ArrayIndexOutOfBoundsException e) {
runs = 0;
}
return com[runs];
}
public Collection[] split(List<Comment> list, int size){
int numBatches = (list.size() / size) + 1;
Collection[] batches = new Collection[numBatches];
Collection<Comment> set = commentCollection;
for(int index = 0; index < numBatches; index++) {
int count = index + 1;
int fromIndex = Math.max(((count - 1) * size), 0);
int toIndex = Math.min((count * size), list.size());
batches[index] = list.subList(fromIndex, toIndex);
}
return batches;
}
Setting the current "run" with the next & previous button actions
public String userNext() {
userReset(false);
getUserPagingInfo().nextPage();
movieController.setRunsOneMore();
return "user_movie_detail";
}
public String userPrev() {
userReset(false);
getUserPagingInfo().previousPage();
movieController.setRunsOneLess();
return "user_movie_detail";
}
I'm not entirely sure what you're asking... do you want to remove the first 4 items from the source Collection before returning them, so that you get the next 4 the next time you call the method? If so, you could just use the Iterator:
Iterator<Comment> iter = commentCollection.iterator();
while (iter.hasNext() && group.size() < 4) {
group.add(iter.next());
iter.remove();
}
By doing this, though, you'd be destroying the movie object's collection of comments (unless it returns a copy of that collection each time, in which case the above wouldn't work at all). I'm guessing you're trying to do something like paging, in which case I'd suggest doing something different like partitioning a List of comments with size 4 and keeping track of a current index (the page) in that partition list.
public static <E extends Object> List<List<E>> split(Collection<E> input, int size) {\n
List<List<E>> master = new ArrayList<List<E>>();
if (input != null && input.size() > 0) {
List<E> col = new ArrayList<E>(input);
boolean done = false;
int startIndex = 0;
int endIndex = col.size() > size ? size : col.size();
while (!done) {
master.add(col.subList(startIndex, endIndex));
if (endIndex == col.size()) {
done = true;
}
else {
startIndex = endIndex;
endIndex = col.size() > (endIndex + size) ? (endIndex + size) : col.size();
}
}
}
return master;
}
You can create a separate sublist that is a deep copy of the original list using the ArrayList constructor.
import java.util.ArrayList;
import java.util.List;
class Scratch {
public static void main(String[] args) {
final List<String> parent = new ArrayList<>();
parent.add("One");
parent.add("Two");
parent.add("Three");
// using the ArrayList constructor here
final List<String> copy = new ArrayList<>(parent.subList(0, 2));
// modifying the new list doesn't affect the original
copy.remove(0);
// outputs:
// parent: [One, Two, Three]
// copy: [Two]
System.out.println("parent: " + parent);
System.out.println("copy: " + copy);
}
}
You can use Vector.remove(collection), example:
public Collection<Comment> getCommentCollection() {
commentCollection = movie.getCommentCollection();
Vector<Comment> group = new Vector<Comment>();
for (Comment com:commentCollection){
group.add(com);
if(group.size() == 4){
break;
}
}
movie.getCommentCollection().remove(commentCollection);
return commentCollection;
}
assuming movie.getCommentCollection() is also a vector
here is my implementation. hope it helps!
dependencies CollectionUtils and Lists to see:
https://mvnrepository.com/artifact/org.apache.commons/commons-lang3/
/**
* efficient collection partition
*
* #param baseCollection base collection to split
* #param maxSize max element size of each sublist returned
* #param balancing whether each of sublists returned needs size balancing
* #return list of sublists, whose order bases on the base collection's iterator implementation
* #since 2020/03/12
*/
public static <T> List<List<T>> partition(final Collection<T> baseCollection, int maxSize, boolean balancing) {
if (CollectionUtils.isEmpty(baseCollection)) {
return Collections.emptyList();
}
int size = baseCollection.size() % maxSize == 0 ? baseCollection.size()/maxSize : baseCollection.size()/maxSize+1;
if (balancing) {
maxSize = baseCollection.size() % size == 0 ? baseCollection.size()/size : baseCollection.size()/size+1;
}
int fullElementSize = baseCollection.size() % size == 0 ? size : baseCollection.size() % size;
List<List<T>> result = Lists.newArrayListWithExpectedSize(size);
Iterator<T> it = baseCollection.iterator();
for (int i = 0; i < size; i++) {
if (balancing && i == fullElementSize) {
maxSize--;
}
maxSize = Math.min(maxSize, baseCollection.size()-i*maxSize);
List<T> subList = Lists.newArrayListWithExpectedSize(maxSize);
for (int i1 = 0; i1 < maxSize; i1++) {
if (it.hasNext()) {
subList.add(it.next());
} else {
break;
}
}
result.add(subList);
}
return result;
}

Categories