Return Iterator without Remove method - java

I have multiple lists and for a specific list (in the example list b), I must return an iterator which doesn't have/allow remove method, I must preserve the information:
public static void main(String[] args){
List<E> a = new ArrayList<E>;
List<E> b = new ArrayList<E>;
b.add("A");
b.add("B");
b.add("C");
myMethod();
}
public Iterator<E> myMethod(){
return b.iterator();
}
I can't change the "public Iterator myMethod()", how is possible to return the iterator for b, without Remove method?

return Collections.unmodifiableList(b).iterator();
This seems to me to be the easy way out. The unmodifiable view on your list ensures that the iterator you get cannot modify the list, that is, the remove method will not work and should throw an exception, an UnsupportedOperationException.
EDIT: Since you can’t change myMethod, there are two options, or really, variants:
Make b unmodifiable altogether. This will only be acceptable if you don’t need to make modifications of b at all, of course.
Make b an unmodifiable view onto a list that can be modified through some other view of the same list.
Code example for 1., in Java 8 and earlier:
List<String> b = Collections.unmodifiableList(Arrays.asList("A", "B", "C"));
In Java 9 and later it’s simpler:
List<String> b = List.of("A", "B", "C");
List.of produces an unmodifiable list.
In case a completely unmodifiable list doesn’t fit your requirements, here a suggestion for code fro option 2.:
List<String> modifiableB = new ArrayList(Arrays.asList("A", "B", "C"));
List<String> b = Collections.unmodifiableList(modifiableB);
Now myMethod will access b, that is, the unmodifiable view, and therefore produce an iterator that cannot remove elements, whereas other methods can modify b through modifiableB.

Just return the iterator of an unmodifiable list wrapping the original list:
return Collections.unmodifiableList(b).iterator();

Related

LinkedHashSet - insertion order and duplicates - keep newest "on top"

I need a collection that keeps insertion order and has unique values. LinkedHashSet looks like the way to go, but there's one problem - when two items are equal, it removes the newest one (which makes sense), here's an example:
set.add("one");
set.add("two");
set.add("three");
set.add("two");
The LinkedHashSet will print:
one, two, three
But what I need is:
one, three, two
What would be the best solution here? Is there any collection/collections method that can do this or should I implement it manually?
Most of the Java Collections can be extended for tweaking.
Subclass LinkedHashSet, overriding the add method.
class TweakedHashSet<T> extends LinkedHashSet<T> {
#Override
public boolean add(T e) {
// Get rid of old one.
boolean wasThere = remove(e);
// Add it.
super.add(e);
// Contract is "true if this set did not already contain the specified element"
return !wasThere;
}
}
You can simply use a special feature of LinkedHashMap:
Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]
In Oracle’s JRE the LinkedHashSet is backed by a LinkedHashMap anyway, so there’s not much functional difference, but the special constructor used here configures the LinkedHashMap to change the order on every access not only on insertion. This might sound as being too much, but in fact affects the insertion of already contained keys (values in the sense of the Set) only. The other affected Map operations (namely get) are not used by the returned Set.
If you’re not using Java 8, you have to help the compiler a bit due to the limited type inference:
Set<String> set
= Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));
but the functionality is the same.
When initializing you're LinkedHashSet you could override the add method.
Set<String> set = new LinkedHashSet<String>(){
#Override
public boolean add(String s) {
if(contains(s))
remove(s);
return super.add(s);
}
};
Now it gives you:
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));
// [3, 1 ,2]
even the addAll method is working.
All solution provided above are excellent but if we don't want to override already implemented collections. We can solve this problem simply by using an ArrayList with a little trick
We can create a method which you will use to insert data into your list
public static <T> void addToList(List<T> list, T element) {
list.remove(element); // Will remove element from list, if list contains it
list.add(element); // Will add element again to the list
}
And we can call this method to add element to our list
List<String> list = new ArrayList<>();
addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
Only disadvantage here is we need to call our custom addToList() method everytime instead of list.add()

