Java-No.14 Google-Guava工具类整理

1、基本功能

1)字符串操作

package com.shma.guava.base;
import java.util.Map;
import org.junit.Test;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
/**
 * 字符串处理
 * @author admin
 *
 */
public class StringsTest {
 /**
  * 判断是否为空
  */
 @Test
 public void testIsNullOrEmpty() {
  String name = "";
  System.out.println(Strings.isNullOrEmpty(name)); //true
  
  String name2 = null;
  System.out.println(Strings.isNullOrEmpty(name2)); //true
  
  String name3 = "shma";
  System.out.println(Strings.isNullOrEmpty(name3)); //false
 }
 /**
  * 截取两个字符串的相同前缀
  */
 @Test
 public void testCommonPrefix() {
  // 两个字符串相同的前缀或者后缀
  String aString = "hi,a.shma.hello";
  String bString = "hi,b.jjq.hello";
  System.out.println(Strings.commonPrefix(aString, bString)); //hi,
 }
 
 /**
  * 截取两个字符串的相同后缀
  */
 @Test
 public void testCommonSuffix() {
  // 两个字符串相同的前缀或者后缀
  String aString = "hi,a.shma.hello";
  String bString = "hi,b.jjq.hello";
  System.out.println(Strings.commonSuffix(aString, bString)); //.hello
 }
 
 /**
  * 字符串补全
  */
 @Test
 public void testPad() {
  int minLength = 5;
  
  //末尾以0补全
  String padEndResult = Strings.padEnd("123", minLength, '0');
  System.out.println(padEndResult); //12300
  
  //开始补全
  String padStartResult = Strings.padStart("123", minLength, '0');
  System.out.println(padStartResult); //00123
  
 }
 
 /**
  * 拆分字符串
  */
 @Test
 public void testSplitter() {
  Iterable<String> iterable = Splitter.onPattern("[,,;]")
           .trimResults()
           .omitEmptyStrings()
           .split("马韶华,张琦,笑笑,,老李,类型 哈哈,非也; 宵夜 ");
  for(String item : iterable) {
   System.out.println(item);
  }
  
  //二次拆分
  String toSplitString = "a=1; b=2, c=3";
  Map<String, String> kvs = Splitter.onPattern("[;,]")
            .trimResults()
            .omitEmptyStrings()
            .withKeyValueSeparator("=")
            .split(toSplitString);
  
  System.out.println(kvs); //{a=1, b=2, c=3}
 }
 
 /**
  * 字符串合并
  */
 @Test
 public void testJoin() {
  
  String users = Joiner.on(",").join(new String[]{"张三", "李四", "王五"});
  System.out.println(users); //张三,李四,王五
  
  //将Map<String, String>合并
  Map<String, String> dataMap = Maps.newHashMap();
  dataMap.put("1001", "张三");
  dataMap.put("1002", "李四");
  dataMap.put("1003", "王五");
  dataMap.put("1004", "马六");
  String mapString = Joiner.on(",").withKeyValueSeparator("=").join(dataMap);
  
  System.out.println(mapString); // 1003=王五,1004=马六,1001=张三,1002=李四
  
  
 }
 
 /**
  * 重复输出次数
  */
 @Test
 public void testRepeat() {
  System.out.println(Strings.repeat("1234", 2)); // 12341234
 }
 
 
}

 2)对象封装:Objects

package com.shma.guava2.base;
import org.junit.Test;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
/**
 * 复写Object中的方法实现
 * @author admin
 *
 */
public class ObjectsTest {
 
 /**
  * 比较大小
  */
 @Test
 public void compareTest() {
  Person person1 = new Person("jqq", 24);
  Person person2 = new Person("jqq", 28);
  Person person3 = new Person("shma", 24);
  Person person4 = new Person("shma", 21);
  Person person5 = new Person("shma", 21);
  System.out.println(person1.compareTo(person2));
  System.out.println(person1.compareTo(person3));
  System.out.println(person3.compareTo(person4));
  System.out.println(person5.compareTo(person4));
 }
 
 /**
  * 实现toString
  */
 @Test
 public void toStringTest() {
  System.out.println(Objects.toStringHelper(this).add("name", "shma").toString());
  System.out.println(Objects.toStringHelper(Person.class).add("name", "shma").add("age", 23).toString());
  
  Person person1 = new Person("jqq", 24);
  Person person2 = new Person("jqq", 24);
  System.out.println(person1);
  System.out.println(person2);
  System.out.println(person1.hashCode());
  System.out.println(person2.hashCode());
  System.out.println(person1.equals(person2));
 }
 /**
  * 判断equals
  */
 @Test
 public void equalsTest() {
  System.out.println(Objects.equal(null, "a")); //false
  System.out.println(Objects.equal("a", "a")); //true
  System.out.println(Objects.equal("", "")); //true
  System.out.println(Objects.equal("a", "")); //false
  System.out.println(Objects.equal(null, null)); //true
  
  System.out.println(Objects.equal(new Person("shma", 23), new Person("shma", 23))); //false
  
  Person person = new Person("jqq", 24);
  System.out.println(Objects.equal(person, person)); //true
 }
 
 /**
  * 计算hashcode
  * 
  */
 @Test
 public void hashCodeTest() {
  System.out.println(Objects.hashCode("a")); //128
  System.out.println(Objects.hashCode("a")); //128
  
  System.out.println(Objects.hashCode("a", "b")); //4066
  System.out.println(Objects.hashCode("b", "a")); //4096
  
  System.out.println(Objects.hashCode("b", "a", "c")); //127075
  System.out.println(Objects.hashCode("a", "c", "b")); //126175
  
  System.out.println(Objects.hashCode(new Person("shma", 23))); //21648900
  System.out.println(Objects.hashCode(new Person("shma", 23))); //21846074
  
  Person person = new Person("jqq", 24);
  System.out.println(Objects.hashCode(person)); //13856786
  System.out.println(Objects.hashCode(person)); //13856786
 }
 
