JDK8-2-流(2.1)- 流操作-distinct

news/2024/11/28 7:46:27/

JDK8-2-流(2.1)- 流操作-distinct

去重操作,如下开头两个菜品一样,对 menu 去重如下:

public class DishDistinctTest1 {public static final List<Dish> menu = Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER),new Dish("rice", true, 350, Dish.Type.OTHER),//季节水果new Dish("season fruit", true, 120, Dish.Type.OTHER),new Dish("pizza", true, 550, Dish.Type.OTHER),//虾new Dish("prawns", false, 300, Dish.Type.FISH),//鲑鱼,三文鱼,new Dish("salmon", false, 450, Dish.Type.FISH));public static void distinctTest() {List<Dish> dishList = menu.stream().distinct().collect(Collectors.toList());System.out.println(dishList);}public static void main(String[] args) {distinctTest();}
}

注意: 对象去重需要重写 equals 和 hashCode 方法(默认对象 equals 方法比较的是对象内存地址是否一致),由 distinct 内部具体实现类 java.util.stream.DistinctOps 可以看出这点。

java.util.HashSet

public boolean contains(Object o) {return map.containsKey(o);
}

java.util.HashMap

public boolean containsKey(Object key) {return getNode(hash(key), key) != null;
}

IDEA 如何自动生成 equals 和 hashCode 方法

空白处右键选择 Generate 或者Alt + Ins 快捷键

@Override
public boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Dish dish = (Dish) o;return vegetarian == dish.vegetarian && calories == dish.calories && Objects.equals(name, dish.name) && type == dish.type;
}@Override
public int hashCode() {return Objects.hash(name, vegetarian, calories, type);
}

使用 filter 方法去重

假设现在需求有变,菜品 Dish 只要名称、是否素食、类型一致即可判断为重复,最直接的方法就是改动 Dish 类中 equals 和 hashCode 方法实现,但是这样会改变原有的规则,导致原来的代码有问题,而且假设还有其他去重的逻辑,那也无法同时满足。

如何实现

① 定义一个 String 类型 的 Set
② 将 Dish 中 name、vegetarian、type 属性拼接成字符串加入到 set 中,利用 Set 集合中无法添加重复元素的特性过滤掉没有成功添加的元素
代码如下:

public class DishDistinctTest2 {public static final List<Dish> REPEATED_DISHES = Arrays.asList(new Dish("pork", false, 790, Dish.Type.MEAT),new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("beef", false, 690, Dish.Type.MEAT));private static <T> Predicate<T> distinctByKey(Function<? super T, String> keyExtractor) {Set<String> set = ConcurrentHashMap.newKeySet();return t -> set.add(keyExtractor.apply(t));}public static void distinctTest2() {List<Dish> dishList = REPEATED_DISHES.stream().filter(distinctByKey(dish -> String.join("-",dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString()))).collect(Collectors.toList());System.out.println(dishList);}public static void main(String[] args) {distinctTest2();}
}

打印结果:

[Dish{name='pork', vegetarian=false, calories=790, type=MEAT}, Dish{name='beef', vegetarian=false, calories=700, type=MEAT}]

其中重点代码为 distinctByKey 方法
如果难以理解的话,可以将以上代码用 JDK7 的方式写,如下:

public static void distinctTestWithJDK7() {List<Dish> dishList = new ArrayList<>();Set<String> set = ConcurrentHashMap.newKeySet();for (Dish dish : REPEATED_DISHES) {String key = String.join("-", dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString());if (set.add(key)) {dishList.add(dish);}}System.out.println(dishList);
}

重复的元素如何保留想要的那一个

① new Dish(“pork”, false, 790, Dish.Type.MEAT),
② new Dish(“pork”, false, 800, Dish.Type.MEAT),
③ new Dish(“beef”, false, 700, Dish.Type.MEAT),
④ new Dish(“beef”, false, 690, Dish.Type.MEAT)

从上面例子的打印结果可以看出保留的是①、③号元素,即两个元素如果重复则保留顺序靠前的,假设现在需求要保留低卡路里的呢
很容易可以想到可以先按照卡路里排序再去重,代码如下:

public static void distinctTest3() {Comparator<Dish> comparator = Comparator.comparing(Dish::getName).reversed().thenComparing(Dish::getCalories);List<Dish> dishList = REPEATED_DISHES.stream().sorted(comparator).filter(distinctByKey(dish -> String.join("-", dish.getName(), Boolean.toString(dish.isVegetarian()), dish.getType().toString()))).collect(Collectors.toList());System.out.println(dishList);
}

http://www.ppmy.cn/news/459566.html

相关文章

光学知识宝典之专业术语解读

光学知识宝典之专业术语解读 一、数值孔径N.A.&#xff1a; 当物件的入射光瞳时半角是u&#xff0c;而折射率是n&#xff0c;n*sin u是物体方数值孔径N.A.&#xff1b; 当物件的出射光瞳时半角是u’&#xff0c;而折射率是n’&#xff0c;n’*sin u’是物体方数值孔径N.A.&a…

相机标定原理

一.总体原理&#xff1a; 摄像机标定(Camera calibration)简单来说是从世界坐标系换到图像坐标系的过程&#xff0c;也就是求最终的投影矩阵的过程。 [1]基本的坐标系&#xff1a; 世界坐标系&#xff1b;相机坐标系&#xff1b;成像平面坐标系&#xff1b;像素坐标系 [2]一…

mysql 种子表_mysql之3种子查询

mysql有3种子查询&#xff0c;包括&#xff0c;where型&#xff0c;from型和exists型。 where型子查询 where后面跟的是条件表达式&#xff0c;条件为真时便取出该行&#xff0c;where型子查询是指内层的select语句的查询结果集充当外层select语句的where后面的条件表达式&…

坑爹的 Apple开发者种子计划

上周提交App审核&#xff0c;提交了几次都是几分钟就被拒。非常郁闷。我Xcode打包后&#xff0c;Validate也是OK的&#xff0c;上传也是OK的&#xff0c;上传后配置好等审核也是OK&#xff0c;就是过了几分钟立刻给来一个reject。说我是什么beta版本的东西打包的&#xff0c;我…

我的世界基岩版种子和java版种子_我的世界:基岩版4大最神奇种子?快点拿小本本记下来!...

大家好&#xff0c;这里是我的世界老炮&#xff0c;一个热爱我的世界的普通玩家。上次我给大家分享了几个JAVA版的地图种子&#xff0c;在投票里发现有很多基岩版玩家。所以这次就来分享一些我的世界基岩版种子。 因为我的世界的基岩版和Java版的开发程序不同&#xff0c;所以相…

我的世界java版种子多村庄_我的世界:1.14最强种子出现,出生遇见露天矿井,BUG村庄超炫!...

Mojang官推发布了一个1.14的种子&#xff1a;-9067906751430010912&#xff0c;理由呢是因为这快照版中&#xff0c;发现了一个村庄生成BUG&#xff0c;结果形成了一个天然的双层村庄&#xff01;号称1.14最强村庄。玩家表示如果正式版还有&#xff0c;一定要去这个村庄玩生存 …

我的世界java版种子多村庄_《我的世界》“村庄与掠夺”PE版种子推荐,出生点就7个村庄相连...

大家好,我是小胖子卡特曼,screw you guys, i m going home!如果你喜欢玩Minecraft和各种主机游戏,那么请关注我,我会努力每天给大家带来最新的游戏资讯和游戏体验分享。 亲爱的玩家自从《村庄与掠夺》这个版本的大更新后,整个MC发生了翻天覆地的变化,村庄相比较之前的版本…

我的世界java版种子多村庄_我的世界:粉丝推荐新版种子,出生附近就有11个村庄2个沙漠神殿...

大家好,我是小胖子卡特曼,screw you guys, i m going home!如果你喜欢玩Minecraft和各种主机游戏,那么请关注我,我会努力每天给大家带来最新的游戏资讯和游戏体验分享。 亲爱的玩家老爷们大家好,好久没有更新粉丝推荐的地图种子了。所以这一期我们继续来做粉丝推荐的种子内…