remove one Collection elements from other

Hi i have two Collection of SomeType a1,a2 and want to remove all the elements of a2 from a1.
Please suggestion which type of Collection i need to use :
ArrayList
LinkList
some other ?.
Is there any library for this ?
Thanks to all.
After reading your response i created a Filter class like this :
public class Filter {
public <T> Set<T> filter(Set<T> all, Set<T> blocked) {
for (T t : all) {
if(blocked.contains(t)) {
all.remove(t);
}
}
return all;
}
}
Use the collection method Collection.removeAll(Collection<?> c);
Well, you can use a1.removeAll(a2), but the removal would be more efficient if your Collections are HashSet (since the search for an element in a HashSet takes O(1), while in Lists it takes O(n)). Whether you can use HashSet depends on whether a1 and a2 can contain duplicate elements.
To remove from a collection you need to have objects(in your case SomeType) that override equals and hashCode.
Then you don't need a library, just use the removeAll method
Collection<SomeType> a1 = new ArrayList<SomeType>();
Collection<SomeType> a2 = new ArrayList<SomeType>();
a1.removeAll(a2);

How to sort a HashSet?

For lists, we use the Collections.sort(List) method. What if we want to sort a HashSet?
A HashSet does not guarantee any order of its elements. If you need this guarantee, consider using a TreeSet to hold your elements.
However if you just need your elements sorted for this one occurrence, then just temporarily create a List and sort that:
Set<?> yourHashSet = new HashSet<>();
...
List<?> sortedList = new ArrayList<>(yourHashSet);
Collections.sort(sortedList);
Add all your objects to the TreeSet, you will get a sorted Set. Below is a raw example.
HashSet myHashSet = new HashSet();
myHashSet.add(1);
myHashSet.add(23);
myHashSet.add(45);
myHashSet.add(12);
TreeSet myTreeSet = new TreeSet();
myTreeSet.addAll(myHashSet);
System.out.println(myTreeSet); // Prints [1, 12, 23, 45]
Update
You can also use TreeSet's constructor that takes a HashSet as a parameter.
HashSet myHashSet = new HashSet();
myHashSet.add(1);
myHashSet.add(23);
myHashSet.add(45);
myHashSet.add(12);
TreeSet myTreeSet = new TreeSet(myHashSet);
System.out.println(myTreeSet); // Prints [1, 12, 23, 45]
Thanks #mounika for the update.
Java 8 way to sort it would be:
fooHashSet.stream()
.sorted(Comparator.comparing(Foo::getSize)) //comparator - how you want to sort it
.collect(Collectors.toList()); //collector - what you want to collect it to
*Foo::getSize it's an example how to sort the HashSet of YourItem's naturally by size.
*Collectors.toList() is going to collect the result of sorting into a List the you will need to capture it with List<Foo> sortedListOfFoo =
You can use a TreeSet instead.
Use java.util.TreeSet as the actual object. When you iterate over this collection, the values come back in a well-defined order.
If you use java.util.HashSet then the order depends on an internal hash function which is almost certainly not lexicographic (based on content).
Just in-case you don't wanna use a TreeSet you could try this using java stream for concise code.
set = set.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new));
You can use Java 8 collectors and TreeSet
list.stream().collect(Collectors.toCollection(TreeSet::new))
Based on the answer given by #LazerBanana i will put my own example of a Set sorted by the Id of the Object:
Set<Clazz> yourSet = [...];
yourSet.stream().sorted(new Comparator<Clazz>() {
#Override
public int compare(Clazz o1, Clazz o2) {
return o1.getId().compareTo(o2.getId());
}
}).collect(Collectors.toList()); // Returns the sorted List (using toSet() wont work)
Elements in HashSet can't be sorted. Whenever you put elements into HashSet, it can mess up the ordering of the whole set. It is deliberately designed like that for performance. When you don't care about the order, HashSet will be the most efficient set for frequent insertions and queries.
TreeSet is the alternative that you can use. When you iterate on the tree set, you will get sorted elements automatically.
But it will adjust the tree to try to remain sorted every time you insert an element.
Perhaps, what you are trying to do is to sort just once. In that case, TreeSet is not the most efficient option because it needs to determine the placing of newly added elements all the time. Use TreeSet only when you want to sort often.
If you only need to sort once, use ArrayList. Create a new list and add all the elements then sort it once. If you want to retain only unique elements (remove all duplicates), then put the list into a LinkedHashSet, it will retain the order you have already sorted.
List<Integer> list = new ArrayList<>();
list.add(6);
list.add(4);
list.add(4);
list.add(5);
Collections.sort(list);
Set<Integer> unique = new LinkedHashSet<>(list); // 4 5 6
Now, you've gotten a sorted set if you want it in a list form then convert it into list.
You can use TreeSet as mentioned in other answers.
Here's a little more elaboration on how to use it:
TreeSet<String> ts = new TreeSet<String>();
ts.add("b1");
ts.add("b3");
ts.add("b2");
ts.add("a1");
ts.add("a2");
System.out.println(ts);
for (String s: ts)
System.out.println(s);
Output:
[a1, a2, a3, a4, a5]
a1
a2
b1
b2
b3
In my humble opinion , LazerBanana's answer should be the top rated answer & accepted because all the other answers pointing to java.util.TreeSet ( or first convert to list then call Collections.sort(...) on the converted list ) didn't bothered to ask OP as what kind of objects your HashSet has i.e. if those elements have a predefined natural ordering or not & that is not optional question but a mandatory question.
You just can't go in & start putting your HashSet elements into a TreeSet if element type doesn't already implement Comparable interface or if you are not explicitly passing Comparator to TreeSet constructor.
From TreeSet JavaDoc ,
Constructs a new, empty tree set, sorted according to the natural
ordering of its elements. All elements inserted into the set must
implement the Comparable interface. Furthermore, all such elements
must be mutually comparable: e1.compareTo(e2) must not throw a
ClassCastException for any elements e1 and e2 in the set. If the user
attempts to add an element to the set that violates this constraint
(for example, the user attempts to add a string element to a set whose
elements are integers), the add call will throw a ClassCastException.
That is why only all Java8 stream based answers - where you define your comparator on the spot - only make sense because implementing comparable in POJO becomes optional. Programmer defines comparator as and when needed. Trying to collect into TreeSet without asking this fundamental question is also incorrect ( Ninja's answer). Assuming object types to be String or Integer is also incorrect.
Having said that, other concerns like ,
Sorting Performance
Memory Foot Print ( retaining original set and creating new sorted sets each time sorting is done or wish to sort the set in - place etc etc )
should be the other relevant points too. Just pointing to API shouldn't be only intention.
Since Original set already contains only unique elements & that constraint is also maintained by sorted set so original set needs to be cleared from memory since data is duplicated.
1. Add all set element in list -> al.addAll(s);
2. Sort all the elements in list using -> Collections.sort(al);
public class SortSetProblem {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList();
Set<String> s = new HashSet<>();
s.add("ved");
s.add("prakash");
s.add("sharma");
s.add("apple");
s.add("ved");
s.add("banana");
System.out.println("Before Sorting");
for (String s1 : s) {
System.out.print(" " + s1);
}
System.out.println("After Sorting");
al.addAll(s);
Collections.sort(al);
for (String set : al) {
System.out.print(" " + set);
}
}
}
input - ved prakash sharma apple ved banana
Output - apple banana prakash sharma ved
If you want want the end Collection to be in the form of Set and if you want to define your own natural order rather than that of TreeSet then -
Convert the HashSet into List
Custom sort the List using Comparator
Convert back the List into LinkedHashSet to maintain order
Display the LinkedHashSet
Sample program -
package demo31;
import java.util.*;
public class App26 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
addElements(set);
List<String> list = new LinkedList<>();
list = convertToList(set);
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
int flag = s2.length() - s1.length();
if(flag != 0) {
return flag;
} else {
return -s1.compareTo(s2);
}
}
});
Set<String> set2 = new LinkedHashSet<>();
set2 = convertToSet(list);
displayElements(set2);
}
public static void addElements(Set<String> set) {
set.add("Hippopotamus");
set.add("Rhinocerous");
set.add("Zebra");
set.add("Tiger");
set.add("Giraffe");
set.add("Cheetah");
set.add("Wolf");
set.add("Fox");
set.add("Dog");
set.add("Cat");
}
public static List<String> convertToList(Set<String> set) {
List<String> list = new LinkedList<>();
for(String element: set) {
list.add(element);
}
return list;
}
public static Set<String> convertToSet(List<String> list) {
Set<String> set = new LinkedHashSet<>();
for(String element: list) {
set.add(element);
}
return set;
}
public static void displayElements(Set<String> set) {
System.out.println(set);
}
}
Output -
[Hippopotamus, Rhinocerous, Giraffe, Cheetah, Zebra, Tiger, Wolf, Fox, Dog, Cat]
Here the collection has been sorted as -
First - Descending order of String length
Second - Descending order of String alphabetical hierarchy
you can do this in the following ways:
Method 1:
Create a list and store all the hashset values into it
sort the list using Collections.sort()
Store the list back into LinkedHashSet as it preserves the insertion order
Method 2:
Create a treeSet and store all the values into it.
Method 2 is more preferable because the other method consumes lot of time to transfer data back and forth between hashset and list.
We can not decide that the elements of a HashSet would be sorted automatically. But we can sort them by converting into TreeSet or any List like ArrayList or LinkedList etc.
// Create a TreeSet object of class E
TreeSet<E> ts = new TreeSet<E> ();
// Convert your HashSet into TreeSet
ts.addAll(yourHashSet);
System.out.println(ts.toString() + "\t Sorted Automatically");
You can use guava library for the same
Set<String> sortedSet = FluentIterable.from(myHashSet).toSortedSet(new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
// descending order of relevance
//required code
}
});
SortedSet has been added Since java 7
https://docs.oracle.com/javase/8/docs/api/java/util/SortedSet.html
You can wrap it in a TreeSet like this:
Set mySet = new HashSet();
mySet.add(4);
mySet.add(5);
mySet.add(3);
mySet.add(1);
System.out.println("mySet items "+ mySet);
TreeSet treeSet = new TreeSet(mySet);
System.out.println("treeSet items "+ treeSet);
output :
mySet items [1, 3, 4, 5]
treeSet items [1, 3, 4, 5]
Set mySet = new HashSet();
mySet.add("five");
mySet.add("elf");
mySet.add("four");
mySet.add("six");
mySet.add("two");
System.out.println("mySet items "+ mySet);
TreeSet treeSet = new TreeSet(mySet);
System.out.println("treeSet items "+ treeSet);
output:
mySet items [six, four, five, two, elf]
treeSet items [elf, five, four, six, two]
requirement for this method is that the objects of the set/list should be comparable (implement the Comparable interface)
The below is my sample code and its already answered by pointing the code in comments , am still sharing because it contains the complete code
package Collections;
import java.util.*;
public class TestSet {
public static void main(String[] args) {
Set<String> objset = new HashSet<>();
objset.add("test");
objset.add("abc");
objset.add("abc");
objset.add("mas");
objset.add("vas");
Iterator itset = objset.iterator();
while(itset.hasNext())
{
System.out.println(itset.next());
}
TreeSet<String> treeobj = new TreeSet(objset);
System.out.println(treeobj);
}
}
TreeSet treeobj = new TreeSet(objset); here we are invoking the treeset constructor which will call the addAll method to add the objects .
See this below code from the TreeSet class how its mentioned ,
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
Convert HashSet to List then sort it using Collection.sort()
List<String> list = new ArrayList<String>(hset);
Collections.sort(List)
This simple command did the trick for me:
myHashSet.toList.sorted
I used this within a print statement, so if you need to actually persist the ordering, you may need to use TreeSets or other structures proposed on this thread.