 class Person implements Comparable<Person> {
     public String name;
     public int age;
     Person(String name, int age) {
         this.name = name;
         this.age = age;
     }
  @Override
  public String toString() {
   return Objects.toStringHelper(Person.class)
         .add("name", this.name)
         .add("age", this.age)
         .toString();
  }
  @Override
  public int hashCode() {
   return Objects.hashCode(name, age);
  }
  @Override
  public boolean equals(Object obj) {
   if (this == obj)
    return true;
   if (obj == null)
    return false;
   if (getClass() != obj.getClass())
    return false;
   Person other = (Person) obj;
   if(this.name == other.name && this.age == other.age) 
    return true;
   return false;
  }
  @Override
  public int compareTo(Person perosn) {
   
   return ComparisonChain.start()
          .compare(this.name, perosn.name)
          .compare(this.age, perosn.age)
          .result();
  }
 }
 
 class Student implements Comparable<Student> {
  private String name;
  private int age;
  private int score;
  
  public Student() {
   super();
  }
  public Student(String name, int age, int score) {
   super();
   this.name = name;
   this.age = age;
   this.score = score;
  }
  @Override
  public String toString() {
   return Objects.toStringHelper(this)
        .add("name", name)
        .add("age", age)
        .add("score", score)
        .toString();
  }
  @Override
  public int hashCode() {
   return Objects.hashCode(name, age);
  }
  @Override
  public boolean equals(Object obj) {
   if (obj instanceof Student) {
             Student that = (Student) obj;
             return Objects.equal(name, that.name)
                     && Objects.equal(age, that.age)
                     && Objects.equal(score, that.score);
         }
         return false;
  }
  @Override
  public int compareTo(Student student) {
   return ComparisonChain.start()
          .compare(this.name, student.name)
          .compare(this.age, student.age)
          .compare(this.score, student.score)
          .result();
  }
 }
}

 3)Preconditions前置效验

package com.shma.guava.base;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.google.common.base.Preconditions;

/**
 * 前置条件:让方法调用的前置条件判断更简单
 * @author admin
 *
	1.checkArgument(boolean) :
		功能描述:检查boolean是否为真。 用作方法中检查参数
		失败时抛出的异常类型: IllegalArgumentException
	2.checkNotNull(T):     
		功能描述:检查value不为null, 直接返回value;
		失败时抛出的异常类型:NullPointerException
	3.checkState(boolean):
		功能描述:检查对象的一些状态,不依赖方法参数。 例如, Iterator可以用来next是否在remove之前被调用。
		失败时抛出的异常类型:IllegalStateException
	4.checkElementIndex(int index, int size):
		功能描述:检查index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。   
		失败时抛出的异常类型:IndexOutOfBoundsException
	5.checkPositionIndex(int index, int size):
		功能描述:检查位置index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。
		失败时抛出的异常类型:IndexOutOfBoundsExceptions
	6.checkPositionIndexes(int start, int end, int size):
		功能描述:检查[start, end)是一个长度为size的list, string或array合法的范围子集。伴随着错误信息。
		失败时抛出的异常类型:IndexOutOfBoundsException
 */
public class PreconditionsTest {

	public void paramCheck(String userName, int age) {
		try {
			checkInputParam(userName, age);
		} catch(Exception e) {
			System.out.println(e.getMessage());
			return;
		}
		
		System.out.println("验证成功");
	}
	
	
	public void checkInputParam(String userName, int age) throws Exception {
		Preconditions.checkNotNull(userName, "用户名不能为空");
		Preconditions.checkArgument(!userName.equals(""), "用户名不能为''");
		Preconditions.checkArgument(age > 0 && age <= 100, "年龄不能小于0或大于100");
		System.out.println("name:" + userName + ", age:" + age);
		
	}
	
	public void checkState(int size, int index) throws Exception {
		Preconditions.checkState(size > index, "index:" + index + ">=size:" + size);
	}
	
	public void checkPositionIndex(int size, int index) throws Exception {
		Preconditions.checkPositionIndex(index, size, "index:" + index + ">=size:" + size);
		
	}
	
	public void checkPositionIndexes(int size, int start, int end) throws Exception {
		Preconditions.checkPositionIndexes(start, end, size);
	}
	
	public void checkElementIndex(int size, int index) {
		Preconditions.checkElementIndex(index, size, "index:" + index + " is not in size:" + size);
	}
	
	@Test
	public void test01() {
		paramCheck(null, 23);
	}
	
	@Test
	public void test02() {
		paramCheck("", 23);
	}
	
	@Test
	public void test03() {
		paramCheck("shma", -1);
	}
	
	@Test
	public void test04() {
		paramCheck("shma", 120);
	}
	
	@Test
	public void test05() {
		paramCheck("shma", 26);
	}
	
