Sorting two lists - java

I am working in Java. I have two lists, let's call them A and B, which I want to sort.
A is an Integer list, so I have no problem to do that. I simply use Collections.sort() to obtain a sorted list of integers.
The problem comes with the list B. I want to make the same changes done before in A.. B is a list of objects, but there's no way to associate the changes in B with changes in A. I mean, there's no condition to create a comparator method.
Little example:
I have:
A -> {5,1,3,6,4}
B -> {a,b,c,d,e}
I want to sort A and apply the same changes to B to obtain:
A -> {1,3,4,5,6}
B -> {b,c,e,a,d}
Is there any way to do that using built-in Java functions? I prefer to avoid writing a sorting algorithm myself because of efficiency.
Thank you!

A TreeMap will always iterate over the keys in the right order, so you can do it like this:
Map<Integer, String> map = new TreeMap<Integer, String>();
map.put(5, "a");
map.put(1, "b");
map.put(3, "c");
map.put(6, "d");
map.put(4, "e");
System.out.println(map.keySet());
System.out.println(map.values());
However if you really want to start and end with the same pair of List instances, I think you'd have to do something convoluted like this:
List<Integer> numbers = new ArrayList<Integer>(Arrays.asList(5, 1, 3, 6, 4));
List<String> letters = new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e"));
Map<Integer, String> map = new HashMap<Integer, String>();
for (int i = 0, n = numbers.size(); i < n; i++) {
map.put(numbers.get(i), letters.get(i));
}
Collections.sort(numbers);
letters.clear();
for (int number : numbers) {
letters.add(map.get(number));
}
System.out.println(numbers);
System.out.println(letters);

I would start by creating a POJO to store A and B,
static class ABPojo implements Comparable<ABPojo> {
public ABPojo(int a, String b) {
this.a = a;
this.b = b;
}
private int a;
private String b;
public int getA() {
return a;
}
public String getB() {
return b;
}
public int compareTo(ABPojo o) {
if (o instanceof ABPojo) {
ABPojo that = (ABPojo) o;
return Integer.valueOf(a).compareTo(that.getA());
}
return 1;
}
}
Then you can loop over the collection of ABPojo(s) after sorting to build your output with something like
public static void main(String[] args) {
List<ABPojo> al = new ArrayList<ABPojo>();
al.add(new ABPojo(5, "a"));
al.add(new ABPojo(1, "b"));
al.add(new ABPojo(3, "c"));
al.add(new ABPojo(6, "d"));
al.add(new ABPojo(4, "e"));
Collections.sort(al);
StringBuilder a = new StringBuilder();
StringBuilder b = new StringBuilder();
for (ABPojo pojo : al) {
if (a.length() > 0) {
a.append(",");
} else {
a.append("{");
}
if (b.length() > 0) {
b.append(",");
} else {
b.append("{");
}
a.append(pojo.getA());
b.append(pojo.getB());
}
a.append("}");
b.append("}");
System.out.println("A -> " + a.toString());
System.out.println("B -> " + b.toString());
}
Output is the requested
A -> {1,3,4,5,6}
B -> {b,c,e,a,d}

Create a map with your elements in A as key and the elements in B as value resp.
Then Collections.Sort() will automatically sort the A elements and its corresponding B elements.

Start with here:
How to find the permutation of a sort in Java
Then manually apply the permutation.
But it sounds like a design problem; consider one list of classes that implement Comparator<T> where the comparison function is just on the numbers.

I don't think there is a Java function that will do this.
However, you could use a map structure instead of a list stucture where the key to the data is your int, and the data is the unsortable list.

Related

Get Max Value from List in Java

