Guava使用案例

Guava使用案例

介绍

Guava是一种基于开源的Java库,谷歌很多项目使用它的很多核心库。这个库是为了方便编码,并减少编码错误。本篇文章主要以测试实例来讲解Guava比较常用的一些类和功能

基础工具类

Guava提供的Preconditions类中有一些基础前置检查方法,当检查不通过时迅速抛出错误。

        int i = 1;
        String a = "";
        int j[] = {4, 1, 2, 3};
        List l = Ints.asList(j);

        //前置检查方法
        //条件检查 不通过抛出IllegalArgumentException及自定义描述 自定义描述支持类似于String.format()的字符串拼接但是只能用%s
        checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
        //空值检查 不通过抛出NullPointerException及自定义描述
        checkNotNull(a, "Argument was null");
        //状态检查 不通过抛出IllegalStateException及自定义描述
        checkState(i >= 0, "Argument was %s but expected nonnegative", i);
        //检查列表、字符串或数组某一索引是否有效 不通过抛出IndexOutOfBoundsException
        checkElementIndex(2, l.size());
        //检查列表、字符串或数组某一位置是否有效 不通过抛出IndexOutOfBoundsException
        checkPositionIndex(2, l.size());
        //检查列表、字符串或数组某一范围是否有效 不通过抛出IndexOutOfBoundsException
        checkPositionIndexes(1, 2, l.size());

Objects方法提供了比较null的方法

        //Objects支持null的对比方法
        Objects.equal("a", "a"); // returns true
        Objects.equal(null, "a"); // returns false
        Objects.equal("a", null); // returns false
        Objects.equal(null, null); // returns true

排序器及对比器

        //对可排序类型做自然排序,如数字按大小,日期按先后排序
        List sortl = Ordering.natural().sortedCopy(l);
        //按对象的字符串形式做字典排序
        Ordering.usingToString();
        //自定义排序器 - 比较字符串长度
        Ordering byLengthOrdering = new Ordering() {
            public int compare(String left, String right) {
                return Ints.compare(left.length(), right.length());
            }
        };

        //排序器能够支持链式调用
        Ordering.natural()
                //获取语义相反的排序器
                .reverse()
                //使用当前排序器,但额外把null值排到最前面
                .nullsFirst()
                //使用当前排序器,但额外把null值排到最后面
                .nullsLast()
                //合成另一个比较器,以处理当前排序器中的相等情况。
                .compound(new Comparator() {
                    @Override
                    public int compare(Integer i, Integer j) {
                        return 0;
                    }
                })
                //基于处理类型T的排序器,返回该类型的可迭代对象Iterable的排序器
                .lexicographical();
        //对集合中元素调用Function,再按返回值用当前排序器排序
        class Foo {
            @Nullable
            String sortedBy;

            int notSortedBy;
        }
        Ordering ordering = Ordering.natural().nullsFirst().onResultOf(new Function() {
            public String apply(Foo foo) {
                return foo.sortedBy;
            }
        });
        //以下是具体排序方法
        //获取可迭代对象中最大的k个元素。
        Ordering.natural().greatestOf(l, 3);
        //判断可迭代对象是否已按排序器排序:允许有排序值相等的元素。
        Ordering.natural().isOrdered(l);
        //进行排序 并把排序结果copy成另一个集合
        List sortedl = Ordering.natural().sortedCopy(l);
        //返回两个参数中最小的那个。如果相等,则返回第一个参数。
        Ordering.natural().min(1, 2);
        //返回多个参数中最小的那个。如果有超过一个参数都最小,则返回第一个最小的参数。
        Ordering.natural().min(1, 2, 3, 4);
        //返回迭代器中最小的元素。如果可迭代对象中没有元素,则抛出NoSuchElementException。
        Ordering.natural().min(l);
        //同理max
        Ordering.natural().max(1, 2);

集合工具类

不可变集合:静态集合不可修改

        //以set为例
        ImmutableSet foob = ImmutableSet.of("a", "b", "c");
        ImmutableSet GOOGLE_COLORS = ImmutableSet.builder().add(new Color(0, 190, 255)).build();
        ImmutableSet defensiveCopy = ImmutableSet.copyOf(l);
        //同理类似的类就是集合前面加上Immutable 包括Collection

