Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


StringJoiner

 

SStringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix.

Prior to adding something to the StringJoiner, its sj.toString() method will, by default, return prefix + suffix. However, if the setEmptyValue method is called, the emptyValue supplied will be returned instead. This can be used, for example, when creating a string using set notation to indicate an empty set, i.e. "{}", where the prefix is "{", the suffix is "}" and nothing has been added to the StringJoiner.

import java.util.StringJoiner;

public class StringJoinerDemo {

	public static void main(String[] args) {
		String delimeter = "-";
		StringJoiner names = new StringJoiner(delimeter);
		
		names.add("Laulau");
		names.add("Kinga");
		names.add("Fusi");
		
		System.out.println(names);
	}
}

Result:

Laulau-Kinga-Fusi

 

Using prefix and suffix

public static void demoWithPrefixAndSuffix() {
		String delimeter = ",";
		String prefix = "(";
		String suffix = ")";
		StringJoiner names = new StringJoiner(delimeter, prefix, suffix);
		
		names.add("Laulau");
		names.add("Kinga");
		names.add("Fusi");
		
		System.out.println(names);
}

Result:

(Laulau,Kinga,Fusi)

Using merge

public static void demoWithMerge() {
		String delimeter = ",";
		String prefix = "(";
		String suffix = ")";
		StringJoiner names1 = new StringJoiner(delimeter, prefix, suffix);

		names1.add("Laulau");
		names1.add("Kinga");
		names1.add("Fusi");

		System.out.println(names1);

		StringJoiner names2 = new StringJoiner(delimeter, prefix, suffix);

		names2.add("John");
		names2.add("Peter");
		names2.add("Andrew");

		System.out.println(names2);
		
		StringJoiner names3 = names1.merge(names2);
		
		System.out.println(names3);
}

Result:

(Laulau,Kinga,Fusi)
(John,Peter,Andrew)
(Laulau,Kinga,Fusi,John,Peter,Andrew)

 

 

 

 

 

 

 

August 5, 2019

Collectors class

 

The Collectors class is used with Stream.collect() to package elements for a terminal operation. Something like this:

List<String> alphabet = Arrays.asList("a", "b", "c", "d");
List<String> otherAlphabet = alphabet.stream()
  .collect(toList());

The Collectors class has three important and useful methods: Collectors.toList, Collectors.toMap, and Collectors.toSet.

You have seen toList() above. Here is toMap().

Map<String, Integer> alphabetMap = alphabet.stream()
  .collect(toMap(letter -> letter, String::length))

Here is Collectors.toSet().

Set<String> result = alphabet.stream()
  .collect(toSet());

Here is Collectors.toCollection().

List<String> alphabetList = alphabet.stream()
  .collect(toCollection(LinkedList::new))

Here is Collectors.joining().

String joins = alphabet.stream()
  .collect(joining(" "));
"a b c d"

Here is Collectors.counting().

Long count = alphabet.stream()
  .collect(counting());

Result

4

 

Collectors.summarizingDouble() / Collectors.summarizingInt() / Collectors.summarizingLong(). This returns a SummaryStatistics class that contains statistical information about the elements.

public static void demoSummarizing() {
		DoubleSummaryStatistics alphabetStats = alphabet.stream()
				.collect(Collectors.summarizingDouble(letter -> letter.length()));

		System.out.println("average: "+alphabetStats.getAverage());
		System.out.println("count: "+alphabetStats.getCount());
		System.out.println("max: "+alphabetStats.getMax());
		System.out.println("sum: "+alphabetStats.getSum());
		System.out.println("min: "+alphabetStats.getMin());
}

Result

average: 1.0
count: 5
max: 1.0
sum: 5.0
min: 1.0

Collectors.groupingBy() is used for grouping objects by some property and storing results in a Map instance.