	@Test
	public void test06() {
		List<Integer> intList=new ArrayList<Integer> ();
        for(int i=0;i<10;i++){            
            try {
                checkState(9, intList.size());
                intList.add(i);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }

        }
        
        try {
            checkPositionIndex(intList.size(),3);    
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkPositionIndex(intList.size(),13);    
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkPositionIndexes(intList.size(),3,7);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkPositionIndexes(intList.size(),3,17);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkPositionIndexes(intList.size(),13,17);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkElementIndex(intList.size(),6);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        try {
            checkElementIndex(intList.size(),16);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
	}
	
}

4)null处理:Optional

package com.shma.guava.base;

import java.util.Set;

import org.junit.Test;

import com.google.common.base.Optional;

/**
 * 非null判断
 * @author admin
 * 
 * 
Optional<T>.of(T)	为Optional赋值,当T为Null直接抛NullPointException,建议这个方法在调用的时候直接传常量,不要传变量
Optional<T>.fromNullable(T)	为Optional赋值,当T为Null则使用默认值。建议与or方法一起用,风骚无比
Optional<T>.absent()	为Optional赋值,采用默认值
T or(T)	当Optional的值为null时,使用or赋予的值返回。与fromNullable是一对好基友
T get()	当Optional的值为null时,抛出IllegalStateException,返回Optional的值
boolean isPresent()	如果Optional存在值,则返回True
T orNull()	当Optional的值为null时,则返回Null。否则返回Optional的值
Set<T> asSet()	将Optional中的值转为一个Set返回,当然只有一个值啦,或者为空,当值为null时。
 *
 */
public class OptionalTest {

	public void sayHello(String userName) {
		userName = Optional.fromNullable(userName).or("游客");
		System.out.println("您好:" + userName);
	}
	
	@Test
	public void testSayHello() {
		sayHello(null); //您好:游客
		sayHello("Tom"); //您好:Tom
	}
	
	@Test
	public void test() {
		Optional<Integer> possible = Optional.of(20);
		Optional<Integer> absentOpt = Optional.absent();
		Optional<Integer> NullableOpt=Optional.fromNullable(null);
        Optional<Integer> NoNullableOpt=Optional.fromNullable(10);
        if(possible.isPresent()){
            System.out.println("possible isPresent:"+possible.isPresent());
            System.out.println("possible value:"+possible.get());
        }
        if(absentOpt.isPresent()){
            System.out.println("absentOpt isPresent:"+absentOpt.isPresent()); ;
        }
        if(NullableOpt.isPresent()){
            System.out.println("fromNullableOpt isPresent:"+NullableOpt.isPresent()); ;
        }
        if(NoNullableOpt.isPresent()){
            System.out.println("NoNullableOpt isPresent:"+NoNullableOpt.isPresent()); ;
        }
	}
	
	@Test
	public void testMethodReturn() {
		Optional<Long> value = method();
		if(value.isPresent()) {
			System.out.println("获得返回值: " + value.get());
		} else {
			System.out.println("获得返回值: " + value.or(-1L));
		}
		
		System.out.println("获得返回值 orNull: " + value.orNull());
        
        Optional<Long> valueNoNull = methodNoNull();
        if(valueNoNull.isPresent()==true){
            Set<Long> set=valueNoNull.asSet();
            System.out.println("获得返回值 set 的 size : " + set.size());    
            System.out.println("获得返回值: " + valueNoNull.get());     
        }else{
            System.out.println("获得返回值: " + valueNoNull.or(-12L));    
        }
        
        System.out.println("获得返回值 orNull: " + valueNoNull.orNull());
	}
	
	private Optional<Long> method() {
        return Optional.fromNullable(null);
    }
	
    private Optional<Long> methodNoNull() {
        return Optional.fromNullable(15L);
    }
}

2、集合工具类

1)不可变集合

package com.shma.guava.collections;

import java.util.List;

import org.junit.Test;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;

/**
 * 不可变集合
 * 禁止修改(添加、删除),修改的时候会抛出异常java.lang.UnsupportedOperationException
 * 
	可变集合类型				可变集合源:JDK or Guava?	Guava不可变集合
	Collection				JDK						ImmutableCollection
	List					JDK						ImmutableList
	Set						JDK						ImmutableSet
	SortedSet/NavigableSet	JDK						ImmutableSortedSet
	Map						JDK						ImmutableMap
	SortedMap				JDK						ImmutableSortedMap
	Multiset				Guava					ImmutableMultiset
	SortedMultiset			Guava					ImmutableSortedMultiset
	Multimap				Guava					ImmutableMultimap
	ListMultimap			Guava					ImmutableListMultimap
	SetMultimap				Guava					ImmutableSetMultimap
	BiMap					Guava					ImmutableBiMap
	ClassToInstanceMap		Guava					ImmutableClassToInstanceMap
	Table					Guava					ImmutableTable
 * 
 * @author admin
 *
 */
public class ImmutableCollectionTest {

	/**
	 * 1) 通过of方法创建
	 */
	public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
			"red",
	        "orange",
	        "yellow",
	        "green",
	        "blue",
	        "purple");
	
	@Test
	public void testImmutableSet() {
		
		System.out.println(COLOR_NAMES); //不可变集合
		
		//2) 通过copyof方法创建
		List<String> dataList = Lists.newArrayList();
		dataList.add("zhangsan");
		dataList.add("lisi");
		
		ImmutableSet<String> dataImmutableSet = ImmutableSet.copyOf(dataList);
		System.out.println(dataImmutableSet);
		
		//3) Builder
		ImmutableSet<String> colorImmutableSet = ImmutableSet.<String>builder()
															.add("red")
															.add("orange")
															.add("yellow")
															.add("green")
															.add("blue")
															.add("purple")
															.build();
		System.out.println(colorImmutableSet);
		dataList.add("wangwu");
		
		System.out.println("list:" + dataList);
		System.out.println("immutable list:" + dataImmutableSet);
		
		
		//copyOf
		ImmutableSet<String> nameSet = ImmutableSet.of("shma", "jjq", "sunwei", "fanhui", "zhouwei", "naiwa", "wangjiakuo", "chengjunjie");
		
		ImmutableSet<String> limitSet = ImmutableSortedSet.copyOf(nameSet);
		System.out.println(limitSet);
		
		List<Integer> intList = Lists.newArrayList();
		for(int i=0; i<100; ++i) {
			intList.add(i);
		}
		
		ImmutableSet<Integer> limitIntSet = ImmutableSet.copyOf(intList.subList(0, 10));
		System.out.println(limitIntSet);
		System.out.println(intList);
		
	}
	
}