Multiset接口与MultiMap的系列集合:提供一个可以统计元素插入重复次数的无序集合

        //Multiset
        Multiset countMap = HashMultiset.create();
        //插入字符串"aa"2次
        countMap.add("aa", 3);
        //删除n次
        countMap.remove("aa", 1);
        //获取不重复的元素只获取到一个aa
        countMap.elementSet();
        //统计aa插入的次数
        countMap.count("aa");
        //和Map的entrySet类似 返回set类型的entry集 支持getElement()和getCount()方法
        countMap.entrySet();
        //给定元素固定次数
        countMap.setCount("aa", 5);
        //返回集合元素的总个数(重复的也记录 若想去重使用countMap.elementSet().size())
        countMap.size();

        //Multimap系列接口集合 和Multiset很相似 可以理解成Multiset的map版本 对应的实现将上面的对应map改为Multimap
        //Multimap主要是为了一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。
        //可以和传统的缓存Map>进行对比
        Multimap multimap = HashMultimap.create();
        //转换成Map>格式 且对转换后的map做操作会影响原有的Multimap
        multimap.asMap();
        //添加键到单个值的映射
        multimap.put(1, "a");
        //添加多个值的映射
        multimap.putAll(2, Lists.newArrayList("a", "b", "c", "d"));
        //移除键到值的映射;如果有这样的键值并成功移除,返回true。
        multimap.remove(2, "b");
        //移除一个key所有的映射值
        multimap.removeAll(1);
        //替换原有的映射值集合
        multimap.replaceValues(2, Lists.newArrayList("a", "b", "c", "d"));

BiMap系列接口集合:为了解决实现键值对的双向映射需要维护两个单独的map的问题

        //其对应的实现HashBiMap,ImmutableBiMap,EnumBiMap,EnumHashBiMap
        BiMap userId = HashBiMap.create();
        userId.put(1, "tom");
        //在想使用反向映射时使用inverse反转键值对关系
        userId.inverse().get("tom");

table :行 列 值

        Table weightedGraph = HashBasedTable.create();
        weightedGraph.put(1, 2, 4);
        weightedGraph.put(1, 3, 20);
        weightedGraph.put(2, 3, 5);
        //返回某一行的列对应值的map
        weightedGraph.row(1); // returns a Map mapping 2 to 4, 3 to 20
        //返回某一列的行对应值的map
        weightedGraph.column(3); // returns a Map mapping 1 to 20, 2 to 5

ClassToInstanceMap系列接口:是一种特殊的Map:它的键是类型,而值是符合键所指类型的对象

        //Guava提供了两种有用的实现:MutableClassToInstanceMap和 ImmutableClassToInstanceMap。
        ClassToInstanceMap numberDefaults = MutableClassToInstanceMap.create();
        numberDefaults.putInstance(Integer.class, Integer.valueOf(0));