User[name=Aaron Holloway,age=16,height=6.626572070342046,gender=FEMALE]
User[name=Nora Reyes,age=8,height=5.909744090999723,gender=FEMALE]
User[name=Nia Allen,age=29,height=6.415385593055398,gender=MALE]
User[name=Quinton Hoover,age=25,height=6.549790026231361,gender=MALE]
User[name=Alannah Salinas,age=20,height=6.4459305179442605,gender=FEMALE]
User[name=Jaylin Fleming,age=13,height=6.265460947673736,gender=FEMALE]
public static void demoGroupBy() {
		
		System.out.println("\ndemo group by\n");
		
		Map<String, Set<User>> userMap = users.stream()
				.collect(Collectors.groupingBy(user -> user.getGender(), Collectors.toSet()));

		Set<User> males = userMap.get("MALE");

		System.out.println("male size: "+males.size()+" . "+males);
		
		Set<User> females = userMap.get("FEMALE");
		
		System.out.println("female size: "+females.size()+" . "+females);
}

Result

demo group by

male size: 2 . [User[name=Nia Allen,age=29,height=6.415385593055398,gender=MALE], User[name=Quinton Hoover,age=25,height=6.549790026231361,gender=MALE]]
female size: 4 . [User[name=Aaron Holloway,age=16,height=6.626572070342046,gender=FEMALE], User[name=Jaylin Fleming,age=13,height=6.265460947673736,gender=FEMALE], User[name=Alannah Salinas,age=20,height=6.4459305179442605,gender=FEMALE], User[name=Nora Reyes,age=8,height=5.909744090999723,gender=FEMALE]]

Collectors.partioningBy() accepts a Predicate instance and collects Stream elements into a Map instance that stores Boolean values as keys and collections as values.

User[name=Johanna Dickson,age=22,height=6.4127723755001504,gender=FEMALE]
User[name=Julian Jenkins,age=23,height=6.775657455958665,gender=FEMALE]
User[name=Antwan Wheeler,age=26,height=6.637100653384031,gender=FEMALE]
User[name=Samantha Parsons,age=14,height=5.622675954086426,gender=MALE]
User[name=Nia Camacho,age=5,height=6.9896558044147294,gender=MALE]
User[name=Giselle Morris,age=6,height=5.580378836408688,gender=MALE]

Result

demo partioning by

adult size: 3 . [User[name=Johanna Dickson,age=22,height=6.4127723755001504,gender=FEMALE], User[name=Julian Jenkins,age=23,height=6.775657455958665,gender=FEMALE], User[name=Antwan Wheeler,age=26,height=6.637100653384031,gender=FEMALE]]
youngens size: 3 . [User[name=Samantha Parsons,age=14,height=5.622675954086426,gender=MALE], User[name=Nia Camacho,age=5,height=6.9896558044147294,gender=MALE], User[name=Giselle Morris,age=6,height=5.580378836408688,gender=MALE]]

 

 

 

 

 

 

August 5, 2019

Foreach

 

Foreach is a way of traversing a list like for loop and while loop. Variables from outside of Foreach must be final or effective final.

Foreach traversing through a List

public static void foreachWithList() {
		List<String> items = new ArrayList<>();
		items.add("A");
		items.add("B");
		items.add("C");
		items.add("D");
		items.add("E");

		// lambda
		// Output : A,B,C,D,E
		items.forEach(item -> System.out.println(item));

		// Output : C
		items.forEach(item -> {
			if ("C".equals(item)) {
				System.out.println(item);
			}
		});

		// method reference
		// Output : A,B,C,D,E
		items.forEach(System.out::println);

		// Stream and filter
		// Output : B
		items.stream().filter(s -> s.contains("B")).forEach(System.out::println);
}

Foreach traversing through a Map

public static void foreachWithMap() {
		Map<String, Integer> items = new HashMap<>();
		items.put("A", 10);
		items.put("B", 20);
		items.put("C", 30);
		items.put("D", 40);
		items.put("E", 50);
		items.put("F", 60);
		
		items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v));
		
		items.forEach((k,v)->{
			System.out.println("Item : " + k + " Count : " + v);
			if("E".equals(k)){
				System.out.println("Hello E");
			}
		});
}

 

August 5, 2019

Streams

 

The key aspect of Stream API is it’s the ability to perform very sophisticated operations such as search, filter, map, or otherwise manipulate data. Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result.

As a general rule, a stream operation by itself does not modify the data source. For example, sorting a stream does not change the order of the source. Rather, sorting stream results in creating a new stream that produces the sorted result.

Features:

  • A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.
  • Streams don’t change the original data structure, they only provide the result as per the pipelined methods.
  • Each intermediate operation is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined. Terminal operations mark the end of the stream and return the result.

Intermediate Operations On Streams (Intermediate operation is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined.)

