原创

【编程系列】Guava-集合


我们在使用集合时,想到的基本就是ArrayList,LinkedList,HashMap,TreeMap等等在Jdk中存在的,为了实现一些复杂的功能,使用起来代码量并不少从而降低了可读性。


Guava Collections 可以帮助你的代码更简短精炼,更重要是它增强了代码的可读性。

本文使用的Guava版本如下:







        <!--guava-->        <dependency>            <groupId>com.google.guava</groupId>            <artifactId>guava</artifactId>            <version>26.0-jre</version>        </dependency>

一、 集合操作

  • 1 集合创建 
各种以s结尾的工厂类简化了集合的创建。在创建泛型实例的时候,它们使代码更加简洁



List<String> list =Lists.newArrayList();List<String> list2 = Lists.newArrayList("a","b","c");Set<Integer> set = Sets.newHashSet();

类型推导的功能,在Java 7中已经得到支持。


Map<String,List<String>> m=new HashMap<>();

可以返回原返回类型的任何子类型的对象



Maps.newHashMap();Maps.newLinkedHashMap();

MapMaker: 超级强大的 Map 构造类





//MapMaker 是用来构造 ConcurrentMap 的工具类。它可以用来构造ConcurrentHashMap:得到线程安全的hashMap//ConcurrentHashMap with concurrency level 8ConcurrentMap<String, Object> map1 = new MapMaker()        .concurrencyLevel(8)        .makeMap();
  • 2 集合转换、查找、过滤、分割 
Lists.transform可以很方便将一个集合转换成另外一种类型的集合。









List<String> listStr = Lists.newArrayList("1""2""3");//将字符串集合转换为Integer集合List<Integer> listInteger = Lists.transform(listStr, new Function<String, Integer>() {        @Nullable        @Override        public Integer apply(@Nullable String s) {            return Integer.valueOf(s);        }    });

在Java 8中的实现为


List<Integer> listInteger2 = listStr.stream().map(Integer::valueOf).collect(Collectors.toList());

Iterables.find查找集合首个匹配的元素









List<String> listStr = Lists.newArrayList("hello", "world", "hehe");//查找首个以h开头的值    String value = Iterables.find(listStr, new Predicate<String>() {    @Override    public boolean apply(String input) {        return input.startsWith("h");    }});

在Java 8中的实现为


String value2 = listStr.stream().findFirst().filter(input -> input.startsWith("h")).get();

Collections2.filter过滤集合中所有符合特定条件的元素。








List<String> listStr = Lists.newArrayList("hello", "world", "hehe");List<String> listWithH = Lists.newArrayList(Collections2.filter(listStr, new Predicate<String>() {    @Override    public boolean apply(@Nullable String s) {        return s.startsWith("h");    }}));

在Java 8中的实现为


List<String> listWithH2 = listStr.stream().filter(input -> input.startsWith("h")).collect(Collectors.toList());

Collections2.transform转换集合中元素的格式。








// 把一个 Set<Integer> 里面所有元素都转换成带格式的 String 来产生新的 Collection<String>:Collection<Integer> listInteger = Lists.newArrayList(1123,4590,5667,7800);Collection<String>  transformCollection =        Collections2.transform(listInteger, input -> new DecimalFormat("#,###").format(input));
System.out.println("transformCollection:"+transformCollection); // transformCollection:[1,123, 4,590, 5,667, 7,800]

Lists.partition可以将一个大的集合分割成小集合,适用于分批查询、插入等场景。




List<String> listStr = Lists.newArrayList("1""2""3","4","5","6","7");List<List<String>> batchList = Lists.partition(listStr,3);//被分割成了: [[1, 2, 3], [4, 5, 6], [7]]
  • 3 分组 
Maps.uniqueIndex可以根据集合中的唯一键把集合转换为以唯一键为key,以元素为value的Map. 
Multimaps.index可以根据集合中的相同的值把集合转换为以相同值为key,以List<元素>为value的Map. 相当于一键多值Map,关于Multimaps在下文做详细讲解










































