It would be performance wise to generate a HashMap associating the string elements with the corresponding indices (i.e. Map<String,Integer>), instead of relaying on List.indexOf() method which performs iteration under the hood. And then define a Comparator based on this Map.
In order to place the elements that not are not present in the listB at the end of the sorted list, we can make use of the method Map.getOrDefault() providing the size of the map as a default value.
List<String> listA = new ArrayList<>(List.of("us","au","in","gb"));
List<String> listB = new ArrayList<>(List.of("au","us","in"));
Map<String, Integer> order = IntStream.range(0, listB.size())
.boxed()
.collect(Collectors.toMap(
listB::get,
Function.identity()
));
Comparator<String> comparator = Comparator.comparingInt(s -> order.getOrDefault(s, order.size()));
listA.sort(comparator);
System.out.println(listA);
Output:
[au, us, in, gb]
In case in you want to preserve the initial order of the elements in listA that are not present in the listB (i.e. group them at the very end of the list according to their initial ordering), you can generate an additional Map. This time based on the listA, which associate each element like gb with a unique Value greater or equal to the size of listB:
List<String> listA = new ArrayList<>(List.of("fr","us","nl","au","in","gb"));
List<String> listB = new ArrayList<>(List.of("au","us","in"));
Map<String, Integer> order = IntStream.range(0, listB.size())
.boxed()
.collect(Collectors.toMap(
listB::get,
Function.identity()
));
Map<String, Integer> resolver = IntStream.range(0, listA.size())
.filter(i -> !order.containsKey(listA.get(i))) // to reduce the size of the Map
.boxed()
.collect(Collectors.toMap(
listA::get,
i -> i + order.size()
));
Comparator<String> comparator = Comparator.comparingInt(
s -> order.getOrDefault(s, resolver.get(s))
);
listA.sort(comparator);
Output:
[au, us, in, fr, nl, gb] // the order of "fr","nl","gb" is preserved