2)新增集合类

package com.shma.guava.collections;

import java.util.Collection;

import org.junit.Test;

import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.MutableClassToInstanceMap;
import com.google.common.collect.Range;
import com.google.common.collect.RangeMap;
import com.google.common.collect.RangeSet;
import com.google.common.collect.Table;
import com.google.common.collect.TreeRangeMap;
import com.google.common.collect.TreeRangeSet;

/**
 * guava新增加的集合类型
 * @author admin
 *
 */
public class MultiCollectionTest {
	
	/**
	 * RangeMap代表了非连续非空的range对应的集合。
	 * 不像RangeSet,RangeMap不会合并相邻的映射,甚至相邻的range对应的是相同的值
	 */
	@Test
	public void rangeMapTest() {
		RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1, 10), "foo"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.put(Range.open(3, 6), "bar"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.put(Range.open(10, 20), "foo"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.remove(Range.closed(5, 11)); 
        System.out.println("rangeMap:"+rangeMap);
	}
	
	/**
	 * RangeSet用来处理一系列不连续,非空的range。
	 * 当添加一个range到一个RangeSet之后,任何有连续的range将被自动合并,而空的range将被自动去除
	 */
	@Test
	public void rangeSetTest() {
		RangeSet<Integer> rangeSet = TreeRangeSet.create();
		rangeSet.add(Range.closed(1, 10));
		System.out.println(rangeSet);
		rangeSet.add(Range.openClosed(11, 15));
		System.out.println(rangeSet);
		rangeSet.add(Range.closedOpen(18, 20));
		System.out.println(rangeSet);
		rangeSet.add(Range.open(13, 21));
		System.out.println(rangeSet);
	}
	
	/**
	 * 当我们需要多个索引的数据结构的时候,
	 * 通常情况下,我们只能用这种丑陋的Map<FirstName, Map<LastName, Person>>来实现。
	 * 为此Guava提供了一个新的集合类型-Table集合类型,来支持这种数据结构的使用场景。
	 * Table支持“row”和“column”,而且提供多种视图。
	 */
	@Test
	public void tableTest() {
		
		Table<String, Integer, String> aTable = HashBasedTable.create();
		
		for(char a = 'A'; a <= 'E'; ++a) {
			for(int b = 1; b <= 5; ++b) {
				aTable.put(Character.toString(a), b, String.format("%s%d", a, b));
			}
		}
		
		System.out.println(aTable);
		
		System.out.println(aTable.column(2));
		System.out.println(aTable.column(3));
		
		System.out.println(aTable.row("D"));
		System.out.println(aTable.row("A"));
		
		System.out.println(aTable.get("A", 2));
		
		System.out.println(aTable.contains("D", 3));
		
		System.out.println(aTable.containsColumn(4));
		System.out.println(aTable.containsRow("C"));
		
		System.out.println(aTable.columnKeySet());
		System.out.println(aTable.columnMap());
		System.out.println(aTable.rowKeySet());
		System.out.println(aTable.rowMap());
		
		System.out.println(aTable.remove("A", 3));
	
		
		Table<Double, Double, String> table = HashBasedTable.create();
		table.put(23.0336165035d, 113.7616206363d, "广东省东莞市东莞市和兴街22-32号");
		
		
		//ClassToInstanceMap
		ClassToInstanceMap<String> instanceMapStr = MutableClassToInstanceMap.create();
		instanceMapStr.put(String.class, "shma");
		System.out.println(instanceMapStr.get(String.class));
		
		instanceMapStr.put(String.class, "jjq");
		System.out.println(instanceMapStr.getInstance(String.class));
	}
	
	/**
	 * key和value的双向关联的数据结构
	 * 
	 * 	Key-Value Map Impl     Value-Key Map Impl     Corresponding BiMap
  		HashMap                 	HashMap                 HashBiMap
  		ImmutableMap             	ImmutableMap            ImmutableBiMap
  		EnumMap                    EnumMap                 EnumBiMap
  		EnumMap                    HashMap                 EnumHashBiMap
	 */
	@Test
	public void bimapTest() {
		BiMap<Integer, String> biMap = HashBiMap.create();
		biMap.put(1, "zhangsan");
		biMap.put(2, "lisi");
		biMap.put(3, "wangwu");
		biMap.put(4, "maliu");
		
		System.out.println("id>>>name:" + biMap); //{4=maliu, 3=wangwu, 2=lisi, 1=zhangsan}
		System.out.println("name>>>id:" + biMap.inverse());//反转 {maliu=4, wangwu=3, lisi=2, zhangsan=1}
		
		biMap.inverse().put("zhaoqi", 5);
		System.out.println("id>>>name:" + biMap); //id>>>name:{5=zhaoqi, 4=maliu, 3=wangwu, 2=lisi, 1=zhangsan}
		System.out.println("name>>>id:" + biMap.inverse());//反转name>>>id:{zhaoqi=5, maliu=4, wangwu=3, lisi=2, zhangsan=1}
	
		//Bimap数据的强制唯一性,key和value必须唯一性
		//biMap.put(6, "maliu"); //java.lang.IllegalArgumentException
		//System.out.println(biMap);
		
		biMap.forcePut(6, "maliu");
		System.out.println(biMap); //{5=zhaoqi, 3=wangwu, 2=lisi, 1=zhangsan, 6=maliu}
		
		
		
	}
	
	/**
	 * 	Multimap:一个key对应多个value
	 *  Implementation            Keys 的行为类似          		Values的行为类似
  		ArrayListMultimap         HashMap                   ArrayList
  		HashMultimap              HashMap                   HashSet
  		LinkedListMultimap        LinkedHashMap*            LinkedList*
  		LinkedHashMultimap        LinkedHashMap             LinkedHashSet
  		TreeMultimap              TreeMap                   TreeSet
  		ImmutableListMultimap     ImmutableMap              ImmutableList
  		ImmutableSetMultimap      ImmutableMap              ImmutableSet
	 */
	@Test
	public void multimapTest() {
		Multimap<String, StudentScore> multimap = ArrayListMultimap.create();
		multimap.put("zhangsan", new StudentScore(1, 48));
		multimap.put("zhangsan", new StudentScore(2, 83));
		multimap.put("zhangsan", new StudentScore(3, 72));
		multimap.put("lisi", new StudentScore(1, 27));
		multimap.put("lisi", new StudentScore(2, 67));
		multimap.put("lisi", new StudentScore(3, 99));
		multimap.put("wangwu", new StudentScore(1, 80));
		multimap.put("wangwu", new StudentScore(2, 36));
		multimap.put("wangwu", new StudentScore(3, 65));
		
		System.out.println(multimap.get("zhangsan")); //[StudentScore{1, 48}, StudentScore{2, 83}, StudentScore{3, 72}]
		System.out.println(multimap.size()); //9
		System.out.println(multimap.keys()); //[wangwu x 3, lisi x 3, zhangsan x 3]
		
		for(String username : multimap.keys()) {
			System.out.println(multimap.get(username));
		}
		
		//修改数据
		Collection<StudentScore> wangwuCollection = multimap.get("wangwu");
		wangwuCollection.clear();
		wangwuCollection.add(new StudentScore(1, 20));
		wangwuCollection.add(new StudentScore(1, 30));
		wangwuCollection.add(new StudentScore(1, 40));
		
		System.out.println(multimap.get("wangwu"));
		
		
		for(StudentScore studentScore : multimap.values()) {
			System.out.println(studentScore);
		}
		
		multimap.removeAll("zhangsan");
		for(String username : multimap.keys()) {
			System.out.println(username + ":" + multimap.get(username));
		}
	}
	
	class StudentScore {
		private int CourseId;
		private int score;
		
		public StudentScore() {
			super();
		}

		public StudentScore(int courseId, int score) {
			super();
			CourseId = courseId;
			this.score = score;
		}

		public int getCourseId() {
			return CourseId;
		}

		public void setCourseId(int courseId) {
			CourseId = courseId;
		}

		public int getScore() {
			return score;
		}

		public void setScore(int score) {
			this.score = score;
		}

		@Override
		public String toString() {
			return Objects.toStringHelper(this).addValue(CourseId).addValue(score).toString();
		}
	}

	/**
	 * 可以在set中多次添加多个相同的元素
	 * 
	 * set:不允许重复,无顺序
	 * list:允许重复,有顺序
	 * multiset:允许重复,但是不保证顺序
	 */
	@Test
	public void multiSetTest() {
		String strWorld="wer|dffd|ddsa|dfd|dreg|de|dr|ce|ghrt|cf|gt|ser|tg|ghrt|cf|gt|" +
                "ser|tg|gt|kldf|dfg|vcd|fg|gt|ls|lser|dfr|wer|dffd|ddsa|dfd|dreg|de|dr|" +
                "ce|ghrt|cf|gt|ser|tg|gt|kldf|dfg|vcd|fg|gt|ls|lser|dfr";
		String[] words = strWorld.split("\\|");
		
		Multiset<String> multiset = HashMultiset.create();
		for(String word : words) {
			multiset.add(word);
		}
		
		for(String word : multiset.elementSet()) {
			System.out.println("word:" + word + ", count:" + multiset.count(word));
		}
		
		if(multiset.contains("lser")) {
			multiset.add("lser", 10);
		}
		
		multiset.setCount("ce", 19);
		multiset.setCount("cf", 3, 11);
		multiset.setCount("dr", 3, 11); //修改不成功
		
		for(Entry<String> entry : multiset.entrySet()) {
			System.out.println("word:" + entry.getElement() + ", count:" + entry.getCount());
		}
	}
	
	
	
}

3)迭代器:Iterators