streams-f1

  • map: The map method is used to map the items in the collection to other objects according to the Function passed as an argument. map() creates a new stream after applying a function to all elements of the original stream.
List number = Arrays.asList(2,3,4,5);
List square = number.stream().map(x->x*x).collect(Collectors.toList());

String number = Stream.of(9, 4, 6, 1, 3, 5).map(num -> num + "").collect(Collectors.joining(","));
// 9,4,6,1,3,5

double average = Stream.of("1","5","10","4").mapToInt(Integer::parseInt).average().getAsDouble();
// 5
  • filter: The filter method is used to select elements as per the Predicate passed as argument.
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().filter(s->s.startsWith("S")).collect(Collectors.toList());
  • sorted: The sorted method is used to sort the stream.
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().sorted().collect(Collectors.toList());

List<Integer> numbers = Stream.of(9,4,6,1,3,5).sorted((a, b) -> {

	return a.compareTo(b);

}).collect(Collectors.toList());

System.out.println("numbers: " + numbers.toString());
numbers: [1, 3, 4, 5, 6, 9]

Terminal Operations On Streams(Terminal operations mark the end of the stream and return the result)

  • collect: The collect method is used to return the result of the intermediate operations performed on the stream. It is a way to get elements out of the stream.
List number = Arrays.asList(2,3,4,5,3);
Set square = number.stream().map(x->x*x).collect(Collectors.toSet());

Set<Integer> set = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));

//[1, 2, 3]
		
Map<Integer,String> map = Stream.of(1, 2, 3).collect(Collectors.toConcurrentMap(num -> num, num -> num+""));
	
//{1=1, 2=2, 3=3}
  • forEach: The forEach method is used to iterate through every element of the stream.
List number = Arrays.asList(2,3,4,5);
number.stream().map(x->x*x).forEach(y->System.out.println(y));
  • reduce: The reduce method is used to reduce the elements of a stream to a single value.
    The reduce method takes a BinaryOperator as a parameter.
List number = Arrays.asList(2,3,4,5);
int even = number.stream().filter(x->x%2==0).reduce(0,(ans,i)-> ans+i);
int initialValue = 5;
		
int reducedNumber = Stream.of(1, 2, 3).reduce(initialValue, (num, index)-> {
			System.out.println("num: "+num+", index: "+index);
			return index;
		});
		
System.out.println("reducedNumber: "+reducedNumber);
num: 3, index: 1
num: 1, index: 2
num: 2, index: 3
reducedNumber: 3

 

String listToString = Stream.of("Folau", "Kinga", "Laulau").collect(Collectors.joining(", ", "(", ")"));

System.out.println("listToString: " + listToString);
listToString: (Folau, Kinga, Laulau)

 

 

 

 

 

 

 

 

 

Examples

foreach – terminal operation

Performs an action for each element of this stream.

List<String> words = Arrays.asList("I", "can", "program", "in", "Java");
words.stream().forEach(System.out::println);

allMatch() – terminal operation

Returns whether all elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then true is returned and the predicate is not evaluated.

// Creating a list of Integers 
List<Integer> list = Arrays.asList(3, 4, 6, 12, 20); 
      
// Check if all elements of stream 
// are divisible by 3 or not using  
// Stream allMatch(Predicate predicate) 
boolean answer = list.stream().allMatch(n-> n % 3 ==0); 

map() – intermediate operation

Returns a stream consisting of the results of applying the given function to the elements of this stream.

// Creating a list of Integers 
List<Integer> list = Arrays.asList(3, 6, 9, 12, 15); 
  
// Using Stream map(Function mapper) and 
// displaying the corresponding new stream 
list.stream().map(number -> number * 3).forEach(System.out::println)

collect() – terminal operation

Stream.collect() method used to receive elements from a stream and store them in a collection.

users.add(new User("John", "john@gmail.com",100000));
users.add(new User("Peter", "peter@gmail.com", 50000));
 
// find employees whose salaries are above 100000
List<User> filteredList = users.stream().filter(user->user.getSalary() > 100000).collect(Collectors.toList());

// Collections to Map
Map<Integer, String> result1 = users.stream().collect(
                Collectors.toMap(User::getId, User::getName));
Map<Integer, String> result2 = users.stream().collect(
                Collectors.toMap(u -> u.getId(), u -> u.getName()));