How to subtract collections with Comparator interface instead of overriding equals

I want to compute differences between collections. When using CollectionUtils.subtract() for custom comparison I need to override the object's equals() method. But what if I need to compare collections of objects of the same type but different comparison criterion? What about the Comparator interface, it seems perfectly suited here? AFAIK Comparator is mainly used for sorting. Isn't there a method that uses Comparators for subtracting?
static <Type> Collection<Type> subtract(Collection<Type> a, Collection<Type> b, Comparator<Type> c) {
Set<Type> subtrahend = new TreeSet<Type>(c);
subtrahend.addAll(b);
Collection<Type> result = new ArrayList<Type>();
for (Type item: a) {
if (!subtrahend.contains(item)) result.add(item);
}
return result;
}
The subtrahent tree-set is not necessary, but will improve performance for large b.
If you have an ArrayList, multiple removes can be more expensive than taking a copy.
List<Type> list = /* ArrayList */
Set<Type> toRemove = /* HashSet */
List<Type> copy = new ArrayList<Type>(list.size());
for(Type t: list)
if(!toRemove.contains(t))
copy.add(t);
list = copy;
Personally I would use a loop. Its likely to be shorter and clearer.
Collection<Type> collection =
for(Iterator<Type> i=collection.iterator(); i.hasNext();)
if (i.next() is to be removed)
i.remove();
The reason an Iterator is used explicitly is to use the Iterator.remove() which avoids a ConcurrentModificationException. Another way to avoid it is to use a copy of the collection which might be preferred.
for(Type t : new ArrayList<Type>(collection))
if (t is to be removed)
collection.remove(t);
This doesn't perform as well but may perform well enough.