I have a list like below
List{
{Type=A, Amt=30000},{Type=A, Amt=50000},{Type=B, Amt=40000},{Type=B,Amt=60000},{Type=C,spRqstAmt=50000},{Type=C,Amt=10000}
}
I need to print the max amount for each Type.
Output:
Type: A,
Amt: 50000
Type: B,
Amt: 60000
Type: C,
Amt: 50000
How to achieve this?
Check the below code . This should work for you . There could be other approaches also , this is using HashMap.
Data Class
class Data{
String type;
int amt;
Data(String s , int a){
this.type = s;
this.amt = a;
}
}
Implementation Class:
Data data1 = new Data("A",30000);
Data data2 = new Data("A",50000);
Data data3 = new Data("B",40000);
Data data4 = new Data("B",60000);
Data data5 = new Data("C",50000);
Data data6 = new Data("C",10000);
ArrayList<Data> al = new ArrayList<>();
al.add(data1);
al.add(data2);
al.add(data3);
al.add(data4);
al.add(data5);
al.add(data6);
HashMap<String,Integer> hm = new HashMap<>();
for (int i = 0; i < al.size(); i++) {
if(hm.containsKey(al.get(i).type)) {
int tempAmt = hm.get(al.get(i).type);
if(al.get(i).amt > tempAmt) {
hm.put(al.get(i).type, al.get(i).amt);
}
}else {
hm.put(al.get(i).type,al.get(i).amt);
}
}
System.out.println(hm);
Assuming a class:
class DataClass {
public String type;
public Integer amt;
}
You can:
HashMap<String, Integer> values = new HashMap<>();
for (DataClass data : listDatas) {
if (values.contains(data.type)) {
int amt = data.get(type);
if (data.amt > amt) {
values.put(data.type, data.amt);
}
} else {
values.put(data.type, data.amt);
}
}
Then you can print the results.
Similar to the answer of Tomáš Záluský, using the stream API but this way you retain the Element objects, if you need them later:
import static java.util.Comparator.comparingInt;
import static java.util.function.BinaryOperator.maxBy;
import static java.util.stream.Collectors.toMap;
Map<String, Element> groupByMax = list.stream()
.collect(toMap(Element::getType, e -> e, maxBy(comparingInt(Element::getAmt))));
Collection<Element> elementsWithMaxAmts = groupByMax.values();
There's no real need to use streams in this case. Give the following list of maps:
List<Map<String, String>> list =
List.of(Map.of("Type", "A", "Amt", "30000"),
Map.of("Type", "A", "Amt", "50000"),
Map.of("Type", "B", "Amt", "40000"),
Map.of("Type", "B", "Amt", "60000"),
Map.of("Type", "C", "Amt", "50000"),
Map.of("Type", "C", "Amt", "10000"));
You can use the merge method of map to combine the results.
Map<String, String> results = new HashMap<>();
for (Map<String, String> m : list) {
results.merge(m.get("Type"), m.get("Amt"),
(a, b) -> Integer.valueOf(a)
.compareTo(Integer.valueOf(b)) == 1 ? a :
b);
}
results.entrySet().forEach(System.out::println);
Prints the following with the type showing the maximum amount.
A=50000
B=60000
C=50000
Assuming your list contains objects of type Element, aggregate them using toMap collector into map where type is a key and value conflicts are resolved in favour of greatest value:
Map<String,Integer> m = list.stream().collect(Collectors.toMap(Element::getType, Element::getAmt, Math::max))
(code from top of my head, I didn't tested it)

Sort your list treemap by another list

Well, I saw other ways that involve list of arrays. Although nothing similar to this.
List<String> orderList = new ArrayList<>();
orderList.add("ARMOR");
orderList.add("ADIDAS");
orderList.add("NIKE");
I have my List<TreeMap<Brand,String>> brands returning this list.
[{NIKE=Shoes},{ADIDAS=Clothing},{ARMOR=Backpacks},{NIKE=Shorts}]
I want to sort this by the orderList provided to get the following:
[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]
But I don't know how to.
Perhaps there are other ways to modify this as a list and reorder it, I am not sure.
You can convert the List<String> orderList to HashMap<String, Integer> indexMap:
["ARMOR", "ADIDAS", "NIKE"] -> {"ARMOR": 0, "ADIDAS": 1, "NIKE": 2}
Map<String, Integer> indexMap = IntStream.range(0, orderList.size()) // [0, 1, 2]
.boxed() // use int, not Integer
.collect(Collectors.toMap(i -> orderList.get(i), i -> i)); // add index to each element
Then, given TreeMap<String, String> brands you can do:
List<Map.Entry<String, String>> result = brands.entrySet()
.stream()
.filter(entry -> indexMap.contains(entry.getKey()) // Remove elements not in indexMap... and therefore also not in orderList
.sorted((a, b) -> Integer.compare(indexMap.get(a.getKey()), indexMap(b.getKey()))) // Sort all present brands to match the order from orderList
.collect(Collectors.toList());
You can use a customized Comparator directly with Collections.sort() method from the java.util.Collections API. Here is a copy-paste working example:
public class SortByList {
enum Brand {
ADIDAS, ARMOR, NIKE
}
static class BrandMap extends TreeMap<Brand, String> {
public BrandMap(Brand brand, String type) {
put(brand, type);
}
}
public static void main(String[] args) {
List<String> orderList = new ArrayList<>();
orderList.add("ARMOR");
orderList.add("ADIDAS");
orderList.add("NIKE");
List<TreeMap<Brand, String>> brands = new ArrayList<>();
brands.add(new BrandMap(Brand.NIKE, "Shoes"));
brands.add(new BrandMap(Brand.ADIDAS, "Clothing"));
brands.add(new BrandMap(Brand.ARMOR, "Backpacks"));
brands.add(new BrandMap(Brand.NIKE, "Shorts"));
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
int index1 = orderList.indexOf(key1.name());
int index2 = orderList.indexOf(key2.name());
return Integer.compare(index1, index2);
});
//[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]
System.out.println(brands);
}
}
Just a note: I've created BrandMap just to easily generate the test data and populate them to the list. You can leave it and just take the Comparator implementation written in lambda expression. It should work.
Another update: If you care about performance and your orderList is likely to get bigger in time, using orderList.indexOf() inside the sort function won't do good. Because it makes a sequential search which costs O(N) for each sorting comparison:
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
// extra iteration costs extra O(N)
int index1 = orderList.indexOf(key1.name());
// extra iteration costs extra O(N)
int index2 = orderList.indexOf(key2.name());
return Integer.compare(index1, index2);
});
If this is the case, I suggest to store your orderList in a HashMap instead of a List or array if you have the chance. Alternatively, you can convert it in a one time operation and use your map before your sorting operation:
// this will cost O(N) only for once
Map<String, Integer> orderMap = new HashMap<>();
for (int i = 0; i < orderList.size(); i++) {
orderMap.put(orderList.get(i), i);
}
// then sort
brands.sort((o1, o2) -> {
Brand key1 = o1.keySet().iterator().next();
Brand key2 = o2.keySet().iterator().next();
// now use the map for getting indices in constant O(1) time
int index1 = orderMap.get(key1.name());
int index2 = orderMap.get(key2.name());
return Integer.compare(index1, index2);
});
A little extra: Surely, storing in a HashMap costs an extra O(N) space but I assume memory consumption is tolerable in your case.
You're welcome. Cheers!

