[elegant code] 14 guava selection method and source code analysis of eventBus observer mode

Posted by davidz on Thu, 13 Jan 2022 14:53:50 +0100

[elegant code] 14 guava selection method and source code analysis of eventBus observer mode

Welcome to b station official account / public number [hexagon warrior Xia Ning], a man who wants to fill all the indicators. The article has been published in github directory Included.
The handsome and beautiful in front of the screen, if it helps you, please like it and add a collection, which is really important to me. Don't worry about where you go next time.

1. Background

google's guava is a very classic tool class, but after years of development of java, many methods have excellent alternatives. The following is a very classic method that I still feel in my daily development.

2. Basic tools

Because of the popularity of Optional and stream, the methods in this tool class are basically useless.

3. Collections (important)

This section mainly introduces the collection of guava. Although the tool class is a supplement to apache, it is too fancy.

3.1 immutable set

  1. General usage
public static void immutableOrdinary() {
    // General creation
    Set<Integer> set = ImmutableSet.of(1, 2, 3, 1);
    List<Integer> list = ImmutableList.of(1, 2, 3, 1);
    // The k-v continuous writing method of map is still very comfortable
    Map<Integer, Integer> map = ImmutableMap.of(1, 2, 3, 1);

    // Loop creation
    ImmutableSet.Builder<Integer> builder = ImmutableSet.<Integer>builder();
    for (int i = 0; i < 10; i++) {
        builder.add(i);
    }
    Set<Integer> build = builder.build();

    // The general list is immutable. It is worth noting that collections unmodifiableList()
    List<Integer> collect = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
    List<Integer> integers = ImmutableList.copyOf(collect);
}
  1. Insert performance comparisons in almost the same way
public static void effectiveList() {
    StopWatch sw = new StopWatch();
    sw.start("listAdd");
    Stream.Builder<Integer> builder1 = Stream.builder();
    for (int i = 0; i < 10000; i++) {
        builder1.add(i);
    }
    List<Integer> list = builder1.build().collect(Collectors.toList());
    sw.stop();

    sw.start("ImmutableListAdd");
    ImmutableList.Builder<Integer> builder = ImmutableList.builder();
    for (int i = 0; i < 10000; i++) {
        builder.add(i);
    }
    List<Integer> guava = builder.build();
    sw.stop();
    System.out.println(sw.prettyPrint());
}
  • You can see that the immutable set with obvious insertion speed is faster
---------------------------------------------
ns         %     Task name
---------------------------------------------
072909616  081%  listAdd
017632120  019%  ImmutableListAdd
  1. Get the speed comparison of a single element
List<Object> listUn = Collections.unmodifiableList(list);
sw.start("listGet");
list.get(0);
sw.stop();
sw.start("listUnGet");
listUn.get(0);
sw.stop();
sw.start("guavaGet");
guava.get(0);
sw.stop();
  • list is too slow. The other two are almost as efficient. guava still needs to be faster
---------------------------------------------
000018405  089%  listGet
000001323  006%  listUnGet
000000936  005%  guavaGet
  1. Cycle performance comparison
List<Object> listUn = Collections.unmodifiableList(list);
sw.start("listFor");
IntStream.range(0, 10000).boxed().forEach(list::get);
sw.stop();
sw.start("listUnFor");
IntStream.range(0, 10000).boxed().forEach(listUn::get);
sw.stop();
sw.start("guavaFor");
IntStream.range(0, 10000).boxed().forEach(guava::get);
sw.stop();
  • Consistent with the above results
---------------------------------------------
007031946  053%  listFor
003760153  028%  listUnFor
002458426  019%  guavaFor

As mentioned above, this is why I like guava very much
5. Difference and comparison

// The general list is immutable. It is worth noting that collections Unmodifiablelist() if the original list is changed, it will be changed
list.remove(0);
// That is, list and unmodifiableList will be one less

3.2 new set type