public class MapsGroupTest{  class Apple {        int id;        String color;        public Apple(int id, String color) {            this.id = id;            this.color = color;        }        public int getId() {            return id;        }        public String getColor() {            return color;        }        @Override        public String toString() {            return MoreObjects.toStringHelper(Apple.class).add("id", id).add("color", color).toString();        }    }
@Test public void test1() { List<Apple> appleList = Lists.newArrayList(new Apple(1, "red"), new Apple(2, "red"), new Apple(3, "green"), new Apple(4, "green")); // 以主键为key,生成键值对:Map<id,Apple> Map<Integer, Apple> appleMap = Maps.uniqueIndex(appleList, new Function<Apple, Integer>() { @Nullable @Override public Integer apply(@Nullable Apple apple) { return apple.getId(); } }); // 相当于根据颜色分类:转为Map<颜色,Collection<Apple>> Multimap<String, Apple> multiMap = Multimaps.index(appleList, new Function<Apple, String>() { @Nullable @Override public String apply(@Nullable Apple apple) { return apple.getColor(); } }); }}

在Java 8中的实现为






List<Apple> appleList = Lists.newArrayList(new Apple(1, "red"), new Apple(2, "red"), new Apple(3, "green"), new Apple(4, "green"));Map<Integer, Apple> appleMap = appleList.stream().collect(Collectors.toMap(Apple::getId, apple -> apple));
Map<String, List<Apple>> groupsByColor = appleList.stream().collect(Collectors.groupingBy(Apple::getColor));
  • 4 其他

排序:




















List<Integer> numbers = new ArrayList<Integer>();numbers.add(new Integer(5));numbers.add(new Integer(2));numbers.add(new Integer(15));numbers.add(new Integer(51));numbers.add(new Integer(53));numbers.add(new Integer(35));numbers.add(new Integer(45));numbers.add(new Integer(32));numbers.add(new Integer(43));numbers.add(new Integer(16));
Ordering ordering = Ordering.natural();System.out.println("Input List: ");System.out.println(numbers);
Collections.sort(numbers,ordering);System.out.println("Sorted List: ");System.out.println(numbers);

集合的交集、并集和差集:




Sets.intersection(setA, setB);Sets.union(setA, setB);Sets.difference(setA, setB);

二、 不可变集合

不可变对象有很多优点,包括:

  • 当对象被不可信的库调用时,不可变形式是安全的;

  • 不可变对象被多个线程调用时,不存在竞态条件问题

  • 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);

  • 不可变对象因为有固定不变,可以作为常量来安全使用。

JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式,但我认为不够好:
  • 还在使用 Collections.unmodifiableXXX() ?Immutable Collections 这才是真正的不可修改的集合

