[英]Is there a way to turn this for loop to use Java stream instead?
Set<String> unique = new HashSet<>();
List<String> duplicates = new ArrayList<>();
for (Animal cat: animals) {
if (!unique.add(cat.getName())) {
duplicates.add(cat.getName());
}
}
return duplicates;
我想知道是否有简化的方法? 我是Java流的新手,我尝试使用Map,但是我改用了传统的for循环。
我想知道是否有简化的方法? 一世
流方式可能不是您所需要的,您的代码实际上很简单。
由于多个中间操作(过滤,映射,归约,流收集的结果...),流允许从输入( Stream<Foo>
)传递到结果( Foo
, List<String>
...)。
每个流操作都依赖于下一个操作的返回流:
为简化起见,将给出一条链:
a-> b(使用a)-> c(使用b)-> d(使用c)-> e(使用d)
实际上,您的代码不能依靠这种逻辑,因为返回的List<String>
不需要仅返回具有重复名称的列表,可以这样编写:
List<String> duplicates =
animals.stream()
.collect(Collectors.groupingBy(Animal::getName))
.entrySet().stream()
.filter(e -> e.getValue().size() > 1)
.map(Entry::getKey)
.collect(Collectors.toList());
你想返回一个List
,这些发生的顺序每个重复appearition的。
这意味着您不映射Stream<Animal>
-> Stream<String>
如
a-> b(使用a)
因为如果未在结果中添加动物名称,则需要过滤掉元素,但是没有将流设计为增量地填充结果。 所以你被困住了。
您可以编写此代码,但是正如所说的那样,这实际上并不是一种简化,但是它仍然没有应用相同的逻辑,因为dup名称的顺序与您的代码中的顺序不同:
List<String> duplicates =
animals.stream()
.collect(Collectors.groupingBy(Animal::getName, LinkedHashMap::new, Collectors.toList()))
.values().stream()
.flatMap(l-> l.stream().skip(1))
.map(Animal::getName)
.collect(Collectors.toList());
您是否要根据Animal
名称提取重复的Animal
列表的字符串名称? 尽管您的代码不涉及第一个发现的重复项,而是返回列表中n-1
重复项所在的List,但它是:
Set<String> set = new HashSet<>();
List<String> names = animals.stream()
.map(cat -> cat.getName()) // Names to collect and compare
.filter(name -> !set.add(name)) // Collect duplicates
.collect(Collectors.toList()); // To the List
该解决方案基于您的for-loop
并且执行相同的操作。 但是,Stream API的文档指出,构造应为非推断且无状态的 -这意味着独立于可能更改状态的源。
这是有关Stream-API文档的另一种工作方式-但有点复杂:
List<String> names = animals.stream()
.collect(Collectors.groupingBy(
Animal::getName,
Collectors.counting())) // Collects to Map <name, count>
.entrySet() // Gets the entries
.stream() // Stream of the entries
.filter(entry -> entry.getValue() > 1) // Filters the duplicates
.map(entry -> Collections.nCopies( // Creates n-1 copies of the key as same as the
entry.getValue().intValue() - 1, // OP's sample consumes the first duplication
entry.getKey())) // and includes the remainin ones
.flatMap(List::stream) // Flattens the structure
.collect(Collectors.toList()); // Results in the List
两种方式均来自输入:
List<Animal> animals = Arrays.asList(
new Animal("A"), new Animal("A"), new Animal("A"),
new Animal("B"), new Animal("B"), new Animal("C"));
以下输出(无序):
[A,B,A]
我不知道这是否可以被简化,但这是使用流的一种方法:
return animals.stream()
.collect(Collectors.groupingBy(Animal::getName))
.values()
.stream()
.flatMap(group -> group.stream().skip(1))
.map(Animal::getName)
.collect(Collectors.toList());
不要重新发明轮子,而要使用commns-collection.substract()之类的库。
// I have not tested the code, but I think you get the idea
Set<> unique = new HashSet(animals)
Collection<> dublicates = CollectionUtil.subtract(animals, unique)
总有一种方法-仍然不简单,但是要简短得多:
List<Animal> duplicates = animals.stream()
.collect( Collectors.collectingAndThen( Collectors.groupingBy( Animal::getName ),
map -> {
map.entrySet().removeIf( e -> e.getValue().size() < 2 );
return( map.values().stream().flatMap( List::stream ).collect( Collectors.toList() ) );
} ) );
问题是“如何使用Streams?” 但我认为一个不错的答案是“流并不总是一种简化”。
检测重复项的问题很经典,并且有一种典型的处理方法:
因此,尽管它并不能真正回答问题,但是正确的做法是像这样:
List<Animal> animals =
Arrays.asList(
new Animal("Alice"),
new Animal("Alice"),
new Animal("Alice"),
new Animal("Bob"),
new Animal("Charlie"),
new Animal("Bob"));
List<Animal> duplicates = new ArrayList<>();
animals.sort(Comparator.comparing(Animal::getName));
for (int i = 1; i < animals.size(); i++) {
Animal current = animals.get(i);
if (animals.get(i - 1).getName().equals(current.getName())
//Bonus : Also compare to the previous-previous in order to avoid multiple duplicates
&& (i < 2 || !animals.get(i - 2).getName().equals(current.getName()))) {
duplicates.add(current);
}
}
duplicates.forEach(a -> System.out.println(a.getName()));
输出:
Bob
Alice
它可能不容易理解(取决于您的经验),但要比使用Stream
创建一个中间HashMap要干净得多。
因此,要么这样做(为了提高性能),要么就这样做(为了提高可读性)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.