package com.shma.guava.collections;

import java.util.Iterator;
import java.util.List;

import org.junit.Test;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;

/**
 * 新增迭代器
 * @author admin
 *
 */
public class IteratorsTest {

	/**
	 * 判断迭代器中的元素是否都满足某一个条件
	 * 全部满足才为true
	 */
	@Test
	public void testAll() {
		List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
		
		Predicate<String> predicate = new Predicate<String>() {

			@Override
			public boolean apply(String input) {
				return input.startsWith("P");
			}
		};
		
		boolean allIsStartsWithP = Iterators.all(friuts.iterator(), predicate);
		System.out.println(allIsStartsWithP); //false
	}
	
	/**
	 * 判断迭代器中是否有一个满足条件的记录
	 * 只要有一个满足 则返回true
	 */
	@Test
	public void testAny() {
			List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
			
			Predicate<String> predicate = new Predicate<String>() {

				@Override
				public boolean apply(String input) {
					return input.startsWith("A");
				}
			};
			
			boolean allIsStartsWithP = Iterators.any(friuts.iterator(), predicate);
			System.out.println(allIsStartsWithP); //true
	}
	
	/**
	 * get方法获得迭代器中的第x个元素
	 */
	@Test
	public void testGet() {
		List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
		System.out.println(Iterators.get(friuts.iterator(), 2)); //Peach
		
	}
	