is there a better alternative to List<T> initalization than invoking Arrays.asList?

Is there a better alternative to using Arrays.asList as a List bulk initializer? Of concern is that this one is verbose and involves an extraneous class and method.
List<Double> myList = new ArrayList<Double>(Arrays.asList(3.01d, 4.02d, 5.03d));
Edit: This question pertains to a bulk initialization which would usually have more than the three values shown in the example.
If you know that you won't need to add anything to the list later, you can just do
List<Double> myList = Arrays.asList(3.01d, 4.02d, 5.03d);
I'm pretty sure the list returned from Arrays.asList can be modified, but only in that you can change the elements that are there -- you can't add new elements to it.
Use guava,
List<Double> myList = Lists.newArrayList(3.01d, 4.02d, 5.03d));
Doesn't match the question 100%, but adding this answer in case the wanting to instatiate a List was just to be able to immediately do something with the values in it, vs. just instantiating a List for the sake of just doing the instantiation alone.
With Java 8 you can use Stream.of(T... values), then manipulate the steam via the stream API to get the results you would want from the list of values.
For example, to get the max value of a series of values ...
int maxValue = Stream.of(10, 5, 25).max(Integer::compareTo).get();
The above example is also helpful for when you want Math.max(#,#) type functionality, but have more than two arguments to process.
Since java 9 you are able to use List.of factory method:
static <E> List<E> of​(E... elements)
Returns an immutable list containing an arbitrary number of elements.
Or use guava:
public static <E> ImmutableList<E> of(E e1, E e2,...)
Returns an immutable list containing the given elements, in order.
Yes, you can do it like this:
List<Double> myList = Arrays.asList(new Double[]{3.01d, 4.02d, 5.03d});
// or
List<Double> myList = Arrays.asList(3.01d, 4.02d, 5.03d);
Another option would be as an anonymous inner class:
List<Double> myList = new ArrayList() {
{
add(3.01d);
add(4.02d);
add(5.03d);
}
};

Categories

Resources