Sorting sets alphabetically, letters in sets separated by commas

public static void main(String[] args) throws IOException
{
HashSet set = new HashSet<String>();
set.add("{}");
set.add("{a}");
set.add("{b}");
set.add("{a, b}");
set.add("{a, c}");
sortedSet(set);
}
public static void sortedSet(HashSet set)
{
List<String> setList = new ArrayList<String>(set);
List<String> orderedByAlpha = new ArrayList<String>(set);
//sort by alphabetical order
orderedByAlpha = (List<String>) setList.stream()
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
.collect(Collectors.toList());
System.out.println(orderedByAlpha);
}
I am trying to sort alphabetically but the output I get is this :
[{a, b}, {a, c}, {a}, {b}, {}]
but it should be:
[{a}, {a, b}, {a, c}, {b}, {}]
You're output doesn't match your code. You are showing 2D array lists, but your converting to a 1D arraylist, doesn't make sense.
public static void main(String[] args)
{
test(Arrays.asList("a", "d", "f", "a", "b"));
}
static void test(List<String> setList)
{
List<String> out = setList.stream().sorted((a, b) -> a.compareToIgnoreCase(b)).collect(Collectors.toList());
System.out.println(out);
}
This is properly sorting 1D arrays, so you're correct there.
You'll probably need to implement your own comparator to compare the 2D array lists to sort them.
instead of having the source as a List<String> I'd recommend you have it as a List<Set<String>> e.g.
List<Set<String>> setList = new ArrayList<>();
setList.add(new HashSet<>(Arrays.asList("a","b")));
setList.add(new HashSet<>(Arrays.asList("a","c")));
setList.add(new HashSet<>(Collections.singletonList("a")));
setList.add(new HashSet<>(Collections.singletonList("b")));
setList.add(new HashSet<>());
Then apply the following comparator along with the mapping operation to yield the expected result:
List<String> result =
setList.stream()
.sorted(Comparator.comparing((Function<Set<String>, Boolean>) Set::isEmpty)
.thenComparing(s -> String.join("", s),
String.CASE_INSENSITIVE_ORDER))
.map(Object::toString)
.collect(Collectors.toList());
and this prints:
[[a], [a, b], [a, c], [b], []]
note that, currently the result is a list of strings where each string is the string representation of a given set. if however, you want the result to be a List<Set<String>> then simply remove the map operation above.
Edit:
Managed to get a solution working based on your initial idea....
So, first, you need a completely new comparator instead of just (s1, s2) -> s1.compareToIgnoreCase(s2) as it will not suffice.
Given the input:
Set<String> set = new HashSet<>();
set.add("{}");
set.add("{a}");
set.add("{b}");
set.add("{a, b}");
set.add("{a, c}");
and the following stream pipeline:
List<String> result = set.stream()
.map(s -> s.replaceAll("[^A-Za-z]+", ""))
.sorted(Comparator.comparing(String::isEmpty)
.thenComparing(String.CASE_INSENSITIVE_ORDER))
.map(s -> Arrays.stream(s.split(""))
.collect(Collectors.joining(", ", "{", "}")))
.collect(Collectors.toList());
Then we would have a result of:
[{a}, {a, b}, {a, c}, {b}, {}]
Well, as #Aomine and #Holger noted already, you need a custom comparator.
But IMHO their solutions look over-engineered. You don't need any of costly operations like split and substring:
String.substring creates a new String object and calls System.arraycopy() under the hood
String.split is even more costly. It iterates over your string and calls String.substring multiple times. Moreover it creates an ArrayList to store all the substrings. If the number of substrings is big enough then your ArrayList will need to expand its capacity (perhaps not only once) causing another call of System.arraycopy().
For your simple case I would slightly modify the code of built-in String.compareTo method:
Comparator<String> customComparator =
(s1, s2) -> {
int len1 = s1.length();
int len2 = s2.length();
if (len1 == 2) return 1;
if (len2 == 2) return -1;
int lim = Math.min(len1, len2) - 1;
for (int k = 1; k < lim; k++) {
char c1 = s1.charAt(k);
char c2 = s2.charAt(k);
if (c1 != c2) {
return c1 - c2;
}
}
return len1 - len2;
};
It will compare the strings with complexity O(n), where n is the length of shorter string. At the same time it will neither create any new objects nor perform any array replication.
The same comparator can be implemented using Stream API:
Comparator<String> customComparatorUsingStreams =
(s1, s2) -> {
if (s1.length() == 2) return 1;
if (s2.length() == 2) return -1;
return IntStream.range(1, Math.min(s1.length(), s2.length()) - 1)
.map(i -> s1.charAt(i) - s2.charAt(i))
.filter(i -> i != 0)
.findFirst()
.orElse(0);
};
You can use your custom comparator like this:
List<String> orderedByAlpha = setList.stream()
.sorted(customComparatorUsingStreams)
.collect(Collectors.toList());
System.out.println(orderedByAlpha);
A take on it (slightly similar to the answer by Aomine) would be to strip the strings of the characters that makes String#compareTo() fail, in this case ('{' and '}'). Also, the special case that an empty string ("{}") is to be sorted after the rest needs to be taken care of.
The following code implements such a comparator:
static final Comparator<String> COMPARE_IGNORING_CURLY_BRACES_WITH_EMPTY_LAST = (s1, s2) -> {
Function<String, String> strip = string -> string.replaceAll("[{}]", "");
String strippedS1 = strip.apply(s1);
String strippedS2 = strip.apply(s2);
return strippedS1.isEmpty() || strippedS2.isEmpty() ?
strippedS2.length() - strippedS1.length() :
strippedS1.compareTo(strippedS2);
};
Of course, this is not the most efficient solution. If efficiency is truly important here, I would loop through the characters, like String#compareTo() does, as suggested by ETO.