	/**
	 * filter方法过滤符合条件的项
	 */
	@Test
	public void testFilter() {
		List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
		
		Predicate<String> predicate = new Predicate<String>() {

			@Override
			public boolean apply(String input) {
				return input.startsWith("P");
			}
		};
		
		Iterator<String> startPElements = Iterators.filter(friuts.iterator(), predicate);
		
		System.out.println(Iterators.toString(startPElements)); //[Pear, Peach]
	}
	
	/**
	 * find方法返回符合条件的第一个元素
	 */
	@Test
	public void testFind() {
		
		List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
		
		Predicate<String> predicate = new Predicate<String>() {

			@Override
			public boolean apply(String input) {
				return input.startsWith("P");
			}
		};
		
		String find = Iterators.find(friuts.iterator(), predicate);
		System.out.println(find); //Pear
		
	}
	
	/**
	 * 对迭代器元素做转换
	 */
	@Test
	public void testTransform() {
		List<String> friuts = Lists.newArrayList("Apple","Pear","Peach","Banana");
		
		//字符串转换成了其长度
		Iterator<Integer> lengths = Iterators.transform(friuts.iterator(), new Function<String, Integer>() {

			@Override
			public Integer apply(String input) {
				return input.length();
			}
		});
		
		System.out.println(Iterators.toString(lengths)); //[5, 4, 5, 6]
		
	}
}

3、本地缓存:Cache

1)guava提供了两种不同的方式来加载数据

  • CacheLoader:在build cache的时候定义一个CacheLoader来获取数据,适用的情况:有固定的方式可以根据key来加载或计算value的值,比如从数据库中获取数据

  • Callable:在get的时候传入一个Callable对象,适用的情况:如果从缓存中获取不到数据,则另外计算一个出来,并把计算结果加入到缓存中

package com.shma.guava.cache;

import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

public class CacheLoaderTest {

	/**
	 * 不需要延迟处理(泛型的方式封装)
	 * 
	 * @return
	 */
	public <K, V> LoadingCache<K, V> cached(CacheLoader<K, V> cacheLoader) {
		LoadingCache<K, V> cache = CacheBuilder.newBuilder().maximumSize(2)
				.weakKeys().softValues()
				.refreshAfterWrite(120, TimeUnit.SECONDS)
				.expireAfterWrite(10, TimeUnit.MINUTES)
				.removalListener(new RemovalListener<K, V>() {
					@Override
					public void onRemoval(RemovalNotification<K, V> rn) {
						System.out.println(rn.getKey() + "被移除");

					}
				}).build(cacheLoader);
		return cache;
	}

	/**
	 * 通过key获取value 调用方式 commonCache.get(key) ; return String
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */

	public LoadingCache<String, String> commonCache(final String key)
			throws Exception {
		LoadingCache<String, String> commonCache = cached(new CacheLoader<String, String>() {
			@Override
			public String load(String key) throws Exception {
				return "hello " + key + "!";
			}
		});
		return commonCache;
	}

	@Test
	public void testCache() throws Exception {
		LoadingCache<String, String> commonCache = commonCache("peida");
		System.out.println("peida:" + commonCache.get("peida"));
		System.out.println("harry:" + commonCache.get("harry"));
		System.out.println("lisa:" + commonCache.get("lisa"));
	}

	@Test
	public void testCacheLoader() {
		// 缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
		LoadingCache<Integer, Student> studentCache
		// CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
		= CacheBuilder.newBuilder()
		// 设置并发级别为8,并发级别是指可以同时写缓存的线程数
				.concurrencyLevel(8)
				// 设置写缓存后8秒钟过期
				.expireAfterWrite(8, TimeUnit.SECONDS)
				// 设置缓存容器的初始容量为10
				.initialCapacity(10)
				// 设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
				.maximumSize(100)
				// 设置要统计缓存的命中率
				.recordStats()
				// 设置缓存的移除通知
				.removalListener(new RemovalListener<Object, Object>() {
					@Override
					public void onRemoval(
							RemovalNotification<Object, Object> notification) {
						System.out.println(notification.getKey()
								+ " was removed, cause is "
								+ notification.getCause());
					}
				})
				// build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
				.build(new CacheLoader<Integer, Student>() {
					@Override
					public Student load(Integer key) throws Exception {
						System.out.println("load student " + key);
						Student student = new Student();
						student.setId(key);
						student.setName("name " + key);
						return student;
					}
				});
	}

	class Student {
		private int id;
		private String name;

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
	}
}
package com.shma.guava.cache;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

public class CallableTest {
	
	private static Cache<String, String> cacheFormCallable = null;

	/**
	 * 对需要延迟处理的可以采用这个机制;(泛型的方式封装)
	 * 
	 * @param <K>
	 * @param <V>
	 * @param key
	 * @param callable
	 * @return V
	 * @throws Exception
	 */
	public static <K, V> Cache<K, V> callableCached() throws Exception {
		Cache<K, V> cache = CacheBuilder.newBuilder().maximumSize(10000)
				.expireAfterWrite(10, TimeUnit.MINUTES).build();
		return cache;
	}