RangeSet与RangeMap类:用来表述区间

        //当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略。
        RangeSet rangeSet = TreeRangeSet.create();
        rangeSet.add(Range.closed(1, 10)); // {[1,10]}
        rangeSet.add(Range.closedOpen(11, 15));//不相连区间:{[1,10], [11,15)}
        rangeSet.add(Range.closedOpen(15, 20)); //相连区间; {[1,10], [11,20)}
        rangeSet.add(Range.openClosed(0, 0)); //空区间; {[1,10], [11,20)}
        rangeSet.remove(Range.open(5, 10)); //分割[1, 10]; {[1,5], [10,10], [11,20)}

        //RangeMap类除了上述区间形容外 描述了”不相交的、非空的区间”到特定值的映射
        //和RangeSet不同,RangeMap不会合并相邻的映射,即便相邻的区间映射到相同的值。
        //Range区间也是Guava提供的一种新类型
        RangeMap rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}
        rangeMap.put(Range.open(3, 6), "bar"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo"}
        rangeMap.put(Range.open(10, 20), "foo"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo", (10,20) => "foo"}
        rangeMap.remove(Range.closed(5, 11)); //{[1,3] => "foo", (3,5) => "bar", (11,20) => "foo"}

集合类工具:对应集合类后面加上s

        //Lists
        List intList = Ints.asList(4, 5, 6);
        List theseElements = Lists.newArrayList("alpha", "beta", "gamma");
        //反转List
        List thoseElements = Lists.reverse(theseElements);
        //指定大小分割
        List> conList = Lists.partition(thoseElements, 3);
        //Sets
        Set wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
        Set primes = ImmutableSet.of("two", "three", "five", "seven");
        //联合两个set
        Sets.union(wordsWithPrimeLength, primes);
        //求交集
        Sets.intersection(wordsWithPrimeLength, primes);

        //Maps
        Map left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
        Map right = ImmutableMap.of("b", 2, "c", 4, "d", 4);
        //Maps.difference(Map, Map)用来比较两个Map以获取所有不同点。该方法返回MapDifference对象,把不同点的维恩图分解
        MapDifference diff = Maps.difference(left, right);
        //找到键值对都相等的
        diff.entriesInCommon(); // {"b" => 2}
        //找到键相等值不想等的
        diff.entriesDiffering(); // {"c" => 3},{"c" => 4}
        //找到只存在于左边的
        diff.entriesOnlyOnLeft(); // {"a" => 1}
        //找到只存在于右边的
        diff.entriesOnlyOnRight(); // {"d" => 5}

        //Iterables工具类连接两个Iterable(集合)
        Iterable concatenated = Iterables.concat(
                Ints.asList(1, 2, 3),
                Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6

        //Tables
        Table table = HashBasedTable.create();
        //把Table转置成Table 也就是行列互换
        Tables.transpose(table);

缓存

Guava提供了一套缓存类,如果希望系统牺牲内存空间去执行的更快速,可以使用它

        //cache缓存
        // LoadingCache是Cache的缓存实现
        LoadingCache cache = CacheBuilder.newBuilder()
                //设置缓存大小
                .maximumSize(1000)
                //设置到期时间
                .expireAfterWrite(10, TimeUnit.MINUTES)
                //设置缓存里的值两分钟刷新一次
                .refreshAfterWrite(2,TimeUnit.MINUTES)
                //开启缓存的统计功能
                .recordStats()
                //构建缓存
                .build(new CacheLoader() {
                    //此处实现如果根据key找不到value需要去如何获取
                    @Override
                    public Object load(String s) throws Exception {
                        return new Foo();
                    }

                    //如果批量加载有比反复调用load更优的方法则重写这个方法
                    @Override
                    public Map loadAll(Iterable keys) throws Exception {
                        return super.loadAll(keys);
                    }
                });
        cache.put("aa", new Foo());
        cache.get("aa");
        //除了在build的时候设置没有key的调用方法外我们还能在调用的时候手动写
        String key = "bb";
        cache.get(key, new Callable() {
            @Override
            public Object call() throws Exception {
                if (key == null)
                    return null;
                return new Foo();
            }
        });
        //缓存回收
        //除了不能超过大小和设定的时间自动回收外还可以调用方法手动回收
        cache.invalidate("aa");//个别清除
        cache.invalidateAll(Lists.newArrayList("cc","dd"));//批量清除
        cache.invalidateAll();//清除所有缓存项
        //清理的时机:在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话
        //如果想自己维护则可以调用Cache.cleanUp();
        cache.cleanUp();
        //另外有时候需要缓存中的数据做出变化重载一次,这个过程可以异步执行
        cache.refresh("aa");
        //还可以调用一下缓存的统计查看缓存的使用情况(需要在构建时开启)
        CacheStats cacheStats = cache.stats();
        cacheStats.hitRate();//缓存命中率
        cacheStats.averageLoadPenalty();//加载新值的平均时间,单位为纳秒
        cacheStats.evictionCount();//缓存项被回收的总数,不包括显式清除 
  

Guava封装流操作

简化流操作

        //guava提供了一个源与汇的概念对应读写字节字符共有4个类ByteSource,CharSource,ByteSink,CharSink
        URL url=new URL("this the resource url");
        File file=new File("your file path");
        Resources.toByteArray(url);//从url中获得字节数组
        Files.toByteArray(file);//从文件中获得字节数组
        CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);//获得字符源
        ByteSource byteSource=Files.asByteSource(file);//获得字节源
        Resources.asCharSource(url, Charsets.UTF_8);//从url中获得字符源
        Resources.asByteSource(url);//从url中获得字节源

        CharSink charSink=Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND);//获得字符汇 以append追加方式(覆盖方式可以省略)
        ByteSink byteSink = Files.asByteSink(file,FileWriteMode.APPEND);//或者字节汇 以append追加方式(覆盖方式可以省略)

        byteSource.asCharSource(Charsets.UTF_8);//字节源转字符源
        byteSink.asCharSink(Charsets.UTF_8);//字节汇转字符汇
        byte[] bytes=byteSource.read();//读取出byte[]
        String string=charSource.read();//读取出String
        byteSource.copyTo(byteSink);//字节源copy到汇 可以使用OutputStream
        charSource.copyTo(charSink);//字符源copy到汇 可以使用Appendable
        byteSink.write(bytes);//写入字节
        charSink.write(string);//写入字符(使用的是CharSequence,可以用String)
        //案例
        //逐行读取 以utf-8编码
        ImmutableList lines = Files.asCharSource(file, Charsets.UTF_8).readLines();
        //读取单词
        Multiset wordOccurrences = HashMultiset.create(
                //以空格拆分
                Splitter.on(CharMatcher.WHITESPACE)
                        .trimResults()
                        .omitEmptyStrings()
                        .split(Files.asCharSource(file, Charsets.UTF_8).read()));
        //获取文件按照sha1生成的hash码
        HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());
        //File的工具类Files
        //将url资源copy到file中
        Resources.asByteSource(url).copyTo(Files.asByteSink(file));

