diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index e29355b85b9c..30c38eae0a3e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -20,7 +20,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.util.*; import com.siyeh.ig.psiutils.BoolUtils; import org.jetbrains.annotations.Contract; @@ -30,6 +32,7 @@ import org.jetbrains.annotations.Nullable; import java.text.MessageFormat; import java.util.Arrays; +import java.util.stream.Stream; /** * @author Pavel.Dolgov @@ -57,6 +60,9 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns private static final String ALL_MATCH_METHOD = "allMatch"; private static final String COUNTING_COLLECTOR = "counting"; + private static final String TO_LIST_COLLECTOR = "toList"; + private static final String TO_SET_COLLECTOR = "toSet"; + private static final String TO_COLLECTION_COLLECTOR = "toCollection"; private static final String MIN_BY_COLLECTOR = "minBy"; private static final String MAX_BY_COLLECTOR = "maxBy"; private static final String MAPPING_COLLECTOR = "mapping"; @@ -150,6 +156,13 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } } + @Contract("null -> false") + private boolean isCollectionStream(PsiMethodCallExpression qualifierCall) { + if (qualifierCall == null) return false; + PsiMethod qualifier = qualifierCall.resolveMethod(); + return isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0); + } + private void handleStreamForEach(PsiMethodCallExpression methodCall, PsiMethod method) { final String name; if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_METHOD, 1)) { @@ -162,9 +175,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return; } final PsiMethodCallExpression qualifierCall = getQualifierMethodCall(methodCall); - if (qualifierCall == null) return; - final PsiMethod qualifier = qualifierCall.resolveMethod(); - if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) { + if (isCollectionStream(qualifierCall)) { final ReplaceStreamMethodFix fix = new ReplaceStreamMethodFix(name, FOR_EACH_METHOD, true); holder .registerProblem(methodCall, getCallChainRange(methodCall, qualifierCall), fix.getMessage(), new SimplifyCallChainFix(fix)); @@ -176,7 +187,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns if(parameter instanceof PsiMethodCallExpression) { PsiMethodCallExpression collectorCall = (PsiMethodCallExpression)parameter; PsiMethod collectorMethod = collectorCall.resolveMethod(); - ReplaceCollectorFix fix = null; + ReplaceCollectorFix fix; if(isCallOf(collectorMethod, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, COUNTING_COLLECTOR, 0)) { fix = new ReplaceCollectorFix(COUNTING_COLLECTOR, "count()", false); } else if(isCallOf(collectorMethod, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, MIN_BY_COLLECTOR, 1)) { @@ -197,9 +208,26 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns fix = new ReplaceCollectorFix(SUMMING_LONG_COLLECTOR, "mapToLong({0}).sum()", false); } else if(isCallOf(collectorMethod, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, SUMMING_DOUBLE_COLLECTOR, 1)) { fix = new ReplaceCollectorFix(SUMMING_DOUBLE_COLLECTOR, "mapToDouble({0}).sum()", false); + } else { + PsiType type = methodCall.getType(); + if(type instanceof PsiClassType && !(((PsiClassType)type).resolve() instanceof PsiTypeParameter)) { + String replacement = collectorToCollection(collectorCall); + if (replacement != null) { + PsiMethodCallExpression qualifier = getQualifierMethodCall(methodCall); + if (isCollectionStream(qualifier)) { + PsiElement startElement = qualifier.getMethodExpression().getReferenceNameElement(); + if (startElement != null) { + holder.registerProblem(methodCall, new TextRange(startElement.getTextOffset() - methodCall.getTextOffset(), + methodCall.getTextLength()), + "Can be replaced with '" + replacement + "' constructor", + new SimplifyCallChainFix(new SimplifyCollectionCreationFix(replacement))); + } + } + } + } + return; } - if (fix != null && - collectorCall.getArgumentList().getExpressions().length == collectorMethod.getParameterList().getParametersCount()) { + if (collectorCall.getArgumentList().getExpressions().length == collectorMethod.getParameterList().getParametersCount()) { TextRange range = methodCall.getTextRange(); PsiElement nameElement = methodCall.getMethodExpression().getReferenceNameElement(); if(nameElement != null) { @@ -247,6 +275,51 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns }; } + private static boolean isCollectionConstructor(PsiMethod ctor) { + if(!ctor.getModifierList().hasExplicitModifier(PsiModifier.PUBLIC)) return false; + PsiParameterList list = ctor.getParameterList(); + if(list.getParametersCount() != 1) return false; + PsiParameter parameter = list.getParameters()[0]; + PsiTypeElement typeElement = parameter.getTypeElement(); + if(typeElement == null) return false; + PsiType type = typeElement.getType(); + if(!(type instanceof PsiClassType)) return false; + PsiClass aClass = ((PsiClassType)type).resolve(); + if(aClass == null) return false; + return CommonClassNames.JAVA_UTIL_COLLECTION.equals(aClass.getQualifiedName()); + } + + @Nullable + private static String collectorToCollection(PsiMethodCallExpression call) { + PsiMethod method = call.resolveMethod(); + if(isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, TO_LIST_COLLECTOR, 0)) { + return CommonClassNames.JAVA_UTIL_ARRAY_LIST; + } + if(isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, TO_SET_COLLECTOR, 0)) { + return CommonClassNames.JAVA_UTIL_HASH_SET; + } + if(isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, TO_COLLECTION_COLLECTOR, 1)) { + PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if(expressions.length == 1 && expressions[0] instanceof PsiMethodReferenceExpression) { + PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)expressions[0]; + if(methodRef.isConstructor()) { + PsiElement element = methodRef.resolve(); + if(element instanceof PsiMethod) { + PsiMethod ctor = (PsiMethod)element; + if(ctor.getParameterList().getParametersCount() == 0) { + PsiClass aClass = ctor.getContainingClass(); + if (aClass != null && + Stream.of(aClass.getConstructors()).anyMatch(SimplifyStreamApiCallChainsInspection::isCollectionConstructor)) { + return aClass.getQualifiedName(); + } + } + } + } + } + } + return null; + } + static boolean isParentNegated(PsiMethodCallExpression methodCall) { PsiElement parent = PsiUtil.skipParenthesizedExprUp(methodCall.getParent()); return parent instanceof PsiExpression && BoolUtils.isNegation((PsiExpression)parent); @@ -689,4 +762,51 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } } } + + private static class SimplifyCollectionCreationFix implements CallChainFix { + private String myReplacement; + + public SimplifyCollectionCreationFix(String replacement) { + myReplacement = replacement; + } + + @Override + public String getName() { + return "Replace with '"+myReplacement+"' constructor"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getStartElement(); + if(!(element instanceof PsiMethodCallExpression)) return; + PsiMethodCallExpression collectCall = (PsiMethodCallExpression)element; + PsiType type = collectCall.getType(); + if(!(type instanceof PsiClassType)) return; + PsiClass resolvedType = ((PsiClassType)type).resolve(); + if(resolvedType == null || resolvedType instanceof PsiTypeParameter) return; + PsiMethodCallExpression streamCall = getQualifierMethodCall(collectCall); + if(streamCall == null) return; + PsiExpression collectionExpression = streamCall.getMethodExpression().getQualifierExpression(); + if(collectionExpression == null) return; + String typeText = type.getCanonicalText(); + if(CommonClassNames.JAVA_UTIL_LIST.equals(resolvedType.getQualifiedName()) || + CommonClassNames.JAVA_UTIL_SET.equals(resolvedType.getQualifiedName())) { + PsiType[] parameters = ((PsiClassType)type).getParameters(); + if(parameters.length != 1) return; + typeText = myReplacement + "<" + parameters[0].getCanonicalText() + ">"; + } + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + PsiExpression result = factory + .createExpressionFromText("new " + typeText + "(" + collectionExpression.getText() + ")", element); + PsiNewExpression newExpression = (PsiNewExpression)element.replace(result); + PsiJavaCodeReferenceElement classReference = newExpression.getClassOrAnonymousClassReference(); + LOG.assertTrue(classReference != null); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(classReference); + if (PsiDiamondTypeUtil.canCollapseToDiamond(newExpression, newExpression, null)) { + PsiDiamondTypeUtil.replaceExplicitWithDiamond(classReference.getParameterList()); + } + CodeStyleManager.getInstance(project).reformat(newExpression); + } + } } diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollection.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollection.java new file mode 100644 index 000000000000..dccf7083d015 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollection.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new TreeSet<>(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionGeneric.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionGeneric.java new file mode 100644 index 000000000000..4a40cf0dc51e --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionGeneric.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new TreeSet(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeAddAll.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeAddAll.java new file mode 100644 index 000000000000..4fb25baaf003 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeAddAll.java @@ -0,0 +1,18 @@ +// "Replace with 'Test.MyType' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + public MyType() {} + + public MyType(Collection coll) { + super(coll); + } + } + + public static void test(List s) { + new MyType(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeGeneric.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeGeneric.java new file mode 100644 index 000000000000..8b73ffd5cef4 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionMyTypeGeneric.java @@ -0,0 +1,18 @@ +// "Replace with 'Test.MyType' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + public MyType() {} + + public MyType(Collection coll) { + super(coll); + } + } + + public static void testMy(List s) { + new MyType(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionOtherType.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionOtherType.java new file mode 100644 index 000000000000..6b1baeccdcaa --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToCollectionOtherType.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new TreeSet(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToList.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToList.java new file mode 100644 index 000000000000..81ea9abe9a18 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToList.java @@ -0,0 +1,11 @@ +// "Replace with 'java.util.ArrayList' constructor" "true" + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new ArrayList<>(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToListOtherType.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToListOtherType.java new file mode 100644 index 000000000000..8ee47516fa34 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToListOtherType.java @@ -0,0 +1,11 @@ +// "Replace with 'java.util.ArrayList' constructor" "true" + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new ArrayList(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToSet.java b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToSet.java new file mode 100644 index 000000000000..e340cfe9684e --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterStreamToSet.java @@ -0,0 +1,11 @@ +// "Replace with 'java.util.HashSet' constructor" "true" + +import java.util.HashSet; +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + new HashSet<>(s).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollection.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollection.java new file mode 100644 index 000000000000..31eb7befc2f6 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollection.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toCollection(TreeSet::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionGeneric.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionGeneric.java new file mode 100644 index 000000000000..5ea3bff968e3 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionGeneric.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toCollection(TreeSet::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionInvalid.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionInvalid.java new file mode 100644 index 000000000000..3523cbd17f59 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionInvalid.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "false" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toCollection(TreeSet::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyType.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyType.java new file mode 100644 index 000000000000..70e4bdfba45b --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyType.java @@ -0,0 +1,14 @@ +// "Replace with 'Test.MyType' constructor" "false" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + + } + + public static void test(List s) { + s.stream().collect(Collectors.toCollection(MyType::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAll.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAll.java new file mode 100644 index 000000000000..770e70aa5ed4 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAll.java @@ -0,0 +1,18 @@ +// "Replace with 'Test.MyType' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + public MyType() {} + + public MyType(Collection coll) { + super(coll); + } + } + + public static void test(List s) { + s.stream().collect(Collectors.toCollection(MyType::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAllPrivate.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAllPrivate.java new file mode 100644 index 000000000000..30ff25be3cee --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeAddAllPrivate.java @@ -0,0 +1,18 @@ +// "Replace with 'Test.MyType' constructor" "false" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + public MyType() {} + + private MyType(Collection coll) { + super(coll); + } + } + + public static void test(List s) { + s.stream().collect(Collectors.toCollection(MyType::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeGeneric.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeGeneric.java new file mode 100644 index 000000000000..8b207a2fe13b --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionMyTypeGeneric.java @@ -0,0 +1,18 @@ +// "Replace with 'Test.MyType' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + static class MyType extends ArrayList { + public MyType() {} + + public MyType(Collection coll) { + super(coll); + } + } + + public static void testMy(List s) { + s.stream().collect(Collectors.toCollection(MyType::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionOtherType.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionOtherType.java new file mode 100644 index 000000000000..492cc61b4dd5 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToCollectionOtherType.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.TreeSet' constructor" "true" + +import java.util.*; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toCollection(TreeSet::new)).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToList.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToList.java new file mode 100644 index 000000000000..dcf6c386e8e6 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToList.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.ArrayList' constructor" "true" + +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toList()).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToListOtherType.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToListOtherType.java new file mode 100644 index 000000000000..10edac2d1b3e --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToListOtherType.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.ArrayList' constructor" "true" + +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toList()).contains("abc"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToSet.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToSet.java new file mode 100644 index 000000000000..574e4e371636 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeStreamToSet.java @@ -0,0 +1,10 @@ +// "Replace with 'java.util.HashSet' constructor" "true" + +import java.util.List; +import java.util.stream.*; + +class Test { + public static void test(List s) { + s.stream().collect(Collectors.toSet()).contains("abc"); + } +} \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html b/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html index 6ae43bcaa8d0..c063840f5ffd 100644 --- a/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html +++ b/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html @@ -6,29 +6,32 @@ It allows to avoid creating redundant temporary objects when traversing a collec The following call chains are replaced by this inspection:

    -
  • Collection.stream().forEach() → Collection.forEach()
  • -
  • Collection.stream().forEachOrdered() → Collection.forEach()
  • +
  • collection.stream().forEach() → collection.forEach()
  • +
  • collection.stream().forEachOrdered() → collection.forEach()
  • +
  • collection.stream().collect(Collectors.toList()) → new ArrayList<>(collection)
  • +
  • collection.stream().collect(Collectors.toSet()) → new HashSet<>(collection)
  • +
  • collection.stream().collect(Collectors.toCollection(CollectionType::new)) → new CollectionType<>(collection)
  • Arrays.asList().stream() → Arrays.stream() or Stream.of()
  • Collections.singleton().stream() → Stream.of()
  • Collections.singletonList().stream() → Stream.of()
  • Collections.emptyList().stream() → Stream.empty()
  • Collections.emptySet().stream() → Stream.empty()
  • -
  • Stream.filter().findFirst().isPresent() → Stream.anyMatch()
  • -
  • Stream.filter().findAny().isPresent() → Stream.anyMatch()
  • -
  • Stream.collect(Collectors.counting()) → Stream.count()
  • -
  • Stream.collect(Collectors.maxBy()) → Stream.max()
  • -
  • Stream.collect(Collectors.minBy()) → Stream.min()
  • -
  • Stream.collect(Collectors.mapping()) → Stream.map().collect()
  • -
  • Stream.collect(Collectors.reducing()) → Stream.reduce() or Stream.map().reduce()
  • -
  • Stream.collect(Collectors.summingInt()) → Stream.mapToInt().sum()
  • -
  • Stream.collect(Collectors.summingLong()) → Stream.mapToLong().sum()
  • -
  • Stream.collect(Collectors.summingDouble()) → Stream.mapToDouble().sum()
  • -
  • !Stream.anyMatch() → Stream.noneMatch()
  • -
  • !Stream.anyMatch(x -> !(...)) → Stream.allMatch()
  • -
  • !Stream.noneMatch() → Stream.anyMatch()
  • -
  • Stream.noneMatch(x -> !(...)) → Stream.allMatch()
  • -
  • Stream.allMatch(x -> !(...)) → Stream.noneMatch()
  • -
  • !Stream.allMatch(x -> !(...)) → Stream.anyMatch()
  • +
  • stream.filter().findFirst().isPresent() → stream.anyMatch()
  • +
  • stream.filter().findAny().isPresent() → stream.anyMatch()
  • +
  • stream.collect(Collectors.counting()) → stream.count()
  • +
  • stream.collect(Collectors.maxBy()) → stream.max()
  • +
  • stream.collect(Collectors.minBy()) → stream.min()
  • +
  • stream.collect(Collectors.mapping()) → stream.map().collect()
  • +
  • stream.collect(Collectors.reducing()) → stream.reduce() or Stream.map().reduce()
  • +
  • stream.collect(Collectors.summingInt()) → stream.mapToInt().sum()
  • +
  • stream.collect(Collectors.summingLong()) → stream.mapToLong().sum()
  • +
  • stream.collect(Collectors.summingDouble()) → stream.mapToDouble().sum()
  • +
  • !stream.anyMatch() → stream.noneMatch()
  • +
  • !stream.anyMatch(x -> !(...)) → stream.allMatch()
  • +
  • !stream.noneMatch() → stream.anyMatch()
  • +
  • stream.noneMatch(x -> !(...)) → stream.allMatch()
  • +
  • stream.allMatch(x -> !(...)) → stream.noneMatch()
  • +
  • !stream.allMatch(x -> !(...)) → stream.anyMatch()

Note that the replacements semantic may have minor difference in some cases.