public static void newCollections(){
    // Only BiMap and Table are introduced here. Multiset and Multimap are embedded in the list
    BiMap<Integer,String> biMap=HashBiMap.create();
    biMap.put(1,"Zhang San");
    biMap.put(2,"Li Si");
    biMap.put(3,"Wang Wu");
    biMap.put(4,"Zhao Liu");
    biMap.put(5,"Li Qi");
    biMap.put(6,"Little");
    Integer result = biMap.inverse().get("Zhao Liu");
    // Output result 4
    System.out.println(result);
    // ===========================================================
    // table is a very interesting data structure and enlightening thinking, although I don't know what use it is
    /*
     *  Company: IBM, Microsoft, TCS
     *  IBM         -> {101:Mahesh, 102:Ramesh, 103:Suresh}
     *  Microsoft     -> {101:Sohan, 102:Mohan, 103:Rohan }
     *  TCS         -> {101:Ram, 102: Shyam, 103: Sunil }
     *
     * */
    //create a table
    Table<String, String, String> employeeTable = HashBasedTable.create();

    //initialize the table with employee details
    employeeTable.put("IBM", "101","Mahesh");
    employeeTable.put("IBM", "102","Ramesh");
    employeeTable.put("IBM", "103","Suresh");

    employeeTable.put("Microsoft", "111","Sohan");
    employeeTable.put("Microsoft", "112","Mohan");
    employeeTable.put("Microsoft", "113","Rohan");

    employeeTable.put("TCS", "121","Ram");
    employeeTable.put("TCS", "102","Shyam");
    employeeTable.put("TCS", "123","Sunil");

    //All row data
    System.out.println(employeeTable.cellSet());
    //All companies
    System.out.println(employeeTable.rowKeySet());
    //All employee numbers
    System.out.println(employeeTable.columnKeySet());
    //Name of all employees
    System.out.println(employeeTable.values());
    //All employees and employee numbers in the company
    System.out.println(employeeTable.rowMap());
    //Company and employee name corresponding to employee number
    System.out.println(employeeTable.columnMap());
}

The output is as follows

4
[(IBM,101)=Mahesh, (IBM,102)=Ramesh, (IBM,103)=Suresh, (Microsoft,111)=Sohan, (Microsoft,112)=Mohan, (Microsoft,113)=Rohan, (TCS,121)=Ram, (TCS,102)=Shyam, (TCS,123)=Sunil]
[IBM, Microsoft, TCS]
[101, 102, 103, 111, 112, 113, 121, 123]
[Mahesh, Ramesh, Suresh, Sohan, Mohan, Rohan, Ram, Shyam, Sunil]
{IBM={101=Mahesh, 102=Ramesh, 103=Suresh}, Microsoft={111=Sohan, 112=Mohan, 113=Rohan}, TCS={121=Ram, 102=Shyam, 123=Sunil}}
{101={IBM=Mahesh}, 102={IBM=Ramesh, TCS=Shyam}, 103={IBM=Suresh}, 111={Microsoft=Sohan}, 112={Microsoft=Mohan}, 113={Microsoft=Rohan}, 121={TCS=Ram}, 123={TCS=Sunil}}

3.3 collection tools

public static void Collections() {
    // Personally, I think the following methods are used. The first ones can be regarded as the memory implementation of redis intersection and union. The latter is the memory implementation of Cartesian product of database
    // Intersection, union
    Set<Integer> set1 = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
    Set<Integer> set2 = Stream.of(3, 4, 5, 6).collect(Collectors.toSet());
    // Find the difference set of set1
    System.out.println(Sets.difference(set1, set2));
    // Find the union of set1 and set2 difference sets
    System.out.println(Sets.symmetricDifference(set1, set2));
    // Find intersection
    System.out.println(Sets.intersection(set1, set2));
    // Cartesian product
    System.out.println(Sets.cartesianProduct(set1, set2));

    // Cartesian product
    List<Integer> list1 = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
    List<Integer> list2 = Stream.of(2, 3, 4, 5).collect(Collectors.toList());
    System.out.println(Lists.cartesianProduct(list1, list2));
}

The output is as follows

[1, 2]
[1, 2, 5, 6]
[3, 4]
[[1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 3], [3, 4], [3, 5], [3, 6], [4, 3], [4, 4], [4, 5], [4, 6]]

4. Cache (important)

This part will be shared separately in the next article.

5. Functional idioms

Like 2, it is not used much under the strong action of stream

6. Concurrency

The future part already has a completable future (introduced in Section 4 thread), which is very easy to use. Some of the functions of Service are very powerful, but they are generally not used.
The current limiting part will be shared in the next chapter