	private String getCallableCache(final String userName) {
		try {
			// Callable只有在缓存值不存在时,才会调用
			return cacheFormCallable.get(userName, new Callable<String>() {
				@Override
				public String call() throws Exception {
					System.out.println(userName + " from db");
					return "hello " + userName + "!";
				}
			});
		} catch (ExecutionException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Test
	public void testCallableCache() throws Exception {
		final String u1name = "peida";
		final String u2name = "jerry";
		final String u3name = "lisa";
		cacheFormCallable = callableCached();
		System.out.println("peida:" + getCallableCache(u1name));
		System.out.println("jerry:" + getCallableCache(u2name));
		System.out.println("lisa:" + getCallableCache(u3name));
		System.out.println("peida:" + getCallableCache(u1name));

	}
}

4)文件流处理

package com.shma.guava.files;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;

/**
 * 文件流处理
 * @author admin
 *
 */
public class FilesTest {

	/**
	 * 演示向文件中写入字节流
	 */
	@Test
	public void testWrite() {
		String content = "我是中国人\r\n我深爱着我的祖国";
		String descFilePath = "D:/2.txt";
		File descFile = new File(descFilePath);
		try {
			Files.write(content.getBytes(), descFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 一次性读取文件所有内容,小个文件
	 */
	@Test
	public void testReadLines() {
		String descFilePath = "D:/2.txt";
		File descFile = new File(descFilePath);
		try {
			List<String> lines = Files.readLines(descFile, Charsets.UTF_8);
			System.out.println(lines);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 分行读取
	 * @throws IOException 
	 */
	@Test
	public void testReadRowLines() throws IOException {
		String descFilePath = "D:/2.txt";
		File descFile = new File(descFilePath);
		CounterLine counterLine = new CounterLine();
		int counter = Files.readLines(descFile, Charsets.UTF_8, counterLine);
		System.out.println(counter);
		System.out.println(counterLine.getResult());
		
		List<String> lines = Files.readLines(descFile, Charsets.UTF_8, new StatusLine());
		System.out.println(lines);
	}
	
	class CounterLine implements LineProcessor<Integer> {

		private final AtomicInteger rowNum = new AtomicInteger();
		
		@Override
		public boolean processLine(String line) throws IOException {
			rowNum.incrementAndGet();
			return true;
		}

		@Override
		public Integer getResult() {
			return rowNum.get();
		}
		
	}
	
	class StatusLine implements LineProcessor<List<String>> {

		private final List<String> lines = Lists.newArrayList();
		
		@Override
		public boolean processLine(String line) throws IOException {
			lines.add(line);
			return true;
		}

		@Override
		public List<String> getResult() {
			return lines;
		}
		
	}
	
	/**
	 * 复制
	 * @throws IOException
	 */
	@Test
	public void testCopy() throws IOException {
		String sourceFilePath = "D:/2.txt";
		File sourceFile = new File(sourceFilePath);
		
		String targetFilePath = "D:/3.txt";
		File targetFile = new File(targetFilePath);
		
		Files.copy(sourceFile, targetFile);
	}
	
	/**
	 * 比较两个文件的内容是否完全一致
	 * @throws IOException 
	 */
	@Test
	public void testEquals() throws IOException {
		String sourceFilePath = "D:/2.txt";
		File sourceFile = new File(sourceFilePath);
		
		String targetFilePath = "D:/3.txt";
		File targetFile = new File(targetFilePath);
		
		System.out.println(Files.equal(sourceFile, targetFile)); //true
		
		System.out.println(Files.getNameWithoutExtension(targetFilePath)); //3
		
		Files.move(sourceFile, new File("D:/4.txt"));
	}
}

5)net

package com.shma.guava;

import java.net.InetAddress;

import org.junit.Test;

import com.google.common.net.HostAndPort;
import com.google.common.net.HttpHeaders;
import com.google.common.net.InetAddresses;

/**
 * 网络编程
 * @author admin
 *
 */
public class NetTest {

	/**
	 * 定义头信息常量
	 */
	@Test
	public void testHttpHeaders() {
		System.out.println(HttpHeaders.X_FORWARDED_FOR); //X-Forwarded-For
		System.out.println(HttpHeaders.COOKIE); //Cookie
	}
	
	@Test
	public void testHostAndPort() {
		HostAndPort hostAndPort = HostAndPort.fromString("192.168.28.133:8080");
		System.out.println("host:" + hostAndPort.getHostText()); //host:192.168.28.133
		System.out.println("port:" + hostAndPort.getPort()); //port:8080
	}
	
	@Test
	public void testInetAddresses() {
		InetAddress inetAddress = InetAddresses.forUriString("192.168.28.133");
		System.out.println(inetAddress);
	}
}

6)反射

package com.shma.guava.reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;

import org.junit.Test;

import com.google.common.collect.Lists;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Reflection;
import com.google.common.reflect.TypeToken;

/**
 * 反射
 * @author admin
 *
 */
public class ReflectTest {

	/**
	 * TypeToken类是用来解决java运行时泛型类型被擦除的问题
	 */
	@Test
	public void testTypeToken() {
		
		//泛型的运行时类型擦除
		ArrayList<Integer> intList = Lists.newArrayList();
		ArrayList<String> stringList = Lists.newArrayList();
		
		System.out.println("intList type is : " + intList.getClass());
		System.out.println("stringList type is : " + stringList.getClass());
		
		System.out.println(intList.getClass().isAssignableFrom(stringList.getClass())); //true
		
		//认为stringList和intList的类型是一样的,这就是所谓的泛型类型擦除。
		//运行时我们不知道泛型类型的类型参数是什么了
		
		TypeToken<ArrayList<String>> typeToken = new TypeToken<ArrayList<String>>() {};
        TypeToken<?> genericTypeToken = typeToken.resolveType(ArrayList.class.getTypeParameters()[0]);
        System.out.println(genericTypeToken.getType()); //class java.lang.String
		
	}
	
	@Test
	public void testInvokable() throws NoSuchMethodException, SecurityException {
		//判断方法是否为public
		Method method = ReflectTest.class.getMethod("testTypeToken", null);
		System.out.println(Modifier.isPublic(method.getModifiers())); //true
		
		System.out.println(Invokable.from(method).isPublic()); //true
		
		System.out.println(Invokable.from(ReflectTest.class.getConstructor(null)).isPackagePrivate());
	}
	
	/**
	 * 动态代理
	 */
	@Test
	public void testProxy() {
		InvocationHandler invocationHandler = new MyInvocationHandler(new MyFoo());

        // Guava Dynamic Proxy implement
        IFoo foo = Reflection.newProxy(IFoo.class, invocationHandler);
        foo.doSomething();
        //jdk Dynamic proxy implement
        IFoo jdkFoo = (IFoo) Proxy.newProxyInstance(
                IFoo.class.getClassLoader(),
                new Class<?>[]{IFoo.class},
                invocationHandler);
        jdkFoo.doSomething();

	    
	}
	
	public class MyInvocationHandler implements InvocationHandler {
	    
		private Object concreteClass;  
	      
	    public MyInvocationHandler(Object concreteClass){  
	        this.concreteClass=concreteClass;  
	    }
		
		public Object invoke(Object proxy, Method method, Object[] args)
	                throws Throwable {
	            System.out.println("proxy println something");
	            Object object = method.invoke(concreteClass, args);//普通的Java反射代码,通过反射执行某个类的某方法  
	            System.out.println("After invoke method...");  
	            return object;
	        }
	}

	public interface IFoo {
		void doSomething();
	}
	
	public class MyFoo implements IFoo {

		@Override
		public void doSomething() {
			System.out.println("MyFoo doSomething...");
			
		}
		
	}

}

7)并发

package com.shma.guava.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.RateLimiter;

public class ConcurrentTest {

	@Test
	public void testListenableFuture() throws InterruptedException, ExecutionException {
		ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
        final ListenableFuture<Integer> listenableFuture = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("call execute.2.");
                TimeUnit.SECONDS.sleep(1);
                return 7;
            }
        });
        