Intersection and union of ArrayLists in Java

Are there any methods to do so? I was looking but couldn't find any.
Another question: I need these methods so I can filter files.
Some are AND filters and some are OR filters (like in set theory), so I need to filter according to all files and the unite/intersects ArrayLists that holds those files.
Should I use a different data structure to hold the files? Is there anything else that would offer a better runtime?
Here's a plain implementation without using any third-party library. Main advantage over retainAll, removeAll and addAll is that these methods don't modify the original lists input to the methods.
public class Test {
public static void main(String... args) throws Exception {
List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));
System.out.println(new Test().intersection(list1, list2));
System.out.println(new Test().union(list1, list2));
}
public <T> List<T> union(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();
set.addAll(list1);
set.addAll(list2);
return new ArrayList<T>(set);
}
public <T> List<T> intersection(List<T> list1, List<T> list2) {
List<T> list = new ArrayList<T>();
for (T t : list1) {
if(list2.contains(t)) {
list.add(t);
}
}
return list;
}
}
Collection (so ArrayList also) have:
col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union
Use a List implementation if you accept repetitions, a Set implementation if you don't:
Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");
Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");
col1.addAll(col2);
System.out.println(col1);
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]
This post is fairly old, but nevertheless it was the first one popping up on google when looking for that topic.
I want to give an update using Java 8 streams doing (basically) the same thing in a single line:
List<T> intersect = list1.stream()
.filter(list2::contains)
.collect(Collectors.toList());
List<T> union = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
If anyone has a better/faster solution let me know, but this solution is a nice one liner that can be easily included in a method without adding a unnecessary helper class/method and still keep the readability.
list1.retainAll(list2) - is intersection
union will be removeAll and then addAll.
Find more in the documentation of collection(ArrayList is a collection)
http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html
Unions and intersections defined only for sets, not lists. As you mentioned.
Check guava library for filters. Also guava provides real intersections and unions
static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)
You can use CollectionUtils from apache commons.
The solution marked is not efficient. It has a O(n^2) time complexity. What we can do is to sort both lists, and the execute an intersection algorithm as the one below.
private static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) {
ArrayList<Integer> res = new ArrayList<Integer>();
int i = 0, j = 0;
while (i != f.size() && j != s.size()) {
if (f.get(i) < s.get(j)) {
i ++;
} else if (f.get(i) > s.get(j)) {
j ++;
} else {
res.add(f.get(i));
i ++; j ++;
}
}
return res;
}
This one has a complexity of O(n log n + n) which is in O(n log n).
The union is done in a similar manner. Just make sure you make the suitable modifications on the if-elseif-else statements.
You can also use iterators if you want (I know they are more efficient in C++, I dont know if this is true in Java as well).
One-liners since JAVA 8
Union
if there are no duplicates:
return concat(a.stream(), b.stream()).collect(toList());
union and distinct:
return concat(a.stream(), b.stream()).distinct().collect(toList());
union and distinct if Collection/Set return type:
return concat(a.stream(), b.stream()).collect(toSet());
Intersect
if no duplicates:
return a.stream().filter(b::contains).collect(toList());
PERFORMANCE: If collection b is huge and not O(1), then pre-optimize the filter performance by adding 1 line before return: Copy to HasSet (import java.util.Set;):
... b = Set.copyOf(b);
intersect and distinct:
return a.stream().distinct().filter(b::contains).collect(toList());
- imports
import static java.util.stream.Stream.concat;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
I think you should use a Set to hold the files if you want to do intersection and union on them. Then you can use Guava's Sets class to do union, intersection and filtering by a Predicate as well. The difference between these methods and the other suggestions is that all of these methods create lazy views of the union, intersection, etc. of the two sets. Apache Commons creates a new collection and copies data to it. retainAll changes one of your collections by removing elements from it.
Here is a way how you can do an intersection with streams (remember that you have to use java 8 for streams):
List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());
An example for lists with different types. If you have a realtion between foo and bar and you can get a bar-object from foo than you can modify your stream:
List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));
fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());
You can use commons-collections4 CollectionUtils
Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);
Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]
Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]
Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]
retainAll will modify your list
Guava doesn't have APIs for List (only for set)
I found ListUtils very useful for this use case.
Use ListUtils from org.apache.commons.collections if you do not want to modify existing list.
ListUtils.intersection(list1, list2)
In Java 8, I use simple helper methods like this:
public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
return Stream.concat(coll1.stream(), coll2.stream())
.filter(coll1::contains)
.filter(coll2::contains)
.collect(Collectors.toSet());
}
public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
If the objects in the list are hashable (i.e. have a decent hashCode and equals function), the fastest approach between tables approx. size > 20 is to construct a HashSet for the larger of the two lists.
public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
if (b.size() > a.size()) {
return intersection(b, a);
} else {
if (b.size() > 20 && !(a instanceof HashSet)) {
a = new HashSet(a);
}
ArrayList<T> result = new ArrayList();
for (T objb : b) {
if (a.contains(objb)) {
result.add(objb);
}
}
return result;
}
}
I was also working on the similar situation and reached here searching for help. Ended up finding my own solution for Arrays.
ArrayList AbsentDates = new ArrayList(); // Will Store Array1-Array2
Note : Posting this if it can help someone reaching this page for help.
ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
public void AbsentDays() {
findDates("April", "2017");//Array one with dates in Month April 2017
findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017
for (int i = 0; i < Dates.size(); i++) {
for (int j = 0; j < PresentDates.size(); j++) {
if (Dates.get(i).equals(PresentDates.get(j))) {
Dates.remove(i);
}
}
AbsentDates = Dates;
}
System.out.println(AbsentDates );
}
Intersection of two list of different object based on common key - Java 8
private List<User> intersection(List<User> users, List<OtherUser> list) {
return list.stream()
.flatMap(OtherUser -> users.stream()
.filter(user -> user.getId()
.equalsIgnoreCase(OtherUser.getId())))
.collect(Collectors.toList());
}
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
Set<T> set1, set2;
if (col1 instanceof Set) {
set1 = (Set) col1;
} else {
set1 = new HashSet<>(col1);
}
if (col2 instanceof Set) {
set2 = (Set) col2;
} else {
set2 = new HashSet<>(col2);
}
Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));
for (T t : set1) {
if (set2.contains(t)) {
intersection.add(t);
}
}
return intersection;
}
JDK8+ (Probably Best Performance)
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
boolean isCol1Larger = col1.size() > col2.size();
Set<T> largerSet;
Collection<T> smallerCol;
if (isCol1Larger) {
if (col1 instanceof Set) {
largerSet = (Set<T>) col1;
} else {
largerSet = new HashSet<>(col1);
}
smallerCol = col2;
} else {
if (col2 instanceof Set) {
largerSet = (Set<T>) col2;
} else {
largerSet = new HashSet<>(col2);
}
smallerCol = col1;
}
return smallerCol.stream()
.filter(largerSet::contains)
.collect(Collectors.toSet());
}
If you don't care about performance and prefer smaller code just use:
col1.stream().filter(col2::contains).collect(Collectors.toList());
First, I am copying all values of arrays into a single array then I am removing duplicates values into the array. Line 12, explaining if same number occur more than time then put some extra garbage value into "j" position. At the end, traverse from start-end and check if same garbage value occur then discard.
public class Union {
public static void main(String[] args){
int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
int arr2[]={1,3,2,1,3,2,4,6,3,4};
int arr3[]=new int[arr1.length+arr2.length];
for(int i=0;i<arr1.length;i++)
arr3[i]=arr1[i];
for(int i=0;i<arr2.length;i++)
arr3[arr1.length+i]=arr2[i];
System.out.println(Arrays.toString(arr3));
for(int i=0;i<arr3.length;i++)
{
for(int j=i+1;j<arr3.length;j++)
{
if(arr3[i]==arr3[j])
arr3[j]=99999999; //line 12
}
}
for(int i=0;i<arr3.length;i++)
{
if(arr3[i]!=99999999)
System.out.print(arr3[i]+" ");
}
}
}
After testing, here is my best intersection approach.
Faster speed compared to pure HashSet Approach. HashSet and HashMap below has similar performance for arrays with more than 1 million records.
As for Java 8 Stream approach, speed is quite slow for array size larger then 10k.
Hope this can help.
public static List<String> hashMapIntersection(List<String> target, List<String> support) {
List<String> r = new ArrayList<String>();
Map<String, Integer> map = new HashMap<String, Integer>();
for (String s : support) {
map.put(s, 0);
}
for (String s : target) {
if (map.containsKey(s)) {
r.add(s);
}
}
return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();
List<String> r = new ArrayList<String>();
Set<String> set = new HashSet<String>(b);
for (String s : a) {
if (set.contains(s)) {
r.add(s);
}
}
print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
return r;
}
public static void union(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();
Set<String> r= new HashSet<String>(a);
r.addAll(b);
print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}
retainAll() method use for finding common element..i.e;intersection
list1.retainAll(list2)
You can use the methods:
CollectionUtils.containsAny and CollectionUtils.containsAll
from Apache Commons.
Final solution:
//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();
set.addAll(list1);
set.addAll(list2);
return new ArrayList<T>(set);
}
//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
list1.retainAll(list2);
return list1;
}
//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
list1.removeAll(list2);
return list1;
}
If you had your data in Sets you could use Guava's Sets class.
If the number matches than I am checking it's occur first time or not with help of "indexOf()" if the number matches first time then print and save into in a string so, that when the next time same number matches then it's won't print because due to "indexOf()" condition will be false.
class Intersection
{
public static void main(String[] args)
{
String s="";
int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};
for (int i = 0; i < array1.length; i++)
{
for (int j = 0; j < array2.length; j++)
{
char c=(char)(array1[i]);
if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
{
System.out.println("Common element is : "+(array1[i]));
s+=c;
}
}
}
}
}