7. String processing Strings

apache is not bad either. This one is almost the same

8. Primitive types

The function looks great, but no actual use scenario has been found yet

9. Interval Ranges

The function looks great, but no actual use scenario has been found yet

10.I/O

apache's IOUtils are simpler to use than this

11. Hash (business specific)

The bloom filter provided in this can still be used when there are scenes in need. Hash algorithm also has a place in dealing with file consistency verification.
This part will be shared separately in the next article.

12. Event bus eventbus (important)

Super easy observer mode, useful can use this thing, write a lot more comfortable. I like the design idea of this piece very much and feel it necessary to expand it. It should be noted that @ Subscribe will be notified only if the method parameters are consistent with the delivered object parameters.

12.1 code usage

  1. Create observer
public static class eventBusObject {
    @Subscribe
    public void listenStr1(String str) {
        System.out.println(str + "listenStr1");
    }

    @Subscribe
    public void listenStr2(String str) {
        System.out.println(str + "listenStr2");
    }

    @Subscribe
    public void listenObj(Object str) {
        System.out.println(str + "listenStr1");
    }

    @Subscribe
    public void listenInt1(Integer str) {
        System.out.println(str + "listenInt1");
    }

    public void listenInt2(Integer str) {
        System.out.println(str + "listenInt2");
    }
}
  1. Method notification
public static void eventBus() {
    EventBus eventBus = new EventBus("eventBusTest");
    eventBus.register(new eventBusObject());
    eventBus.post(100);
    eventBus.post("I'm a string");
}
  1. Output results
100listenInt1
100listenStr1
 I'm a string listenStr1
 I'm a string listenStr2
 I'm a string listenStr1

12.2 core source code

  1. register
// Click to enter register. The method is as follows
public void register(Object object) {
	subscribers.register(object);
}
// Click again to enter the register. The method is as follows. Here it is mainly reflection, and then it is saved
void register(Object listener) {
	// The core method reflects the method to get @ Subscribe the annotation, and classifies the incoming class
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);

    for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();

      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }

      eventSubscribers.addAll(eventMethodsInListener);
    }
  }
  1. call
// Main method call post
public void post(Object event) {
	// This method conforms to the iterative set of event
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event));
    }
  }

// The main method calls the getSubscribers method
Iterator<Subscriber> getSubscribers(Object event) {
	// This method returns all the parent objects of event. The top level is Object. The following is to take the intersection of the two for assembly
    ImmutableSet<Class<?>> eventTypes = flattenHierarchy(event.getClass());

    List<Iterator<Subscriber>> subscriberIterators =
        Lists.newArrayListWithCapacity(eventTypes.size());

    for (Class<?> eventType : eventTypes) {
      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
      if (eventSubscribers != null) {
        // eager no-copy snapshot
        subscriberIterators.add(eventSubscribers.iterator());
      }
    }

    return Iterators.concat(subscriberIterators.iterator());
  } 
// Return to the main method dispatcher Dispatch is called, and the method is marked as 2.1
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {
  checkNotNull(event);
  checkNotNull(subscribers);
  Queue<Event> queueForThread = queue.get();
  queueForThread.offer(new Event(event, subscribers));

  if (!dispatching.get()) {
    dispatching.set(true);
    try {
      Event nextEvent;
      while ((nextEvent = queueForThread.poll()) != null) {
        while (nextEvent.subscribers.hasNext()) {
        	//  Core method call, marked 2.1.1
          nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
        }
      }
    } finally {
      dispatching.remove();
      queue.remove();
    }
  }
}
// From 2.1.1, you can see that the task is directly thrown into the thread pool
final void dispatchEvent(final Object event) {
    executor.execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              invokeSubscriberMethod(event);
            } catch (InvocationTargetException e) {
              bus.handleSubscriberException(e.getCause(), context(event));
            }
          }
        });
  }

13. Mathematical operation Math

Generally, it is not used. There are Math packages under apache, and the functions are basically the same

14. Reflection (less important)

Spring also brings this tool class. Personally, I feel that spring has been very excellent in dealing with routine cases. The generic direction is added here, which is relatively simple to use. However, in view of the few opportunities for ordinary use, it will not be expanded.

Topics: Java