what would be most optimal way to rewrite this iteration with java 8 stream().
for (name : names){
if(name == a){
doSomething();
break;
}
if(name == b){
doSomethingElse();
break;
}
if(name == c){
doSomethingElseElse();
break;
}
}
Basically, iterating over the list with 3 conditions if any of them is satisfied want to break loop and in every condition want to call different method.
You can use anyMatch to find the first element matching one of your conditions and terminate. Use side effects for calling the processing methods :
boolean found =
names.stream()
.anyMatch (name -> {
if (name.equals(a)) {
doSomething();
return true;
} else if (name.equals(b)) {
doSomethingElse ();
return true;
} else if (name.equals(c)) {
doSomethingElseElse ();
return true;
} else {
return false;
}
}
);
Pretty ugly, but does what you asked in a single iteration.
The answer by Eran is definitely the straightforward way of performing the search. However, I would like to propose a slightly different approach:
private static final Map<String, Runnable> stringToRunnable = new HashMap<>();
{
stringToRunnable.put("a", this::doSomething);
stringToRunnable.put("b", this::doSomethingElse);
stringToRunnable.put("c", this::doSomethingElseElse);
}
public static void main(String[] args) {
List<String> names = Arrays.asList("1", "2", "b", "a");
names.stream()
.filter(stringToRunnable::containsKey)
.findFirst()
.ifPresent(name -> stringToRunnable.get(name).run());
}
private void doSomethingElseElse() {
}
private void doSomethingElse() {
}
public void doSomething() {
}
The part that does the job is the code below, but I added it to a main() function assuming a, b, and c are strings. However, the idea would work with any datatype.
names.stream()
.filter(stringToRunnable::containsKey)
.findFirst()
.ifPresent(name -> stringToRunnable.get(name).run());
The idea is to keep a map of keys and Runnables. By having Runnable as value it is possible to define a void method reference without parameters. The stream first filters away all values not present in the map, then finds the first hit, and executes its method if found.
Collection collection;
collection.forEach(name->{
if(name.equals(a))){
doSomething();
}
if(name.equals(b)){
doSomethingElse();
}
if(name.equals(c)){
doSomethingElseElse();
}
});
Related
I would like to find the most efficent way how to find 1st element of collection which is inside another collection where I take also the 1st entry. I think this is "ugly" solution.
public class UnitLine {}
public class Unit {
private Collection<UnitLine> unitLines;
public Collection<UnitLine> getUnitLines() {
return unitLines;
}
}
public class Wrapper {
Collection<Unit> units;
public Collection<Unit> getUnits() {
return units;
}
}
public static void main(String[] args) {
Wrapper wrapper = new Wrapper();
Unit unit = wrapper.getUnits().stream().findFirst().orElseGet(null);
if (unit != null) {
UnitLine unitLine = unit.getUnitLines().stream().findFirst().orElseGet(null);
if (unitLine != null) {
// do something
}
}
}
Turning Optionals into nulls means you can't take advantage of the nice features of Optional. Here's an equivalent of your code that uses Optional map and orElse usefully:
UnitLine unitLine = wrapper.getUnits().stream()
.findFirst()
.map(Unit::getLines)
.orElse(Collections.emptySet()).stream()
.findFirst()
.orElse(null);
That code gets the first element of the first collection, or null if the first collection is empty. To get the first element of the first non-empty collection, you can use flatMap:
wrapper.getUnits().stream()
.map(Unit::getUnitLines)
.flatMap(Collection::stream)
.findFirst()
I'm running out of idea of how will I merge this 2 conditions, it has the same return so I need to merge
if ((StringUtils.isBlank(ext))) {
return true;
}
for (String str : INVALID_EXTENSION_ARR) {
if (ext.matches(str)) {
return true;
} else if (ext.matches(str.toLowerCase())) {
return true;
}
}
You don't need a loop.
Populate INVALID_EXTENSION_ARR with values in lowercase:
private static final List<String> INVALID_EXTENSION_ARR = Arrays.asList("foo", "bar", "whatever"); // Note: All in lowercase!
Then it's just one line:
return StringUtils.isBlank(ext) || INVALID_EXTENSION_ARR.contains(ext.toLowerCase());
Note: I have assumed when you used matches() you meant to use equals().
——-
If the list of acceptable extensions is “large” (say, more than 10), you’ll get better performance if you use a Set instead of a List:
private static final Set<String> INVALID_EXTENSION_ARR = new HashSet<>(Arrays.asList("foo", "bar", "whatever"));
Or for recent java versions:
private static final Set<String> INVALID_EXTENSION_ARR = Set.of("foo", "bar", "whatever");
But you would be unlikely to notice much difference unless the size was more than say 100.
Assuming that the loop will always be entered into,
for (String str : INVALID_EXTENSION_ARR) {
if (StringUtils.isBlank(ext) || ext.matches(str)) {
return true;
} else if (ext.matches(str.toLowerCase())) {
return true;
}
}
but I think that way that had it was easier to read and does not need to re-evaluate StringUtils.isBlank(ext) every time.
It is helpful if you provide more context, but this is one of the ways you can compact it.
for (String str : INVALID_EXTENSION_ARR) {
if (StringUtils.isBlank(ext) || ext.toLowerCase().matches(str.toLowerCase())) {
return true;
}
}
I have some values in my Array list and I have to remove some particular values based on conditions.I am using IF condition but there are many conditions to compare so I need to optimize the comparison time.for e.g. my list is
Msisdn_array={45,85,79,60,502}
and if condition match then remove the entry.
Below are the Conditions, Is there any possible way to optimize this conditions.
if(Msisdn_array.contains("60") && Msisdn_array.contains("910"))
{
Msisdn_array.remove("60");
Msisdn_array.remove("910");
}
if(Msisdn_array.contains("75") && Msisdn_array.contains("500"))
{
Msisdn_array.remove("75");
Msisdn_array.remove("500");
}
if(Msisdn_array.contains("76") && Msisdn_array.contains("502"))
{
Msisdn_array.remove("76");
Msisdn_array.remove("502");
}
if(Msisdn_array.contains("61") && Msisdn_array.contains("911"))
{
Msisdn_array.remove("61");
Msisdn_array.remove("911");
}
if(Msisdn_array.contains("77") && Msisdn_array.contains("503"))
{
Msisdn_array.remove("77");
Msisdn_array.remove("503");
}
if(Msisdn_array.contains("78") && Msisdn_array.contains("505"))
{
Msisdn_array.remove("78");
Msisdn_array.remove("505");
}
if(Msisdn_array.contains("79") && Msisdn_array.contains("507"))
{
Msisdn_array.remove("79");
Msisdn_array.remove("507");
}
if(Msisdn_array.contains("62") && Msisdn_array.contains("912"))
{
Msisdn_array.remove("62");
Msisdn_array.remove("912");
}
if(Msisdn_array.contains("63") && Msisdn_array.contains("913"))
{
Msisdn_array.remove("63");
Msisdn_array.remove("913");
}
if(Msisdn_array.contains("64") && Msisdn_array.contains("914"))
{
Msisdn_array.remove("64");
Msisdn_array.remove("914");
}
if(Msisdn_array.contains("65") && Msisdn_array.contains("915"))
{
Msisdn_array.remove("65");
Msisdn_array.remove("915");
}
if(Msisdn_array.contains("66") && Msisdn_array.contains("916"))
{
Msisdn_array.remove("66");
Msisdn_array.remove("916");
}
if(Msisdn_array.contains("67") && Msisdn_array.contains("917"))
{
Msisdn_array.remove("67");
Msisdn_array.remove("917");
}
if(Msisdn_array.contains("68") && Msisdn_array.contains("918"))
{
Msisdn_array.remove("68");
Msisdn_array.remove("918");
}
if(Msisdn_array.contains("69") && Msisdn_array.contains("919"))
{
Msisdn_array.remove("69");
Msisdn_array.remove("919");
}
if(Msisdn_array.contains("70") && Msisdn_array.contains("920"))
{
Msisdn_array.remove("70");
Msisdn_array.remove("920");
}
if(Msisdn_array.contains("71") && Msisdn_array.contains("921"))
{
Msisdn_array.remove("71");
Msisdn_array.remove("921");
}
if(Msisdn_array.contains("72") && Msisdn_array.contains("922"))
{
Msisdn_array.remove("72");
Msisdn_array.remove("922");
}
if(Msisdn_array.contains("73") && Msisdn_array.contains("923"))
{
Msisdn_array.remove("73");
Msisdn_array.remove("923");
}
if(Msisdn_array.contains("74") && Msisdn_array.contains("924"))
{
Msisdn_array.remove("74");
Msisdn_array.remove("924");
}
if(Msisdn_array.contains("80") && Msisdn_array.contains("926"))
{
Msisdn_array.remove("80");
Msisdn_array.remove("926");
}
if(Msisdn_array.contains("81") && Msisdn_array.contains("927"))
{
Msisdn_array.remove("81");
Msisdn_array.remove("927");
}
if(Msisdn_array.contains("82") && Msisdn_array.contains("928"))
{
Msisdn_array.remove("82");
Msisdn_array.remove("928");
}
One potential optimization is that you could don't need to check if the second item is in the list. Instead just attempt to remove it. If it was removed, the remove method will return true and you can also remove the first item.
if(Msisdn_array.contains("60") && Msisdn_array.remove("910")){
Msisdn_array.remove("60");
}
If you don't want to write out each if statement, you could store the groups in a Map, with the first item as the key and the second item as the value.
Map<String, String> rules = new HashMap<>();
rules.put("60", "910");
rules.put("75", "500");
rules.put("76", "502");
...
...
for(Entry entry : rules.values()) {
if(Msisdn_array.contains(entry.getKey()) && Msisdn_array.remove(entry.getValue())){
Msisdn_array.remove(entry.getKey());
}
}
I think you only need to extract a method to check if all of a group values are existed in List and then remove all. For example:
private void removeIfAllExist(List<String> list, String[] values) {
for (String v : values) {
if (!list.contains(v)) {
return;
}
}
list.removeAll(Arrays.asList(values));
}
public void yourLogic() {
List<String> list = new ArrayList<>(Arrays.asList("45", "85", "79", "60", "502"));
String[][] conditions = new String[][]{
new String[]{"60", "910"},
new String[]{"75", "500"},
new String[]{"76", "502"},
new String[]{"61", "911"},
new String[]{"77", "503"},
// more conditions
};
for (String[] values : conditions) {
removeIfAllExist(list, values);
}
}
If you do not iterate through the list often, I suggest you use a Set.
Search in this collection is fast.
contains is an O(n) operation. The list is traversed until the element is found. So every time you call contains you are potentially traversing the entire list.
An optimization might be to traverse the list once, manually, and check if the elements exist, and then do your removes afterwards, at the cost of some extra memory to store the boolean variables:
boolean exists_72 = false;
boolean exists_922 = false;
for(String element : Msisdn_array) {
if(element.equals("72")) {
exists_72 = true;
} else if(element.equals("922")) {
exists_922 = true;
}
}
if(exists_72 && exists_922) }
Msisdn_array.remove("72");
Msisdn_array.remove("922");
}
as remove returns a boolean is sucessfully removed you could do
if (list.remove ("60") && list.remove ("90"))
{
// everything ok
}
else {
// put back "60"
list.add ("60");
}
but personally I would go for readability a just create a method
removeBoth (String one, String two) {
if(list.contains(one) && list.contains(two))
{
list.remove(one);
list.remove(two);
}
}
It's slow because of List.contains being slow and also because List.remove being even slower (as it must move all elements with bigger indexes in order to fill the gap). It's ugly because of code repetition.
Msisdn_array is against Java naming convention and it's no array, so lets call it inputList. Let's use a HashSet for the lookups and another one to track what should be removed.
class Worker {
private final Set<String> inputListAsSet = new HashSet<>();
private final Set<String> removals = new HashSet<>();
public static void process(List<String> inputList) {
final Worker worker = new Worker(inputList);
worker.removeIfBothPresent("60", "910");
worker.removeIfBothPresent("75", "500");
worker.removeIfBothPresent("76", "502");
worker.removeIfBothPresent("61", "911");
worker.removeIfBothPresent("72", "922");
worker.removeIfBothPresent("73", "923");
worker.removeIfBothPresent("74", "924");
worker.removeIfBothPresent("80", "926");
worker.removeIfBothPresent("81", "927");
worker.removeIfBothPresent("82", "928");
inputList.removeAll(worker.removals);
}
private Worker(List<String> inputList) {
inputListAsSet.addAll(inputList);
}
private void removeIfBothPresent(String first, String second) {
if (inputListAsSet.contains(first) && inputListAsSet.contains(second)) {
removals.add(first);
removals.add(second);
}
}
}
Instead of using a class instance, the sets could be passed as arguments, but creating a worker seems cleaner. Note that despite the optimizations, my code is shorter than the original. In case of duplicates, it's not exactly equivalent.
I like java-8's optional chaning style.
So I want check double null.
class A {
public String getSome() {
return ""; // some string
}
}
class B {
public String getSome() {
return ""; // some string
}
}
class T {
A a;
B b;
public String result() {
if (a.getSome() != null) {
if (b.getSome() != null) {
return a+b;
} else {
throw new RuntimeException();
}
} else {
throw new RuntimeException();
}
}
}
How can I convert T.result() to Optional Style?
I tried this style but IDE told me 'cyclic interface'.
public String result() {
return Optional.ofNullable(a.getSome())
.map(a -> {
return Optional.ofNullable(b.getSome())
.map(b -> {
return a + b;
})
.orElseThrow(RuntimeException::new);
})
.orElseThrow(RuntimeException::new);
}
While #Eran gave a possible solution, I don't think you add simplicity by using chaining and Optionals.
The new Java 8 API and features must not be a replacement for all pre-Java 8 code. There's a lot of questions for example about using Stream to perform some tasks while a simple for loop would do the trick.
In your case since you only want to check if the reference is not null, simply do:
public String result() {
return Objects.requireNonNull(a.getSome()) + Objects.requireNonNull(b.getSome());
}
This should be much simpler :
public String result() {
return Optional.ofNullable(a.getSome()).orElseThrow(RuntimeException::new) +
Optional.ofNullable(b.getSome()).orElseThrow(RuntimeException::new);
}
And if you change the getSome methods to return an Optional<String>, the result method would be even simpler :
public String result() {
return a.getSome().orElseThrow(RuntimeException::new) +
b.getSome().orElseThrow(RuntimeException::new);
}
However, if it's possible that either a or b themselves would be null, you need some extra code to handle that.
Rewriting your method in what you call "Optional style" would result in the following monstrosity:
Optional.ofNullable(a.getSome())
.flatMap(x ->
Optional.ofNullable(b.getSome()).map(x::concat)
).orElseThrow(RuntimeException::new)
Why in the world would you want to do this? You are trying to solve a problem that doesn't exist. Please just follow ZouZou's suggestion and use requireNonNull, but put each requireNonNull in a separate line to make stack traces easier to decipher.
What your IDE complained about is probably your use of a and b as both variables in the method and also as parameter names in the lambdas. You are not allowed to do that.
I'm trying to use a method to compare t2o different lists. Basically I want to pass two different lists to a method which will return true or false if the elements of one array list are contained in the other using .contains. Right now it only returns true - and I'm not sure why. I'd like it to return false. If someone could help me figure this out, that would be great.
public class ArrayListTest {
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
list1.add("cat");
list1.add("dog");
list1.add("zebra");
list1.add("lion");
list1.add("mouse");
//Test Values
//list2.add("cat");
list2.add("lizard");
boolean doesitcontain = contains(list1, list2);
System.out.println(doesitcontain);
}
public static boolean contains (List<String>list1, List<String>list2){
boolean yesitcontains;
for(int i = 0; i < list1.size(); i++){
if(list2.contains(list1.get(i))){
System.out.println("Duplicate: "+list1.get(i));
yesitcontains = true;
System.out.println(yesitcontains);
}else{
yesitcontains = false;
System.out.println(yesitcontains);
}
}
if (yesitcontains = true){
return true;
}else
return false;
}
}
You have inadvertently used the assignment operator where you intended the equality operator. In your specific case you should rewrite all this:
if (yesitcontains = true){
return true;
}else
return false;
}
to just
return yesitcontains;
and avoid any chance of confusion.
Furthermore, your algorithm will not work because you should return true immediately when you see a duplicate. Instead you go on with the loop and "forget" your finding. You can expect this to always return false except if the very last elements coincide.
In a wider context, I should also give you the following general advice:
Avoid indexed iteration over lists. Not all lists are ArrayLists and may show O(n) complexity for get(i). Instead use the enhanced for loop, which is safer, more concise, and more obvious;
Know the library: if you're just after confirming there are no duplicates, just Collections.disjoint(list1, list2) would give you what you need;
Be aware of algorithmic complexity: checking for duplicates in two lists is O(n2), but if you turn one of them into a HashSet, you'll get O(n).
Taking everything said above into account, the following would be an appropriate implementation:
static boolean disjoint(Collection<?> c1, Collection<?> c2) {
for(Object o : c1)
if (c2.contains(o))
return true;
return false;
}
If you look at Collections.disjoint, you'll find this exact same loop, preceded by a piece of code which optimizes the usage of sets for reasons described above.
Seems to me your method should be rewritten to:
public static boolean contains(List<String>list1, List<String>list2) {
return list2.containsAll(list1);
}
The code you currently have actually only checks if the last element of list1 is also in list2.
If you're actually looking for a contains any, this simple solution will do:
public static boolean contains(List<String>list1, List<String>list2) {
for (String str : list1) {
if (list2.contains(str)) {
return true;
}
}
return false;
}
if (yesitcontains = true){
should be
if (yesitcontains == true){
== is for comparison and = is for assignment.
if (yesitcontains = true){
will always evaluate to if(true) which causing return true;
EDIT:
(OR)
simply return yesitcontains; as commented.
if (yesitcontains == true) { } // use `==` here
or just
if (yesitcontains) { }
The below code assigns true to yesitcontains , and the expression will always be true.
if (yesitcontains = true) { }
There is no point of if() in your code , you can simple return yesitcontains;