How to count the number of occurrences of an element in a List

I have an ArrayList, a Collection class of Java, as follows:
ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
As you can see, the animals ArrayList consists of 3 bat elements and one owl element. I was wondering if there is any API in the Collection framework that returns the number of bat occurrences or if there is another way to determine the number of occurrences.
I found that Google's Collection Multiset does have an API that returns the total number of occurrences of an element. But that is compatible only with JDK 1.5. Our product is currently in JDK 1.6, so I cannot use it.
I'm pretty sure the static frequency-method in Collections would come in handy here:
int occurrences = Collections.frequency(animals, "bat");
That's how I'd do it anyway. I'm pretty sure this is jdk 1.6 straight up.
In Java 8:
Map<String, Long> counts =
list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
Alternative Java 8 solution using Streams:
long count = animals.stream().filter(animal -> "bat".equals(animal)).count();
This shows, why it is important to "Refer to objects by their interfaces" as described in Effective Java book.
If you code to the implementation and use ArrayList in let's say, 50 places in your code, when you find a good "List" implementation that count the items, you will have to change all those 50 places, and probably you'll have to break your code ( if it is only used by you there is not a big deal, but if it is used by someone else uses, you'll break their code too)
By programming to the interface you can let those 50 places unchanged and replace the implementation from ArrayList to "CountItemsList" (for instance ) or some other class.
Below is a very basic sample on how this could be written. This is only a sample, a production ready List would be much more complicated.
import java.util.*;
public class CountItemsList<E> extends ArrayList<E> {
// This is private. It is not visible from outside.
private Map<E,Integer> count = new HashMap<E,Integer>();
// There are several entry points to this class
// this is just to show one of them.
public boolean add( E element ) {
if( !count.containsKey( element ) ){
count.put( element, 1 );
} else {
count.put( element, count.get( element ) + 1 );
}
return super.add( element );
}
// This method belongs to CountItemList interface ( or class )
// to used you have to cast.
public int getCount( E element ) {
if( ! count.containsKey( element ) ) {
return 0;
}
return count.get( element );
}
public static void main( String [] args ) {
List<String> animals = new CountItemsList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println( (( CountItemsList<String> )animals).getCount( "bat" ));
}
}
OO principles applied here: inheritance, polymorphism, abstraction, encapsulation.
Sorry there's no simple method call that can do it. All you'd need to do though is create a map and count frequency with it.
HashMap<String,int> frequencymap = new HashMap<String,int>();
foreach(String a in animals) {
if(frequencymap.containsKey(a)) {
frequencymap.put(a, frequencymap.get(a)+1);
}
else{ frequencymap.put(a, 1); }
}
There is no native method in Java to do that for you. However, you can use IterableUtils#countMatches() from Apache Commons-Collections to do it for you.
Simple Way to find the occurrence of string value in an array using Java 8 features.
public void checkDuplicateOccurance() {
List<String> duplicateList = new ArrayList<String>();
duplicateList.add("Cat");
duplicateList.add("Dog");
duplicateList.add("Cat");
duplicateList.add("cow");
duplicateList.add("Cow");
duplicateList.add("Goat");
Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString(),Collectors.counting()));
System.out.println(couterMap);
}
Output : {Cat=2, Goat=1, Cow=1, cow=1, Dog=1}
You can notice "Cow" and cow are not considered as same string, in case you required it under same count, use .toLowerCase(). Please find the snippet below for the same.
Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString().toLowerCase(),Collectors.counting()));
Output : {cat=2, cow=2, goat=1, dog=1}
To achieve that one can do it in several ways, namely:
Methods that return the number of occurrence of a single element:
Collection Frequency
Collections.frequency(animals, "bat");
Java Stream:
Filter
animals.stream().filter("bat"::equals).count();
Just iteration thought the list
public static long manually(Collection<?> c, Object o){
int count = 0;
for(Object e : c)
if(e.equals(o))
count++;
return count;
}
Methods that create a map of frequencies:
Collectors.groupingBy
Map<String, Long> counts =
animals.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
merge
Map<String, Long> map = new HashMap<>();
c.forEach(e -> map.merge(e, 1L, Long::sum));
Manually
Map<String, Integer> mp = new HashMap<>();
animals.forEach(animal -> mp.compute(animal, (k, v) -> (v == null) ? 1 : v + 1));
A running example with all the methods:
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Frequency {
public static int frequency(Collection<?> c, Object o){
return Collections.frequency(c, o);
}
public static long filter(Collection<?> c, Object o){
return c.stream().filter(o::equals).count();
}
public static long manually(Collection<?> c, Object o){
int count = 0;
for(Object e : c)
if(e.equals(o))
count++;
return count;
}
public static Map<?, Long> mapGroupBy(Collection<?> c){
return c.stream()
.collect(Collectors.groupingBy(Function.identity() , Collectors.counting()));
}
public static Map<Object, Long> mapMerge(Collection<?> c){
Map<Object, Long> map = new HashMap<>();
c.forEach(e -> map.merge(e, 1L, Long::sum));
return map;
}
public static Map<Object, Long> manualMap(Collection<?> c){
Map<Object, Long> map = new HashMap<>();
c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
return map;
}
public static void main(String[] args){
List<String> animals = new ArrayList<>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println(frequency(animals, "bat"));
System.out.println(filter(animals,"bat"));
System.out.println(manually(animals,"bat"));
mapGroupBy(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
mapMerge(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
manualMap(animals).forEach((k, v) -> System.out.println(k + " -> "+v));
}
}
The methods name should have reflected what those methods are doing, however, I used the name to reflect the approach being used instead (given that in the current context it is okey).
I wonder, why you can't use that Google's Collection API with JDK 1.6. Does it say so? I think you can, there should not be any compatibility issues, as it is built for a lower version. The case would have been different if that were built for 1.6 and you are running 1.5.
Am I wrong somewhere?
Actually, Collections class has a static method called : frequency(Collection c, Object o) which returns the number of occurrences of the element you are searching for, by the way, this will work perfectly for you:
ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println("Freq of bat: "+Collections.frequency(animals, "bat"));
A slightly more efficient approach might be
Map<String, AtomicInteger> instances = new HashMap<String, AtomicInteger>();
void add(String name) {
AtomicInteger value = instances.get(name);
if (value == null)
instances.put(name, new AtomicInteger(1));
else
value.incrementAndGet();
}
To get the occurrences of the object from the list directly:
int noOfOccurs = Collections.frequency(animals, "bat");
To get the occurrence of the Object collection inside list, override the equals method in the Object class as:
#Override
public boolean equals(Object o){
Animals e;
if(!(o instanceof Animals)){
return false;
}else{
e=(Animals)o;
if(this.type==e.type()){
return true;
}
}
return false;
}
Animals(int type){
this.type = type;
}
Call the Collections.frequency as:
int noOfOccurs = Collections.frequency(animals, new Animals(1));
What you want is a Bag - which is like a set but also counts the number of occurances. Unfortunately the java Collections framework - great as they are dont have a Bag impl. For that one must use the Apache Common Collection link text
List<String> list = Arrays.asList("as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd", "as", "asda",
"asd", "urff", "dfkjds", "hfad", "asd", "qadasd" + "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd",
"qadasd", "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd");
Method 1:
Set<String> set = new LinkedHashSet<>();
set.addAll(list);
for (String s : set) {
System.out.println(s + " : " + Collections.frequency(list, s));
}
Method 2:
int count = 1;
Map<String, Integer> map = new HashMap<>();
Set<String> set1 = new LinkedHashSet<>();
for (String s : list) {
if (!set1.add(s)) {
count = map.get(s) + 1;
}
map.put(s, count);
count = 1;
}
System.out.println(map);
​If you use Eclipse Collections, you can use a Bag. A MutableBag can be returned from any implementation of RichIterable by calling toBag().
MutableList<String> animals = Lists.mutable.with("bat", "owl", "bat", "bat");
MutableBag<String> bag = animals.toBag();
Assert.assertEquals(3, bag.occurrencesOf("bat"));
Assert.assertEquals(1, bag.occurrencesOf("owl"));
The HashBag implementation in Eclipse Collections is backed by a MutableObjectIntMap.
Note: I am a committer for Eclipse Collections.
Put the elements of the arraylist in the hashMap to count the frequency.
So do it the old fashioned way and roll your own:
Map<String, Integer> instances = new HashMap<String, Integer>();
void add(String name) {
Integer value = instances.get(name);
if (value == null) {
value = new Integer(0);
instances.put(name, value);
}
instances.put(name, value++);
}
Java 8 - another method
String searched = "bat";
long n = IntStream.range(0, animals.size())
.filter(i -> searched.equals(animals.get(i)))
.count();
package traversal;
import java.util.ArrayList;
import java.util.List;
public class Occurrance {
static int count;
public static void main(String[] args) {
List<String> ls = new ArrayList<String>();
ls.add("aa");
ls.add("aa");
ls.add("bb");
ls.add("cc");
ls.add("dd");
ls.add("ee");
ls.add("ee");
ls.add("aa");
ls.add("aa");
for (int i = 0; i < ls.size(); i++) {
if (ls.get(i) == "aa") {
count = count + 1;
}
}
System.out.println(count);
}
}
Output: 4
Integer[] spam = new Integer[] {1,2,2,3,4};
List<Integer> list=Arrays.asList(spam);
System.out.println(list.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting())));
System.out.println(list.stream().collect(Collectors.groupingBy(Function.identity(),HashMap::new,Collectors.counting())));
output
{1=1, 2=2, 3=1, 4=1}
If you are a user of my ForEach DSL, it can be done with a Count query.
Count<String> query = Count.from(list);
for (Count<Foo> each: query) each.yield = "bat".equals(each.element);
int number = query.result();
I didn't want to make this case more difficult and made it with two iterators
I have a HashMap with LastName -> FirstName. And my method should delete items with dulicate FirstName.
public static void removeTheFirstNameDuplicates(HashMap<String, String> map)
{
Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
Iterator<Map.Entry<String, String>> iter2 = map.entrySet().iterator();
while(iter.hasNext())
{
Map.Entry<String, String> pair = iter.next();
String name = pair.getValue();
int i = 0;
while(iter2.hasNext())
{
Map.Entry<String, String> nextPair = iter2.next();
if (nextPair.getValue().equals(name))
i++;
}
if (i > 1)
iter.remove();
}
}
List<String> lst = new ArrayList<String>();
lst.add("Ram");
lst.add("Ram");
lst.add("Shiv");
lst.add("Boss");
Map<String, Integer> mp = new HashMap<String, Integer>();
for (String string : lst) {
if(mp.keySet().contains(string))
{
mp.put(string, mp.get(string)+1);
}else
{
mp.put(string, 1);
}
}
System.out.println("=mp="+mp);
Output:
=mp= {Ram=2, Boss=1, Shiv=1}
Map<String,Integer> hm = new HashMap<String, Integer>();
for(String i : animals) {
Integer j = hm.get(i);
hm.put(i,(j==null ? 1 : j+1));
}
for(Map.Entry<String, Integer> val : hm.entrySet()) {
System.out.println(val.getKey()+" occurs : "+val.getValue()+" times");
}
You can use groupingBy feature of Java 8 for your use case.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<String> animals = new ArrayList<>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
Map<String,Long> occurrenceMap =
animals.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting()));
System.out.println("occurrenceMap:: " + occurrenceMap);
}
}
Output
occurrenceMap:: {bat=3, owl=1}

Categories