  • 重要提示:所有Guava不可变集合的实现都不接受null值




























/** * Immutable Collections: 真正的不可修改的集合 * * @author: James * @date: Created in 2019/08/25 13:36 */@Testpublic void TestImmutableCollections() {
// 大家都用过 Collections.unmodifiableXXX() 来做一个不可修改的集合。 // 例如你要构造存储常量的 Set,你可以这样来做 : Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"})); Set<String> unmodifiableSet = Collections.unmodifiableSet(set); System.out.println("Collections.unmodifiableXXX():"+unmodifiableSet);
// 这看上去似乎不错,因为每次调 unmodifiableSet.add() 都会抛出一个 UnsupportedOperationException。 // 感觉安全了?慢!如果有人在原来的 set 上 add 或者 remove 元素会怎么样? // 结果 unmodifiableSet 也是被 add 或者 remove 元素了。而且构造这样一个简单的 set 写了两句长的代码。 // 下面看看 ImmutableSet 是怎么来做地更安全和简洁 : ImmutableSet<String> immutableSet = ImmutableSet.of("RED", "GREEN"); System.out.println("ImmutableSet:"+immutableSet);
// 就这样一句就够了,而且试图调 add 方法的时候,它一样会抛出 UnsupportedOperationException。 // 重要的是代码的可读性增强了不少,非常直观地展现了代码的用意。 // 如果像之前这个代码保护一个 set 怎么做呢?你可以 : ImmutableSet<String> immutableSet2 = ImmutableSet.copyOf(set);}

不可变集合的几种创建方式

  • copyOf方法



List<String> list = Arrays.asList("a", "b", "c");ImmutableSet<String> strings = ImmutableSet.copyOf(list);
  • of方法


ImmutableSet<String> of = ImmutableSet.of("a", "b", "c", "d", "e");
  • Builder工具


ImmutableSet<String> set = ImmutableSet.<String>builder().add("a").add("b").add("c").build();

三、 新的集合类型

1.Multiset
可以用两种方式看待Multiset:

  • 没有元素顺序限制的ArrayList<E>

  • Map<E, Integer>,键为元素,值为计数










Multiset<String> multiset = HashMultiset.create();multiset.add("a");multiset.add("a");multiset.add("b");int a = multiset.count("a");System.out.println("包含重复元素的个数  " + multiset.size());System.out.println("不包含重复元素的个数 " + multiset.elementSet().size());// 可以统计每个元素出现的个数System.out.println("元素a出现的次数 " + a);

输出结果:

Guava 提供了多种Multiset实现

SortedMultiset

SortedMultiset是Multiset接口的变种,它支持高效地获取指定范围的子集。









SortedMultiset<String> sortedMultiset = TreeMultiset.create();sortedMultiset.add("a");sortedMultiset.add("b");sortedMultiset.add("c");sortedMultiset.add("d");sortedMultiset.add("e");SortedMultiset<String> strings = sortedMultiset.subMultiset("a", BoundType.CLOSED, "e", BoundType.OPEN);strings.forEach(System.out::println);

输出结果:

2.Multimap: 一个 key 对应多个 value 
每个有经验的Java程序员都在某处实现过Map<K, List<V>>或Map<K, Set<V>>,并且要忍受这个结构的笨拙。例如,Map<K, Set<V>>通常用来表示非标定有向图。Guava的Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。





















ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();multimap.put("a", 1);multimap.put("a", 2);multimap.put("a", 3);multimap.put("a", 4);multimap.put("b", 1);System.out.println(multimap.size());
// asMap() 提供Map<K,Collection<V>>形式的视图 collections 中// a->{1,2,3,4}// b->{1}Collection<Collection<Integer>> collections = multimap.asMap().values();
// values {1,2,3,4,1}Collection<Integer> values = multimap.values();
Collection<Map.Entry<String, Integer>> entries = multimap.entries();entries.forEach(item -> { System.out.print("key--->" + item.getKey()); System.out.println(" value--->" + item.getValue());});

输出结果:


Multimap的各种实现

3.RangeSet












RangeSet<Integer> rangeSet = TreeRangeSet.create();// 闭区间 [1,10]rangeSet.add(Range.closed(1, 10));// 左闭右开区间 [11,15) 如果左边为11 则和上边的区间为不连续区间  如果左边为10 则两个区间进行合并rangeSet.add(Range.closedOpen(11, 15));// 返回包含指定元素的区间,如果没有这样的区间则返回nullRange<Integer> integerRange = rangeSet.rangeContaining(5);// 返回包含rangeSet 所有区间的最小区间Range<Integer> span = rangeSet.span();System.out.println(integerRange);System.out.println(span);

输出结果:

还存在一个RangeMap<Integer, String> ,但是和RangeSet不同的是RangeMap不会合并相邻的映射,即便相邻的区间映射到相同的值。

4.Table


















Table<String, Integer, String> table = HashBasedTable.create();table.put("a", 1, "第一个值");table.put("a", 2, "第二个值");table.put("a", 3, "第三个值");table.put("b", 1, "第一个值");table.put("b", 4, "第四个值");
Map<Integer, String> a = table.row("a");System.out.println("a: "+a);Map<Integer, String> b = table.row("b");System.out.println("b: "+b);Map<String, String> column = table.column(1);Set<Table.Cell<String, Integer, String>> cells = table.cellSet();System.out.println("cells: " + cells);Map<Integer, Map<String, String>> columnMap = table.columnMap();System.out.println("columnMap: " + columnMap);Collection<String> values = table.values();System.out.println(values);

输出结果:

BiMap: 保证 value 也不重复

BiMap<K, V>是特殊的Map:

  • 可以用 inverse()反转BiMap<K, V>的键值映射

  • 保证值是唯一的,因此 values()返回Set而不是普通的Collection

























/** * 双向 Map *  * 它的特点是它的 value 和它 key 一样也是不可重复的,换句话说它的 key 和 value 是等价的。 * * 如果你往 BiMap 的 value 里面放了重复的元素,就会得到 IllegalArgumentException。 *  * @param * @return void * @author: James * @date: 2019/8/25 */public static void BiMapTest() {
BiMap<Integer, String> empIDNameMap = HashBiMap.create();
empIDNameMap.put(new Integer(101), "Mahesh"); empIDNameMap.put(new Integer(102), "Sohan"); empIDNameMap.put(new Integer(103), "Ramesh"); // empIDNameMap.put(new Integer(104), "Mahesh"); // 报错:java.lang.IllegalArgumentException: value already present: Mahesh
//Emp Id of Employee "Mahesh" System.out.println(empIDNameMap.inverse().get("Mahesh"));}


正文到此结束
该篇文章的评论功能已被站长关闭
本文目录