        System.out.println(listenableFuture.get());
        
//        listenableFuture.addListener(new Runnable() {
//            @Override
//            public void run() {
//                try {
//                    System.out.println("get listenable future's result " + listenableFuture.get());
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                } catch (ExecutionException e) {
//                    e.printStackTrace();
//                }
//            }
//        }, executorService);
        
        Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {
            @Override
            public void onSuccess(Integer result) {
                System.out.println("get listenable future's result with callback " + result);
            }

            @Override
            public void onFailure(Throwable t) {
                t.printStackTrace();
            }
        });

	}
 
    /**
     * RateLimiter类似于JDK的信号量Semphore,他用来限制对资源并发访问的线程数
     */
	@Test
    public void testRateLimiter() {
        ListeningExecutorService executorService = MoreExecutors
                .listeningDecorator(Executors.newCachedThreadPool());
 
        RateLimiter limiter = RateLimiter.create(5.0); // 每秒不超过4个任务被提交
 
        for (int i = 0; i < 10; i++) {
            limiter.acquire(); // 请求RateLimiter, 超过permits会被阻塞
 
            final ListenableFuture<Integer> listenableFuture = executorService
                    .submit(new Task("is "+ i));
        }
    }
 
	@Test
    public void testListenableFuture2() {
        ListeningExecutorService executorService = MoreExecutors
                .listeningDecorator(Executors.newCachedThreadPool());
 
        final ListenableFuture<Integer> listenableFuture = executorService
                .submit(new Task("testListenableFuture"));
 
         
        //同步获取调用结果
        try {
            System.out.println(listenableFuture.get());
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } catch (ExecutionException e1) {
            e1.printStackTrace();
        }
         
        //第一种方式
        listenableFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("get listenable future's result "
                            + listenableFuture.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }, executorService);
 
        //第二种方式
        Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {
            @Override
            public void onSuccess(Integer result) {
                System.out
                        .println("get listenable future's result with callback "
                                + result);
            }
 
            @Override
            public void onFailure(Throwable t) {
                t.printStackTrace();
            }
        });
    }
}
 
class Task implements Callable<Integer> {
    String str;
    public Task(String str){
        this.str = str;
    }
    @Override
    public Integer call() throws Exception {
        System.out.println("call execute.." + str);
        TimeUnit.SECONDS.sleep(1);
        return 7;
    }
}
package com.shma.guava.concurrent;

import java.util.ArrayList;
import java.util.List;

import com.google.common.util.concurrent.Monitor;

/**
 *  * 通过Monitor的Guard进行条件阻塞  
 */
public class MonitorSample {
	private List<String> list = new ArrayList<String>();
	private static final int MAX_SIZE = 10;
	private Monitor monitor = new Monitor();
	private Monitor.Guard listBelowCapacity = new Monitor.Guard(monitor) {
		@Override
		public boolean isSatisfied() {
			return list.size() < MAX_SIZE;
		}
	};

	public void addToList(String item) throws InterruptedException {
		monitor.enterWhen(listBelowCapacity); // Guard(形如Condition),不满足则阻塞,而且我们并没有在Guard进行任何通知操作
		try {
			list.add(item);
		} finally {
			monitor.leave();
		}
	}
}


转载于:https://my.oschina.net/shma1664/blog/596904

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值