Related
This question already has answers here:
How do I remove an object from an ArrayList in Java?
(9 answers)
Closed 5 years ago.
I have a List object which holds 10 elements, i have to do conditional check and remove some of the elements from the list. I have done as below, but it is not working as expected, any inputs?
List<MyDTO> myList = new ArrayList<MyDTO>(); //myist.size () is 10
I want to check as below:
for(MyDTO res : myList){
if(res.getResult().equals("cancel")){
myList.remove()
}
}
As shown in above code, if res.getResult() is "cancel" i want to remove that particular object from the list(myList). Is it the correct way to remove an element completely from list based on conditional check?
Simply use removeIf on your list, for example if you had list of Integers ranging from 1 to 10 and you wanted to remove Integers larger than 4, you would do:
yourListHere.removeIf(x -> x > 4);
Resulting list would contain: 1, 2, 3 and 4
Read here about removeIf:
https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-
BONUS
Additional resources if you are unfamiliar with Java 8 features:
Lambdas - https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
Functional Interfaces - https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Use removeIf:
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given
predicate. Errors or runtime exceptions thrown during iteration or by
the predicate are relayed to the caller.
In your example:
myList.removeIf(res -> res.getResult().equals("cancel"));
As an alternative to removeIf you can use the Java stream solution:
List<MyDTO> newList = myList.stream()
.filter(s -> !s.getResult().equals("cancel"))
.collect(Collectors.toList());
This might be more convenient when the length of the input list and the number of elements to remove is relatively large.
If use Java 8:
myList.removeIf(x -> x.getResult().equals("cancel"));
If older (6,7):
public class MyDTO {
private String result;
(...)
public MyDTO(String result) {
this.result = result;
}
public String getResult() {
return result;
}
(...)
#Override
public int hashCode() {
return 31;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MyDTO other = (MyDTO) obj;
return !((this.result == null) ? (other.result != null) : !this.result.equals(other.result));
}
(...)
}
then:
MyDTO toRemove = new MyDTO("cancel");
while(myList.contains(toRemove))
myList.remove(toRemove);
or:
List<MyDTO> toRemove = new ArrayList<MyDTO>();
for(MyDTO res : myList){
if(res.getResult().equals("cancel")){
toRemove.add(res);
}
}
myList.removeAll(toRemove);
Without overriding the equals method:
for(int i = 0; i < myList.size() ; i++)
if(myList.get(i).getResult().equals("cancel"))
myList.remove(i);
or:
Iterator<MyDTO> i = myList.iterator();
while (i.hasNext())
if(i.next().getResult().equals("cancel"))
i.remove();
I'm developing a Java Application that reads a lot of strings data likes this:
1 cat (first read)
2 dog
3 fish
4 dog
5 fish
6 dog
7 dog
8 cat
9 horse
...(last read)
I need a way to keep all couple [string, occurrences] in order from last read to first read.
string occurrences
horse 1 (first print)
cat 2
dog 4
fish 2 (last print)
Actually i use two list:
1) List<string> input; where i add all data
In my example:
input.add("cat");
input.add("dog");
input.add("fish");
...
2)List<string> possibilities; where I insert the strings once in this way:
if(possibilities.contains("cat")){
possibilities.remove("cat");
}
possibilities.add("cat");
In this way I've got a sorted list where all possibilities.
I use it like that:
int occurrence;
for(String possible:possibilities){
occurrence = Collections.frequency(input, possible);
System.out.println(possible + " " + occurrence);
}
That trick works good but it's too slow(i've got millions of input)... any help?
(English isn’t my first language, so please excuse any mistakes.)
Use a Map<String, Integer>, as #radoslaw pointed, to keep the insertion sorting use LinkedHashMap and not a TreeMap as described here:
LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the elements.
Imagine you have all the strings in some array, call it listOfAllStrings, iterate over this array and use the string as key in your map, if it does not exists, put in the map, if it exists, sum 1 to actual result...
Map<String, Integer> results = new LinkedHashMap<String, Integer>();
for (String s : listOfAllStrings) {
if (results.get(s) != null) {
results.put(s, results.get(s) + 1);
} else {
results.put(s, 1);
}
}
Make use of a TreeMap, which will keep ordering on the keys as specified by the compare of your MyStringComparator class handling MyString class which wraps String adding insertion indexes, like this:
// this better be immutable
class MyString {
private MyString() {}
public static MyString valueOf(String s, Long l) { ... }
private String string;
private Long index;
public hashcode(){ return string.hashcode(); }
public boolean equals() { // return rely on string.equals() }
}
class MyStringComparator implements Comparator<MyString> {
public int compare(MyString s1, MyString s2) {
return -s1.getIndex().compareTo(s2.gtIndex());
}
}
Pass the comparator while constructing the map:
Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator());
Then, while parsing your input, do
Long counter = 0;
while (...) {
MyString item = MyString.valueOf(readString, counter++);
if (map.contains(item)) {
map.put(map.get(item)+1);
} else {
map.put(item,1);
}
}
There will be a lot of instantiation because of the immutable class, and the comparator will not be consistent with equals, but it should work.
Disclaimer: this is untested code just to show what I'd do, I'll come back and recheck it when I get my hands on a compiler.
Here is the complete solution for your problem,
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DataDto implements Comparable<DataDto>{
public int count = 0;
public String string;
public long lastSeenTime;
public DataDto(String string) {
this.string = string;
this.lastSeenTime = System.currentTimeMillis();
}
public boolean equals(Object object) {
if(object != null && object instanceof DataDto) {
DataDto temp = (DataDto) object;
if(temp.string != null && temp.string.equals(this.string)) {
return true;
}
}
return false;
}
public int hashcode() {
return string.hashCode();
}
public int compareTo(DataDto o) {
if(o != null) {
return o.lastSeenTime < this.lastSeenTime ? -1 : 1;
}
return 0;
}
public String toString() {
return this.string + " : " + this.count;
}
public static final void main(String[] args) {
String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"};
Map<String, DataDto> results = new HashMap<String, DataDto>();
for (String s : listOfAllStrings) {
DataDto dataDto = results.get(s);
if(dataDto != null) {
dataDto.count = dataDto.count + 1;
dataDto.lastSeenTime = System.nanoTime();
} else {
dataDto = new DataDto(s);
results.put(s, dataDto);
}
}
List<DataDto> finalResults = new ArrayList<DataDto>(results.values());
System.out.println(finalResults);
Collections.sort(finalResults);
System.out.println(finalResults);
}
}
Ans
[horse : 1, cat : 2, fish : 2, dog : 1]
[fish : 2, horse : 1, cat : 2, dog : 1]
I think this solution will be suitable for your requirement.
If you know that your data is not going to exceed your memory capacity when you read it all into memory, then the solution is simple - using a LinkedList or a and a LinkedHashMap.
For example, if you use a Linked list:
LinkedList<String> input = new LinkedList();
You then proceed to use input.add() as you did originally. But when the input list is full, you basically use Jordi Castilla's solution - but put the entries in the linked list in reverse order. To do that, you do:
Iterator<String> iter = list.descendingIterator();
LinkedHashMap<String,Integer> map = new LinkedHashMap<>();
while (iter.hasNext()) {
String s = iter.next();
if ( map.containsKey(s)) {
map.put( s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Now, the only real difference between his solution and mine is that I'm using list.descendingIterator() which is a method in LinkedList that gives you the entries in backwards order, from "horse" to "cat".
The LinkedHashMap will keep the proper order - whatever was entered first will be printed first, and because we entered things in reverse order, then whatever was read last will be printed first. So if you print your map the result will be:
{horse=1, cat=2, dog=4, fish=2}
If you have a very long file, and you can't load the entire list of strings into memory, you had better keep just the map of frequencies. In this case, in order to keep the order of entry, we'll use an object such as this:
private static class Entry implements Comparable<Entry> {
private static long nextOrder = Long.MIN_VALUE;
private String str;
private int frequency = 1;
private long order = nextOrder++;
public Entry(String str) {
this.str = str;
}
public String getString() {
return str;
}
public int getFrequency() {
return frequency;
}
public void updateEntry() {
frequency++;
order = nextOrder++;
}
#Override
public int compareTo(Entry e) {
if ( order > e.order )
return -1;
if ( order < e.order )
return 1;
return 0;
}
#Override
public String toString() {
return String.format( "%s: %d", str, frequency );
}
}
The trick here is that every time you update the entry (add one to the frequency), it also updates the order. But the compareTo() method orders Entry objects from high order (updated/inserted later) to low order (updated/inserted earlier).
Now you can use a simple HashMap<String,Entry> to store the information as you read it (I'm assuming you are reading from some sort of scanner):
Map<String,Entry> m = new HashMap<>();
while ( scanner.hasNextLine() ) {
String str = scanner.nextLine();
Entry entry = m.get(str);
if ( entry == null ) {
entry = new Entry(str);
m.put(str, entry);
} else {
entry.updateEntry();
}
}
Scanner.close();
Now you can sort the values of the entries:
List<Entry> orderedList = new ArrayList<Entry>(m.values());
m = null;
Collections.sort(orderedList);
Running System.out.println(orderedList) will give you:
[horse: 1, cat: 2, dog: 4, fish: 2]
In principle, you could use a TreeMap whose keys contained the "order" stuff, rather than a plain HashMap like this followed by sorting, but I prefer not having either mutable keys in a map, nor changing the keys constantly. Here we are only changing the values as we fill the map, and each key is inserted into the map only once.
What you could do:
Reverse the order of the list using
Collections.reverse(input). This runs in linear time - O(n);
Create a Set from the input list. A Set garantees uniqueness.
To preserve insertion order, you'll need a LinkedHashSet;
Iterate over this set, just as you did above.
Code:
/* I don't know what logic you use to create the input list,
* so I'm using your input example. */
List<String> input = Arrays.asList("cat", "dog", "fish", "dog",
"fish", "dog", "dog", "cat", "horse");
/* by the way, this changes the input list!
* Copy it in case you need to preserve the original input. */
Collections.reverse(input);
Set<String> possibilities = new LinkedHashSet<String>(strings);
for (String s : possibilities) {
System.out.println(s + " " + Collections.frequency(strings, s));
}
Output:
horse 1
cat 2
dog 4
fish 2
This question is from an assignment. I have to override a toString() method in a class that creates a circularly linked list and I actually have a toString() method that works great, it passes all of my tests everything. So my project is autograded and it apparently doesn't approve of my method 100%. So my question is: is there a better way to write this toString() method that would be more efficient?
public String toString()
{
if (size == 0)
{
return "[]";
}
else
{
String output = "";
Node<E> tempNode = actualElement;
while (tempNode.next() != actualElement)
{
if (output.equals(""))
{
output = "[" + output + tempNode.data().toString();
tempNode = tempNode.next();
}
else
{
output = output + ", " + tempNode.data().toString();
tempNode = tempNode.next();
}
}
output = output + ", " + tempNode.data().toString() + "]";
return output;
}
If i need to elaborate more on the class structure so that this makes more sense let me know.
Use StringBuilder.
StringBuilder builder = new StringBuilder();
builder.append("some text");
builder.append("more text");
return builder.toString();
To improve it further, you can use StringBuilder and append each computed String literals. This saves JVM creating load of individual String literals and thus improves performance.
Strings should always be used unless string builders offer an advantage in terms of simpler code or better performance
if you need to concatenate a large number of strings, appending to a StringBuilder object is more efficient.
I assume actualElement is defined elsewhere in the class, though a better name might be nice. The if (output.equals("")) is unnecessary. Just start the output StringBuilder with a [, and just append to it.
However, you are depending on your list actually being circular. If this list ends up not looping around, you will get an NPE. And, if the list looks more like a 6, as in [A, B, C, D, E, C, D, E...], then the loop will never end.
Use StringBuilder instead may do your a favor.This is snippet is copied from AbstractCollection.toString(),take a look at it.
public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
First you should use a StringBuilder for concatenation of your Strings.
take a look here:
http://javarevisited.blogspot.co.at/2011/07/string-vs-stringbuffer-vs-stringbuilder.html
StringBuilder sb = new StringBuidler();
Node<E> tempNode = actualElement;
while (tempNode.next() != actualElement)
{
if (sb.length() == 0)
{
sb.append("[").append(tempNode.data().toString());
}
else
{
sb.append(", ").append(tempNode.data().toString());
}
tempNode = tempNode.next();
}
sb.append(", ").append(tempNode.data().toString()).append("]");
return sb.toString();
I want to skip a certain request parameter using Enumeration. I use the below code but it didn't give me the desired result. Can any one tell how would I skip an element from Enumeration or whats wrong with the below code?
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
if("James".equalsIgnoreCase(e.nextElement().toString())) {
e.nextElement();
continue;
} else {
list.add(e.nextElement().toString());
}
}
You are calling nextElement() multiple times per loop skipping multiple elements. You need to only call nextElement() once. Something like...
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
String value = e.nextElement();
if(!"James".equalsIgnoreCase(value)) {
list.add(value);
}
}
Problem is, you are invoking e.nextElement() two times in your if. That will consume two elements.
You should store the element in a String type first, and then do the comparison: -
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
String elem = e.nextElement();
if("James".equalsIgnoreCase(elem)) {
continue;
} else {
list.add(elem);
}
}
And you don't need that toString() after e.nextElement(). It will give you String only, as you are using generic type.
As a side note, I would prefer to use a while loop in this case, as number of iteration is not fixed. Below is the equivalent while loop version for your for-loop: -
{
Enumeration<String> e = request.getParameterNames();
while (e.hasMoreElements()) {
String elem = e.nextElement();
if(!"James".equalsIgnoreCase(elem)) {
list.add(elem);
}
}
}
This question already has answers here:
Java: convert List<String> to a join()d String
(23 answers)
Closed 6 years ago.
What is the best way to concatenate a list of String objects? I am thinking of doing this way:
List<String> sList = new ArrayList<String>();
// add elements
if (sList != null)
{
String listString = sList.toString();
listString = listString.subString(1, listString.length() - 1);
}
I somehow found this to be neater than using the StringBuilder/StringBuffer approach.
Any thoughts/comments?
Use one of the the StringUtils.join methods in Apache Commons Lang.
import org.apache.commons.lang3.StringUtils;
String result = StringUtils.join(list, ", ");
If you are fortunate enough to be using Java 8, then it's even easier...just use String.join
String result = String.join(", ", list);
Using Java 8+
String str = list.stream().collect(Collectors.joining())
or even
String str = String.join("", list);
Your approach is dependent on Java's ArrayList#toString() implementation.
While the implementation is documented in the Java API and very unlikely to change, there's a chance it could. It's far more reliable to implement this yourself (loops, StringBuilders, recursion whatever you like better).
Sure this approach may seem "neater" or more "too sweet" or "money" but it is, in my opinion, a worse approach.
A variation on codefin's answer
public static String concatStringsWSep(Iterable<String> strings, String separator) {
StringBuilder sb = new StringBuilder();
String sep = "";
for(String s: strings) {
sb.append(sep).append(s);
sep = separator;
}
return sb.toString();
}
If you are developing for Android, there is TextUtils.join provided by the SDK.
This is the most elegant and clean way I've found so far:
list.stream().collect(Collectors.joining(delimiter));
Guava is a pretty neat library from Google:
Joiner joiner = Joiner.on(", ");
joiner.join(sList);
Have you seen this Coding Horror blog entry?
The Sad Tragedy of Micro-Optimization Theater
I am not shure whether or not it is "neater", but from a performance-standpoint it probably won't matter much.
I prefer String.join(list) in Java 8
It seems to me that the StringBuilder will be quick and efficient.
The basic form would look something like this:
public static String concatStrings(List<String> strings)
{
StringBuilder sb = new StringBuilder();
for(String s: strings)
{
sb.append(s);
}
return sb.toString();
}
If that's too simplistic (and it probably is), you can use a similar approach and add a separator like this:
public static String concatStringsWSep(List<String> strings, String separator)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < strings.size(); i++)
{
sb.append(strings.get(i));
if(i < strings.size() - 1)
sb.append(separator);
}
return sb.toString();
}
I agree with the others who have responded to this question when they say that you should not rely on the toString() method of Java's ArrayList.
ArrayList inherits its toString()-method from AbstractCollection, ie:
public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
Building the string yourself will be far more efficient.
If you really want to aggregate the strings beforehand in some sort of List, you should provide your own method to efficiently join them, e.g. like this:
static String join(Collection<?> items, String sep) {
if(items.size() == 0)
return "";
String[] strings = new String[items.size()];
int length = sep.length() * (items.size() - 1);
int idx = 0;
for(Object item : items) {
String str = item.toString();
strings[idx++] = str;
length += str.length();
}
char[] chars = new char[length];
int pos = 0;
for(String str : strings) {
str.getChars(0, str.length(), chars, pos);
pos += str.length();
if(pos < length) {
sep.getChars(0, sep.length(), chars, pos);
pos += sep.length();
}
}
return new String(chars);
}
I somehow found this to be neater than
using the StringBuilder/StringBuffer
approach.
I guess it depends on what approach you took.
The AbstractCollection#toString() method simply iterates over all the elements and appends them to a StringBuilder. So your method may be saving a few lines of code but at the cost of extra String manipulation. Whether that tradeoff is a good one is up to you.
Rather than depending on ArrayList.toString() implementation, you could write a one-liner, if you are using java 8:
String result = sList.stream()
.reduce("", String::concat);
If you prefer using StringBuffer instead of String since String::concat has a runtime of O(n^2), you could convert every String to StringBuffer first.
StringBuffer result = sList.stream()
.map(StringBuffer::new)
.reduce(new StringBuffer(""), StringBuffer::append);
Next variation on Peter Lawrey's answer without initialization of a new string every loop turn
String concatList(List<String> sList, String separator)
{
Iterator<String> iter = sList.iterator();
StringBuilder sb = new StringBuilder();
while (iter.hasNext())
{
sb.append(iter.next()).append( iter.hasNext() ? separator : "");
}
return sb.toString();
}
Assuming it's faster to just move a pointer / set a byte to null (or however Java implements StringBuilder#setLength), rather than check a condition each time through the loop to see when to append the delimiter, you could use this method:
public static String Intersperse (Collection<?> collection, String delimiter)
{
StringBuilder sb = new StringBuilder ();
for (Object item : collection)
{
if (item == null) continue;
sb.append (item).append (delimiter);
}
sb.setLength (sb.length () - delimiter.length ());
return sb.toString ();
}
In java 8 you can also use a reducer, something like:
public static String join(List<String> strings, String joinStr) {
return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
}
Depending on the need for performance and amount of elements to be added, this might be an ok solution. If the amount of elements are high, the Arraylists reallocation of memory might be a bit slower than StringBuilder.
Using the Functional Java library, import these:
import static fj.pre.Monoid.stringMonoid;
import static fj.data.List.list;
import fj.data.List;
... then you can do this:
List<String> ss = list("foo", "bar", "baz");
String s = stringMonoid.join(ss, ", ");
Or, the generic way, if you don't have a list of Strings:
public static <A> String showList(List<A> l, Show<A> s) {
return stringMonoid.join(l.map(s.showS_()), ", ");
}
if you have json in your dependencies.you can use new JSONArray(list).toString()