Reputation: 159
I have a Collection of String values. If one of the values is an '*', I'd like to replace that value with 3 others, let's say "X", "Y", and "Z".
In other words, I'd like [ "A", "B", "*", "C"] to turn into ["A","B","X","Y","Z","C"]. Order does not matter, so it is simply get rid of one and add the others. These are the ways I can think of for doing it, using that example:
Collection<String> additionalValues = Arrays.asList("X","Y","Z"); // or set or whatever
if (attributes.contains("*")) {
attributes.remove("*");
attributes.addAll(additionalValues);
}
or
attributes.stream()
.flatMap(val -> "*".equals(val) ? additionalValues.stream() : Stream.of(val))
.collect(Collectors.toList());
What's the most efficient way of doing this? Again, order doesn't matter, and ideally I'd like to remove duplicates (so maybe distinct() on stream, or HashSet?).
Upvotes: 0
Views: 2190
Reputation: 159096
For best performance, and to insert replacement values in the correct position, use indexOf(o)
, remove(index)
, and addAll(index, c)
:
Demo
List<String> attributes = new ArrayList<>(Arrays.asList("A", "B", "*", "C"));
Collection<String> additionalValues = Arrays.asList("X","Y","Z");
int idx = attributes.indexOf("*");
if (idx != -1) {
attributes.remove(idx);
attributes.addAll(idx, additionalValues);
}
System.out.println(attributes);
Output
[A, B, X, Y, Z, C]
If order doesn't matter, use the return value of remove(o)
:
if (attributes.remove("*"))
attributes.addAll(additionalValues);
Output
[A, B, C, X, Y, Z]
Upvotes: 0
Reputation: 140326
I'd do it very similarly to your first way:
if (attributes.remove("*")) {
attributes.addAll(additionalValues);
}
You don't need a separate remove
and contains
call for a correctly-implemented collection:
[
Collection.remove(Object)
] Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call).
Upvotes: 2
Reputation: 18245
I think that second one is better. Arrays.asList("X","Y","Z")
retrieve ArrayList
i.e. an array. It is not good for replace values in it.
In general case, if you want to modify a colletion (e.g. replace *
with X
,Y
and Z
), the do create new collection in some way.
Do look at LinkedList
if you want to modify collection itself.
Using Streams:
public static List<String> replace(Collection<String> attributes, String value, Collection<String> additionalValues) {
return attributes.stream()
.map(val -> value.equals(val) ? additionalValues.stream() : Stream.of(val))
.flatMap(Function.identity())
.collect(Collectors.toList());
}
Not using Streams
public static List<String> replace(Collection<String> attributes, String value, Collection<String> additionalValues) {
List<String> res = new LinkedList<>();
for (String attribute : attributes) {
if (value.equals(attribute))
res.addAll(additionalValues);
else
res.add(attribute);
}
return res;
}
Demo:
List<String> attributes = Arrays.asList("A", "B", "*", "C");
List<String> res = replace(attributes, "*", Arrays.asList("X", "Y", "Z")); // ["A", "B", "X", "Y", "Z", "C"]
Upvotes: 1