Map result3 = list.stream().collect(
                        Collectors.toMap(
                                u -> u.getName(), u -> u.getId(), 
                                (oldValue, newValue) -> oldValue,  
                                LinkedHashMap::new
                        ));
int sum = Stream.of(1, 2, 3).collect(Collectors.summingInt(Integer::intValue));

System.out.println("sum: " + sum);
sum: 6

 

long count() – terminal operation

Returns the count of elements in this stream.

long count = Arrays.asList("I","can","program","in","java").stream().count();
System.out.printf("There are %d elements in the stream %n", count);

iterate() – intermediate operation

Create values on demand

//Stream.iterate(initial value, next value)
Stream.iterate(0, n -> n + 1)
                .limit(10)
                .forEach(x -> System.out.println(x));

int numOfIterations = 5;
		int initialValue = 3;

		Stream.iterate(initialValue, n -> {
			return n * 2;
		}).limit(numOfIterations).forEach(x -> System.out.println(x));

Stream Creation

Stream.of("Folau", "Kinga", "Laulau").forEach((name) -> {
			System.out.println("name1: " + name);
		});
		Stream.builder().add("Folau").add("Kinga").add("Laulau").build().forEach((name) -> {
			System.out.println("name2: " + name);
		});

findFirst()

findFirst() returns the first element in the stream.

Stream.of("Folau", "Kinga", "Laulau").filter(name -> name.contains("lau")).forEach((name) -> {
	System.out.println("name: " + name);
});
//name: Folau
//name: Laulau

parallel – run executions in multiple threads.

int numOfIterations = 20;
int initialValue = 3;
		
int sum = Stream.iterate(initialValue, n -> {
			return n * 2;
		}).limit(numOfIterations).parallel().collect(Collectors.summingInt(Integer::intValue));

System.out.println("sum: " + sum);

 

Resources

 

August 5, 2019

Interface default methods and static methods

 

Why the default method?

When you implement an interface, that interface may change. It may add more methods by which you will be forced to implement. Default methods solved this issue in where you don’t need to implement default methods. There are just there for you to use. There is no need for you to modify anything in your class.

The most typical use of default methods in interfaces is to incrementally provide additional functionality to a given type without breaking down the implementing classes. In addition, they can be used to provide additional functionality around an existing abstract method:

 

public interface Screen {

	public boolean turnOn();
	public boolean turnOff();
	
	// default method
    default void defaultMethod() {
       System.out.println("Doing Screen default things...");
    }
}

 

public class DefaultStaticMethodDemo implements Screen{

	public static void main(String[] args) {
		DefaultStaticMethodDemo demo = new DefaultStaticMethodDemo();
		
		demo.defaultMethod();
	}

	@Override
	public boolean turnOn() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean turnOff() {
		// TODO Auto-generated method stub
		return false;
	}

}

 

Implementing multiple interfaces with the same default method names.

public interface Vehicle {
	// default method
    default void defaultMethod() {
       System.out.println("Doing Vehicle default things...");
    }
}

It won’t compile. You have to provide your own implementation.

public class DefaultStaticMethodDemo implements Screen, Vehicle{

	public static void main(String[] args) {
		DefaultStaticMethodDemo demo = new DefaultStaticMethodDemo();
		
		demo.defaultMethod();
	}

	@Override
	public boolean turnOn() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean turnOff() {
		// TODO Auto-generated method stub
		return false;
	}

}

 

public class DefaultStaticMethodDemo implements Screen, Vehicle{

	public static void main(String[] args) {
		DefaultStaticMethodDemo demo = new DefaultStaticMethodDemo();
		
		demo.defaultMethod();
	}

	@Override
	public boolean turnOn() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean turnOff() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void defaultMethod() {
		// TODO Auto-generated method stub
		Vehicle.super.defaultMethod();
	}

}

Having static methods in your interface helps group all related methods into one place. You have all your interface functions as well as utility functions.

public interface Vehicle {
	
	public boolean turnOn();
	// default method
    default void defaultMethod() {
       System.out.println("Doing Vehicle default things...");
    }
    
    
    static double getApproximateDistance(double milePerGallon, double gallons) {
        return milePerGallon*gallons;
    }
}

 

 

 

August 5, 2019