Guava其他实用工具类

并发多线程编程

        ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
        ListenableFuture explosion = service.submit(new Callable() {
            public String call() {
                return "this guava callable";
            }
        });
        Futures.addCallback(explosion, new FutureCallback() {
            //调用成功后执行的方法
            @Override
            public void onSuccess(Object o) {

            }
            ////调用失败后执行的方法
            public void onFailure(Throwable thrown) {
            }
        });

字符串处理

        //字符串拼接-连接器Joiner
        List names = Lists.newArrayList("John", null, "Jane", "Adam", "Tom");
        Joiner.on(",")
                //跳过null
                .skipNulls()//.useForNull("nameless")将null换为nameless
                .join(names); //John,Jane,Adam,Tom
        Map salary = Maps.newHashMap();
        salary.put("John", 1000);
        salary.put("Jane", 1500);
        String result = Joiner.on(" , ")
                //keyvalue中的拼接
                .withKeyValueSeparator(" = ")
                .join(salary);//John=1000,Jane=1500
        //字符串拆分--拆分器Splitter
        Splitter.on(',')
                //去除前后空格
                .trimResults()
                //去除空值
                .omitEmptyStrings()
                //限制拆分字符的长度
                .limit(3)
                .split("foo,bar,,   qux");
        //Splitter.on中支持CharMacher按字符匹配器 还能放入Pattern按正则 或者使用Splitter.onPattern(“\r?\n”)这样正则拆分

Guava提供了更加优质的散列算法

        //Person类
        class Person {
            int id;
            String firstName;
            String lastName;
            int birthYear;

            public Person(int id, String firstName, String lastName, int birthYear) {
                this.id = id;
                this.firstName = firstName;
                this.lastName = lastName;
                this.birthYear = birthYear;
            }
        }
        //hash过滤器
        Funnel personFunnel = new Funnel() {
            @Override
            public void funnel(Person person, PrimitiveSink into) {
                into
                        .putInt(person.id)
                        .putString(person.firstName, Charsets.UTF_8)
                        .putString(person.lastName, Charsets.UTF_8)
                        .putInt(person.birthYear);
            }
        };
        //使用md5算法计算hashcode
        HashFunction hf = Hashing.md5();//已经提供了md5(),sha1(),sha256(),sha512(),murmur3_128(),    murmur3_32(),goodFastHash(int bits)
        HashCode hc = hf.newHasher()
                //额外增加hash运算,可以用来放密码
                .putLong(123456789)
                .putString("数字", Charsets.UTF_8)
                .putObject(new Person(1,"张","三",2000), personFunnel)
                .hash();
        //BloomFilter布鲁姆过滤器
        BloomFilter friends = BloomFilter.create(personFunnel, 500, 0.01);
        Person dude=new Person(1,"aa","bb",1000);
        //假设多人
        List friendsList=Lists.newArrayList(dude);
        for(Person friend : friendsList) {
            friends.put(friend);
        }
        // 很久以后
        if (friends.mightContain(dude)) {
            //dude不是朋友还运行到这里的概率为1%
            //在这儿,我们可以在做进一步精确检查的同时触发一些异步加载
        }

函数式风格

//在guava中我们经常见到参数为function的方法 这里的方法为你提供了一种自定义转换函数的使用
        //拿Lists.transform举例子 可以新建一个函数进行自定义的元素转换 实际上实现并不复杂使用了一种命令模式
        List intTypeList=Lists.newArrayList();
        List stringTypeList = Lists.transform(intTypeList, new Function() {
            @Override
            public String apply(Integer integer) {
                return integer.toString()+integer.toString();
            }
        });
        //当然可以使用lambda表达式
        List stringTypeList2 = Lists.transform(intTypeList,(Integer integer)->integer.toString()+integer.toString());

