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循环。
答案 0 :(得分:5)
我想知道是否有一种简化方法?我
流方式可能不是您所需要的,您的代码实际上很简单。
由于多个中间操作(过滤,映射,归约,流式处理a),流允许从输入(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
。
这意味着您不映射
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());
答案 1 :(得分:3)
您是否要根据名称Animal
提取重复的字符串名称?尽管您的代码不涉及第一个找到的重复项,而是返回列表中n-1
处重复项的列表,但它是:
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的documentation指出,构造应为non-infering和stateless,这意味着它们独立于可能更改状态的源。
关于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]
答案 2 :(得分:1)
我不知道这是否可以被简化,但这是使用流实现的一种方法:
return animals.stream()
.collect(Collectors.groupingBy(Animal::getName))
.values()
.stream()
.flatMap(group -> group.stream().skip(1))
.map(Animal::getName)
.collect(Collectors.toList());
答案 3 :(得分:0)
请勿重新发明轮子,并使用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)
答案 4 :(得分:0)
总有一种方法-仍然不简单,但更短:
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() ) );
} ) );
答案 5 :(得分:-1)
问题是“如何使用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更为简洁。
因此,要么这样做(为了提高性能),要么像您已经做的那样(出于可读性)。