Related
I'm learning how to use stream, and I get a problem with this method.
public static String[] inArray(String[] array1, String[] array2) {
return Arrays.stream(array1)
.filter(str -> Arrays.stream(array2).anyMatch(s -> s.contains(str)))
.distinct().sorted().toArray(**String[]::new**);
}
I'm so confused about String[]::new, could you give me a hint?
String[]::new means size -> new String[size].
When Stream#toArray(IntFunction<A[]> generator) is ready to produce an array, it calls generator and passes (generator.apply) the size of the inner collection to get a collection to fill it up.
I would say the existing answers provide some insight but none of them yet talk about IntFunction<R>.
To add to them explain, what it means in the context of Stream.toArray(String[]::new) is that it represents an IntFunction implementation such as :
new IntFunction<String[]>() {
#Override
public String[] apply(int value) {
return new String[value];
}
}
where the code creates a newly allocated String[] of size value and produces the array of that size as an output.
You are right to be confused, because Java isn't really super clear about types vs. classes.
We know that String[] is a type, as you can declare variables of that type:
jshell> String[] s = new String[]{"Hello", "world"}
s ==> String[2] { "Hello", "world" }
However, String[] actually is treated as a class in Java and not just a type:
jshell> s.getClass()
$2 ==> class [Ljava.lang.String;
That funny looking [Ljava.lang.String, representing the type "array of string" shows up in response to the getClass invocation. I agree that it is surprising. But every object in Java has to have a class, and String[] is that class. (In other languages, you might see something like Array<String> which might be a dash clearer. But then Java has type erasure so again, things look a little confusing.)
In your particular case, here's what's going on. You need to be careful with types when making arrays from streams. Naively, you might get:
jshell> Arrays.asList("a", "b").stream().toArray()
$5 ==> Object[2] { "a", "b" }
So we want the version of toArray that gives us an array:
jshell> Arrays.asList("a", "b").stream().toArray((n) -> new String[n])
$7 ==> String[2] { "a", "b" }
That's better! The result type is an array of strings, instead of just an array of obejcts. Now the (n)->new String[n] can be replaced with a method reference for construction. Java allows array types in method references! So we can write:
jshell> Arrays.asList("a", "b").stream().toArray(String[]::new)
$8 ==> String[2] { "a", "b" }
Aside: There are some caveats when using array types in method references like this, such as the requirement that the array type must be reifiable, but I think that's a little beyond what you might have been asking. The TL;DR here is that, by design, Java allows array types in (constructor-like) method references with ::new.
This is a method reference expression see JLS 15.13. The syntax for method references is:
MethodReference:
ExpressionName :: [TypeArguments] Identifier
Primary :: [TypeArguments] Identifier
ReferenceType :: [TypeArguments] Identifier
super :: [TypeArguments] Identifier
TypeName . super :: [TypeArguments] Identifier
ClassType :: [TypeArguments] new
ArrayType :: new
The particular case you are looking at is the last one. In your example, String[] is an ArrayType which means that it consists of a type name followed by one or more [].
There shouldn't be a class named String[] which is very lame and I could not interpret what it is actually meant for.
See above: it is a type specification not a class name. From a syntactic / linguistic perspective, this usage is analogous to:
Class<?> c = String[].class;
or
if (a instanceof String[])
or even
public void myMethod(String[] arg)
(You wouldn't call those "lame" ... would you?)
Now you could have a valid case for saying that it is syntactically unexpected (especially to a pre-Java 8 programmer) to be able to use the new keyword like this. But this unexpected syntax is a consequence of the strong imperative that the designers have to NOT break backwards compatibility when adding new language features to Java. And it is not unintuitive. (At least, I don't think so. When I first saw this construct, is was obvious to me what it meant.)
Now, if they were starting with a clean slate in 2018, a lot of details of the Java language design would be simpler and cleaner. But they don't have the luxury of doing that.
The documentation of Stream#toArray says it exactly:
The generator function takes an integer, which is the size of the desired array, and produces an array of the desired size.
for example:
IntFunction<int[]> factory = int[]::new;
// v--- once `apply(3)` is invoked,it delegates to `new int[3]`
int [] array = factory.apply(3);
// ^--- [0, 0, 0] create an int array with size 3
String[]::new is a method reference expression and it must be assigned/casted to a certain functional interface type at compile time:
A method reference expression is used to refer to the invocation of a method without actually performing the invocation. Certain forms of method reference expression also allow class instance creation (§15.9) or array creation (§15.10) to be treated as if it were a method invocation.
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
Edit
As #Eugene mentioned in comments below. It's necessary to let you know how and where the stream create an fixed size array to collecting all elements.
The following table is showing the stream how to calculates the array size:
sequential stream - AbstractSpinedBuffer#count
parallel stream
stateless OPs with known/fixed size Spliterator - AbstractConcNode#AbstractConcNode
stateful OPs
fixed size Spliterator - Spliterator#estimateSize
unknown size Spliterator - AbstractConcNode#AbstractConcNode
The following table is showing the stream where to creates a fixed size array by array generator IntFunction:
sequential stream
stateful/stateless OPs with unknown/fixed size Spliterator - SpinedBuffer#asArray
parallel stream
stateless OPs with known/fixed size Spliterator - Nodes#flatten
stateful OPs
fixed size Spliterator - Nodes#collect
unknown size Spliterator - Nodes#flatten
String[]::new
This is lambda for the following method:
public String[] create(int size) {
return new String[size];
}
Your whole stream operation is terminating converting that into an array, that is what you do with the last method toArray(), but an array of what?....
of Strings ( thus String[]::new)
The parameter of toArray(...) is a Functional Interface (namely IntFunction<R> and then String[]::new is defined as the Method Reference or in that case constructor to use that generates an array of the desired type.
See https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
And https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Adding to the answer of Andrew Tobilko:
"String[]::new means size -> new String[size]"
which, since toArray takes an IntFunction, is similar to:
IntFunction<String[]> generator = new IntFunction<String[]>() {
#Override
public String[] apply(int size) {
return new String[size];
}
};
To convert your stream to another List, you can use:
.collect(Collectors.toList());
I have an Object (Object o) which when I call o.toString() it prints:
{data-ved=0EMIuCBcoAA, data-pid=23, href=https://mail.google.com/mail/?tab=wm, class=gb_P}
As you can see there seems to be an array of Strings in this object. Is there a way to directly convert the object to a String[]?
The expected result would be a String[] containing something like:
data-ved=0EMIuCBcoAA
data-pid=23
href=https://mail.google.com/mail/?tab=wm
class=gb_P
Currently, I'm using Regex to extract the parts of the String I need (as array).
Is there a better way to do this?
If it matters, the object is being returned from a JavaScript Executor in Selenium.
As can be seen in the comment trail the object that is returned seems to be a Map (and probably a Map<String, Object> or Map<String, String>). Thus you could do the following:
if( o instanceof Map ) {
Map map = (Map)o; //If you know the generic type you could use Map<String, Object> etc. instead
String dataVed = (String)map.get("data-ved");
...
}
As you can see casting the value is needed as well since there's no generic type provided for the map and thus all you know is that keys and values are objects of any class.
If you know what generic type should be used you could improve that, especially if the value type is String. Unfortunateley due to type erasure you can't get that type at runtime so the only way to get to know what to expect would be some documentation.
I'm new in Java, and I have seen a ArrayList example like this.
listing = new ArrayList<Lot>();
I know that if I want to create an empty array list. Then I will use ArrayList()
But I don't understand what is the <Lot> between the "ArrayList" and "()".
Can someone explain it to me?
Thanks
This is Java Generics. The <Lot> indicates that the ArrayList will contain only objects of type Lot. It is useful because the compiler can do type checking on your ArrayList.
It is called as type parameter. It denotes that ArrayList will only contain objects of type Lot
Check out concept of Generics.
You will get the use of this ArrayList<Lot> with this example :
// (a)Without Generics ....
List myIntList = new ArrayList(); // 1
myIntList.add(new Lot(0)); // 2
Lot x = (Lot) myIntList.iterator().next(); // 3
// (b)With Generics ....
List<Lot> myIntList = new ArrayList<Lot>(); // 1’
myIntList.add(new Lot(0)); // 2’
Lot x = myIntList.iterator().next(); // 3
Two points to be noted in the above e.g
In e.g(b), Since we already specified that ArrayList will contain only objects of type Lot, in Line 3, we didn't have to perform casting it to type object Lot. This is because the compiler already know that it will have only Lot type of objects.
Trying to add any other type of object to e.g (b) will result in compile time error. This is because the compiler has already identified this List is specific to contain elements of only type Lot. This is called type checking
It is an extension to Java's type system called, Generics.
Generics allow you to create a List that contains a specific sub-type of Objects (or a specific set of Objects that implement particular interfaces, instead of a collection that only holds plain Objects.
listing = new ArrayList<Lot>();
this line just says that the type of objects to be inserted,updated,retrieved in or from ArrayList are of the type Lot.
This is what is called the generics in java.
Using the generics type casting is not required at the time of retrieval of objects from any List.
I can declare an array of maps using generics to specify the map type:
private Map<String, Integer>[] myMaps;
However, I can't figure out how to instantiate it properly:
myMaps = new HashMap<String, Integer>[count]; // gives "generic array creation" error
myMaps = new HashMap[count]; // gives an "unchecked or unsafe operation" warning
myMaps = (Map<String, Integer>[])new HashMap[count]; // also gives warning
How can I instantiate this array of maps without getting a compiler error or warning?
Update:
Thank you all for your replies. I ended up going with the List suggestion.
Not strictly an answer to your question, but have you considered using a List instead?
List<Map<String,Integer>> maps = new ArrayList<Map<String,Integer>>();
...
maps.add(new HashMap<String,Integer>());
seems to work just fine.
See Java theory and practice: Generics gotchas for a detailed explanation of why mixing arrays with generics is discouraged.
Update:
As mentioned by Drew in the comments, it might be even better to use the Collection interface instead of List. This might come in handy if you ever need to change to a Set, or one of the other subinterfaces of Collection. Example code:
Collection<Map<String,Integer>> maps = new HashSet<Map<String,Integer>>();
...
maps.add(new HashMap<String,Integer>());
From this starting point, you'd only need to change HashSet to ArrayList, PriorityQueue, or any other class that implements Collection.
You can't safely create a generic array. Effective Java 2nd Edition goes into the details in the chapter on Generics. Start at the last paragraph of page 119:
Why is it illegal to create a generic
array? Because it isn’t typesafe. If
it were legal, casts generated by the
compiler in an otherwise correct
program could fail at runtime with a
ClassCastException. This would violate
the fundamental guarantee provided by
the generic type system.
To make this more concrete, consider
the following code fragment:
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
Let’s pretend that line 1, which
creates a generic array, is legal.
Line 2 creates and initializes a
List<Integer> containing a single
element. Line 3 stores the
List<String> array into an Object
array variable, which is legal because
arrays are covariant. Line 4 stores
the List<Integer> into the sole
element of the Object array, which
succeeds because generics are
implemented by erasure: the runtime
type of a List<Integer> instance is
simply List, and the runtime type of a
List<String>[] instance is List[], so
this assignment doesn’t generate an
ArrayStoreException. Now we’re in
trouble. We’ve stored a List<Integer>
instance into an array that is
declared to hold only List<String>
instances. In line 5, we retrieve the
sole element from the sole list in
this array. The compiler automatically
casts the retrieved element to String,
but it’s an Integer, so we get a
ClassCastException at runtime. In
order to prevent this from happening,
line 1 (which creates a generic array)
generates a compile-time error.
Because arrays and generics don't combine well (as well as other reasons), it's generally better to use Collection objects (in particular List objects) rather than arrays.
In general it is not a good idea to mix generics and arrays in Java, better use an ArrayList.
If you must use an array, the best way to handle this is to put the array creation (your example 2 or 3) in a separate method and annotate it with #SuppressWarnings("unchecked").
You can create generic array of map
Create list of map.
List<Map<String, ?>> myData = new ArrayList<Map<String, ?>>();
Initialize array.
Map<String,?>[]myDataArray=new HashMap[myData .size()];
Populate data in array from list.
myDataArray=myData.toArray(myDataArry);
Short answer appears to be that you really just can't.
See the following for a blog about it.
http://www.bloggingaboutjava.org/2006/01/java-generics-quirks/
One of the comments to the blog states that:
Actually, the engineers made the creation of such an Array illegal. So the creation of an array from generic Class fails. The Collection.toArray method followed by a Cast to the Array works at compile time.
This solves not the problem, that the ArrayStoreCheck can’t be done during Runtime, but you can create an Array of generics in this way.
As suggested by Bill the Lizard, you probably are better off using a
List<Map<String,Integer>>
I know its a bit late to reply but I found this workaround helpful for my case...Hope it helps!
Use an array of HashMap to store HashMaps..
public static void main(String[] args) {
HashMap[] arr = new HashMap[1];//creating an array of size one..just for sake of example
HashMap<String, String> arrMap = new HashMap<String, String>();
//use loops to store desired key-value pairs into the HashMap which will be stored inside the array
arrMap.put("ABC", "Name");
//use loop to store your desired hashMap into the array at a particular index
arr[0] = arrMap;
//desired manipulation of the stored array.
System.out.println(arr[0]);
}
myMaps = new HashMap<String, Integer>[10]();
So that's Wrong
Why not make a List of Maps instead of trying to make an array?
List<Map<String, Integer>> mymaps = new ArrayList<Map<String, Integer>>(count);
So I have two questions about HashMaps in Java:
What is the correct way to initialize a HashMap? I think it might be best in my situation to use:
HashMap x = new HashMap();
But Eclipse keeps suggesting that I use:
HashMap<something, something> map = new HashMap();
Which is better?
Can a HashMap hold different types of objects/data types as values? For example, would this work and be OK:
map.put("one", 1);
map.put("two", {1, 2});
map.put("three", "hello");
In the first put(), I want an int as a value, in the second an int[], and third a string. Is this okay to do in Java with HashMaps? Also, is it okay to store a HashMap as a value within a HashMap?
It really depends on what kind of type safety you need. The non-generic way of doing it is best done as:
Map x = new HashMap();
Note that x is typed as a Map. this makes it much easier to change implementations (to a TreeMap or a LinkedHashMap) in the future.
You can use generics to ensure a certain level of type safety:
Map<String, Object> x = new HashMap<String, Object>();
In Java 7 and later you can do
Map<String, Object> x = new HashMap<>();
The above, while more verbose, avoids compiler warnings. In this case the content of the HashMap can be any Object, so that can be Integer, int[], etc. which is what you are doing.
If you are still using Java 6, Guava Libraries (although it is easy enough to do yourself) has a method called newHashMap() which avoids the need to duplicate the generic typing information when you do a new. It infers the type from the variable declaration (this is a Java feature not available on constructors prior to Java 7).
By the way, when you add an int or other primitive, Java is autoboxing it. That means that the code is equivalent to:
x.put("one", Integer.valueOf(1));
You can certainly put a HashMap as a value in another HashMap, but I think there are issues if you do it recursively (that is put the HashMap as a value in itself).
This is a change made with Java 1.5. What you list first is the old way, the second is the new way.
By using HashMap you can do things like:
HashMap<String, Doohickey> ourMap = new HashMap<String, Doohickey>();
....
Doohickey result = ourMap.get("bob");
If you didn't have the types on the map, you'd have to do this:
Doohickey result = (Doohickey) ourMap.get("bob");
It's really very useful. It helps you catch bugs and avoid writing all sorts of extra casts. It was one of my favorite features of 1.5 (and newer).
You can still put multiple things in the map, just specify it as Map, then you can put any object in (a String, another Map, and Integer, and three MyObjects if you are so inclined).
Eclipse is recommending that you declare the type of the HashMap because that enforces some type safety. Of course, it sounds like you're trying to avoid type safety from your second part.
If you want to do the latter, try declaring map as HashMap<String,Object>.
The way you're writing it is equivalent to
HashMap<Object, Object> map = new HashMap<Object, Object>();
What goes inside the brackets is you communicating to the compiler what you're going to put in the HashMap so that it can do error checking for you. If Object, Object is what you actually want (probably not) you should explicitly declare it. In general you should be as explicit as you can with the declaration to facilitate error checking by the compiler. What you've described should probably be declared like this:
HashMap<String, Object> map = new HashMap<String, Object>();
That way you at least declare that your keys are going to be strings, but your values can be anything. Just remember to use a cast when you get a value back out.
The 2nd one is using generics which came in with Java 1.5. It will reduce the number of casts in your code & can help you catch errors at compiletime instead of runtime. That said, it depends on what you are coding. A quick & dirty map to hold a few objects of various types doesn't need generics. But if the map is holding objects all descending from a type other than Object, it can be worth it.
The prior poster is incorrect about the array in a map. An array is actually an object, so it is a valid value.
Map<String,Object> map = new HashMap<String,Object>();
map.put("one",1); // autoboxed to an object
map.put("two", new int[]{1,2} ); // array of ints is an object
map.put("three","hello"); // string is an object
Also, since HashMap is an object, it can also be a value in a HashMap.
A HashMap can hold any object as a value, even if it is another HashMap. Eclipse is suggesting that you declare the types because that is the recommended practice for Collections. under Java 5. You are free to ignore Eclipse's suggestions.
Under Java 5, an int (or any primitive type) will be autoboxed into an Integer (or other corresponding type) when you add it to a collection. Be careful with this though, as there are some catches to using autoboxing.
Eclipse is suggesting you to define generic type so that you can have type safety. You can write
Map m = new HashMap();
which does not ensure type safety but following will ensure type safety
Map<Object,Object> = new HashMap<Object,Object>();
The Object can be any type such as String, Integer etc.
Map.of literals
As of Java 9, there is yet another way to instantiate a Map. You can create an unmodifiable map from zero, one, or several pairs of objects in a single-line of code. This is quite convenient in many situations.
For an empty Map that cannot be modified, call Map.of(). Why would you want an empty set that cannot be changed? One common case is to avoid returning a NULL where you have no valid content.
For a single key-value pair, call Map.of( myKey , myValue ). For example, Map.of( "favorite_color" , "purple" ).
For multiple key-value pairs, use a series of key-value pairs. ``Map.of( "favorite_foreground_color" , "purple" , "favorite_background_color" , "cream" )`.
If those pairs are difficult to read, you may want to use Map.of and pass Map.Entry objects.
Note that we get back an object of the Map interface. We do not know the underlying concrete class used to make our object. Indeed, the Java team is free to used different concrete classes for different data, or to vary the class in future releases of Java.
The rules discussed in other Answers still apply here, with regard to type-safety. You declare your intended types, and your passed objects must comply. If you want values of various types, use Object.
Map< String , Color > preferences = Map.of( "favorite_color" , Color.BLUE ) ;
In answer to your second question: Yes a HashMap can hold different types of objects. Whether that's a good idea or not depends on the problem you're trying to solve.
That said, your example won't work. The int value is not an Object. You have to use the Integer wrapper class to store an int value in a HashMap