Guava事件总线:内部的订阅通知模型,无需使用事件+事件listener模型 只有一个事件类

        // 给予事件类中方法以@Subscribe注解
        class EventBusChangeRecorder {
            @Subscribe
            public void recordCustomerChange(ChangeEvent e) {
                //事件发生时做一些事
                System.out.println("触发事件");
            }
        }
        //在程序的某处创建事件总线并注册事件
        EventBus eventBus=new EventBus();
        eventBus.register(new EventBusChangeRecorder());
        // 在之后的程序中 提交发生的事件
        ChangeEvent event=new ChangeEvent(new EventBusChangeRecorder());
        eventBus.post(event);
        //需要异步执行可以使用EventBus的子类AsyncEventBus

数学运算

        IntMath.checkedAdd(1,2);//加
        IntMath.checkedSubtract(1,2);//减
        IntMath.checkedMultiply(1,2);//乘
        IntMath.divide(2,1, RoundingMode.DOWN);//除 需要指定舍入格式
        IntMath.checkedPow(1,2);//次方
        IntMath.gcd(3,2);//最大公约数
        IntMath.mod(3, 5);//取模
        IntMath.pow(3, 5);//取幂
        IntMath.isPowerOfTwo(4);//是否2的幂
        IntMath.factorial(7);//阶乘
        IntMath.binomial(4, 3);//二项式系数
        //LongMath与BigIntegerMath同理
        //浮点数运算JDK的Math包已经涵盖比较全面 但是guava依旧提供了一些方法
        DoubleMath.isMathematicalInteger(4.0);//判断该浮点数是不是一个整数
        DoubleMath.roundToInt(3.14,RoundingMode.DOWN);//    舍入为int;对无限小数、溢出抛出异常同理tolong,toBigInteger
        DoubleMath.log2(3.14, RoundingMode.DOWN);//2的浮点对数,并舍入为int

反射工具

//类型工具TypeToken
        //获取原始类
        TypeToken stringTok = TypeToken.of(String.class);
        TypeToken intTok = TypeToken.of(Integer.class);
        //获取泛型类
        TypeToken> stringListTok = new TypeToken>() {};
        //获取通配符类型
        TypeToken> wildMapTok = new TypeToken>() {};
        stringTok.getType();//获得类型
        stringListTok.getRawType();//获得运行时类型
        stringListTok.getTypes();//获取所有的超类和接口及自身类,可以使用classes()和interfaces()方法允许你只浏览超类和接口类
        stringListTok.isArray();//检查类是否为接口
        //通过已经给泛型赋值的类来解析原有类型使用这种泛型后其方法返回的应有类型
        TypeToken> mapToken = new TypeToken>() {};
        TypeToken entrySetToken = mapToken.resolveType(Map.class.getMethod("entrySet").getReturnType());
        //将会返回TypeToken>>
        //Guava提供Invokable封装Method和Constructor
        Invokable, ?> invokable = new TypeToken>(){}.method(List.class.getMethod("size"));
        invokable.getReturnType(); // int
        invokable.isPublic();//判断方法是否是public
        invokable.isOverridable();//是否能被重写
        invokable.getParameters().get(0).isAnnotationPresent(Nullable.class);//方法的第一个参数是否被定义了注解@Nullable

        //包括提供了invoke方法放入实例和参数直接执行
        class MyInvocationHandler extends AbstractInvocationHandler {
            @Override
            protected Object handleInvocation(Object o, Method method, Object[] objects) throws Throwable {
                //加入前后处理方法
                return method.invoke(o,objects);
            }
        }
        //初始化类
        Reflection.initialize(Foo.class);
        //提供动态代理
        Foo foo = Reflection.newProxy(Foo.class, new MyInvocationHandler());
        //ClassPath类路径扫描工具 注意:所以不要将它用于关键任务生产任务
        ClassPath classpath = ClassPath.from(Map.class.getClassLoader());//通过classLoader获取
        //通过直接指定包路径来扫
        for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClasses("com.it.mypackage")) {
            classInfo.getName();//获得路径名
        }

更多Guava介绍参考

Guava开源Git链接

你可能感兴趣的:(java基本功)